use super::functions::*;
use oxilean_kernel::{BinderInfo, Declaration, Environment, Expr, Level, Name};
use std::collections::HashMap;
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct FiberBundle {
pub total_space: String,
pub base_space: String,
pub fiber: String,
pub has_section: bool,
pub is_trivial: bool,
pub structure_group: Option<String>,
}
#[allow(dead_code)]
impl FiberBundle {
pub fn new(total: &str, base: &str, fiber: &str) -> Self {
FiberBundle {
total_space: total.to_string(),
base_space: base.to_string(),
fiber: fiber.to_string(),
has_section: false,
is_trivial: false,
structure_group: None,
}
}
pub fn with_structure_group(mut self, g: &str) -> Self {
self.structure_group = Some(g.to_string());
self
}
pub fn trivial(mut self) -> Self {
self.is_trivial = true;
self.has_section = true;
self
}
pub fn les_description(&self) -> String {
format!(
"... → π_n({}) → π_n({}) → π_n({}) → π_{{n-1}}({}) → ...",
self.fiber, self.total_space, self.base_space, self.fiber
)
}
pub fn is_principal(&self) -> bool {
match &self.structure_group {
Some(g) => *g == self.fiber,
None => false,
}
}
}
pub struct SimplicialComplex {
pub vertices: Vec<usize>,
pub simplices: Vec<Vec<usize>>,
}
impl SimplicialComplex {
pub fn new() -> Self {
SimplicialComplex {
vertices: Vec::new(),
simplices: Vec::new(),
}
}
pub fn add_simplex(&mut self, mut simplex: Vec<usize>) {
simplex.sort_unstable();
simplex.dedup();
for &v in &simplex {
if !self.vertices.contains(&v) {
self.vertices.push(v);
}
}
self.vertices.sort_unstable();
if !self.simplices.contains(&simplex) {
self.simplices.push(simplex);
}
}
pub(super) fn simplices_of_dim(&self, dim: usize) -> Vec<&Vec<usize>> {
self.simplices
.iter()
.filter(|s| s.len() == dim + 1)
.collect()
}
pub fn boundary_matrix(&self, dim: usize) -> Vec<Vec<i64>> {
if dim == 0 {
return Vec::new();
}
let lower = self.simplices_of_dim(dim - 1);
let upper = self.simplices_of_dim(dim);
let rows = lower.len();
let cols = upper.len();
let mut mat = vec![vec![0i64; cols]; rows];
for (col, sigma) in upper.iter().enumerate() {
for i in 0..sigma.len() {
let mut face = sigma[..i].to_vec();
face.extend_from_slice(&sigma[i + 1..]);
if let Some(row) = lower.iter().position(|f| **f == face) {
mat[row][col] = if i % 2 == 0 { 1 } else { -1 };
}
}
}
mat
}
pub fn euler_characteristic(&self) -> i64 {
let max_dim = self.simplices.iter().map(|s| s.len()).max().unwrap_or(0);
let mut chi = 0i64;
for k in 0..max_dim {
let count = self.simplices_of_dim(k).len() as i64;
if k % 2 == 0 {
chi += count;
} else {
chi -= count;
}
}
chi
}
pub fn betti_numbers(&self, max_dim: usize) -> Vec<usize> {
homology_ranks(self, max_dim)
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq)]
pub struct PersistencePoint {
pub birth: f64,
pub death: f64,
pub dimension: usize,
}
#[allow(dead_code)]
impl PersistencePoint {
pub fn new(birth: f64, death: f64, dimension: usize) -> Self {
PersistencePoint {
birth,
death,
dimension,
}
}
pub fn persistence(&self) -> f64 {
self.death - self.birth
}
pub fn is_essential(&self) -> bool {
self.death == f64::INFINITY
}
pub fn midpoint(&self) -> f64 {
if self.is_essential() {
self.birth
} else {
(self.birth + self.death) / 2.0
}
}
}
#[derive(Debug, Clone)]
pub struct TopologicalInvariantTable {
pub cell_counts: Vec<usize>,
pub euler_characteristic: i64,
pub betti_numbers: Vec<usize>,
}
impl TopologicalInvariantTable {
pub fn compute(complex: &SimplicialComplex, max_dim: usize) -> Self {
let cell_counts: Vec<usize> = (0..=max_dim)
.map(|k| complex.simplices_of_dim(k).len())
.collect();
let euler_characteristic = complex.euler_characteristic();
let betti_numbers = homology_ranks(complex, max_dim);
TopologicalInvariantTable {
cell_counts,
euler_characteristic,
betti_numbers,
}
}
pub fn display(&self) -> String {
let mut out = String::new();
out.push_str("Topological Invariants\n");
out.push_str("======================\n");
for (k, &c) in self.cell_counts.iter().enumerate() {
out.push_str(&format!(" #{k}-cells = {c}\n"));
}
out.push_str(&format!(" χ (Euler) = {}\n", self.euler_characteristic));
for (k, &b) in self.betti_numbers.iter().enumerate() {
out.push_str(&format!(" β_{k} = {b}\n"));
}
out
}
}
pub struct CharacteristicClassComputer {
pub chern_roots: Vec<f64>,
}
impl CharacteristicClassComputer {
pub fn new(chern_roots: Vec<f64>) -> Self {
CharacteristicClassComputer { chern_roots }
}
pub fn total_chern_class(&self) -> Vec<f64> {
let n = self.chern_roots.len();
let mut c = vec![0.0f64; n + 1];
c[0] = 1.0;
for &x in &self.chern_roots {
for k in (1..=n).rev() {
c[k] += x * c[k - 1];
}
}
c
}
pub fn chern_character(&self, max_deg: usize) -> Vec<f64> {
let mut ch = vec![0.0f64; max_deg + 1];
for &x in &self.chern_roots {
let mut power = 1.0f64;
let mut factorial = 1.0f64;
for k in 0..=max_deg {
if k > 0 {
power *= x;
factorial *= k as f64;
}
ch[k] += power / factorial;
}
}
ch
}
pub fn euler_number(&self) -> f64 {
let c = self.total_chern_class();
*c.last().unwrap_or(&0.0)
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct SpectralSequencePage {
pub r: usize,
pub data: std::collections::HashMap<(i32, i32), String>,
}
#[allow(dead_code)]
impl SpectralSequencePage {
pub fn new(r: usize) -> Self {
SpectralSequencePage {
r,
data: std::collections::HashMap::new(),
}
}
pub fn set(&mut self, p: i32, q: i32, group: &str) {
self.data.insert((p, q), group.to_string());
}
pub fn get(&self, p: i32, q: i32) -> &str {
self.data.get(&(p, q)).map(|s| s.as_str()).unwrap_or("0")
}
pub fn total_degree(&self, n: i32) -> Vec<&str> {
self.data
.iter()
.filter(|&(&(p, q), _)| p + q == n)
.map(|(_, v)| v.as_str())
.collect()
}
pub fn differential_target(&self, p: i32, q: i32) -> (i32, i32) {
(p + self.r as i32, q - self.r as i32 + 1)
}
}
pub struct UltrametricBallTree {
pub points: Vec<f64>,
pub prime: u32,
}
impl UltrametricBallTree {
pub fn new(points: Vec<f64>, prime: u32) -> Self {
UltrametricBallTree { points, prime }
}
pub fn padic_valuation(&self, x: f64) -> i32 {
let n = x.round().abs() as u64;
if n == 0 {
return i32::MAX;
}
let p = self.prime as u64;
let mut val = 0i32;
let mut m = n;
while m % p == 0 {
val += 1;
m /= p;
}
val
}
pub fn ultrametric_dist(&self, x: f64, y: f64) -> f64 {
let diff = x - y;
if diff.abs() < f64::EPSILON {
return 0.0;
}
let v = self.padic_valuation(diff);
if v == i32::MAX {
0.0
} else {
(self.prime as f64).powi(-v)
}
}
pub fn check_ultrametric_inequality(&self, x: f64, y: f64, z: f64) -> bool {
let dxz = self.ultrametric_dist(x, z);
let dxy = self.ultrametric_dist(x, y);
let dyz = self.ultrametric_dist(y, z);
dxz <= dxy.max(dyz) + f64::EPSILON
}
pub fn ball(&self, center: f64, radius: f64) -> Vec<f64> {
self.points
.iter()
.filter(|&&p| self.ultrametric_dist(center, p) <= radius)
.copied()
.collect()
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct CWComplex {
pub name: String,
pub cells: Vec<u32>,
}
#[allow(dead_code)]
impl CWComplex {
pub fn new(name: &str) -> Self {
CWComplex {
name: name.to_string(),
cells: Vec::new(),
}
}
pub fn add_cells(mut self, dim: usize, count: u32) -> Self {
while self.cells.len() <= dim {
self.cells.push(0);
}
self.cells[dim] += count;
self
}
pub fn sphere(n: usize) -> Self {
let mut cw = CWComplex::new(&format!("S^{n}"));
cw = cw.add_cells(0, 1);
cw = cw.add_cells(n, 1);
cw
}
pub fn torus() -> Self {
CWComplex::new("T^2")
.add_cells(0, 1)
.add_cells(1, 2)
.add_cells(2, 1)
}
pub fn rp2() -> Self {
CWComplex::new("RP^2")
.add_cells(0, 1)
.add_cells(1, 1)
.add_cells(2, 1)
}
pub fn euler_characteristic(&self) -> i64 {
self.cells
.iter()
.enumerate()
.map(|(k, &c)| {
let sign: i64 = if k % 2 == 0 { 1 } else { -1 };
sign * c as i64
})
.sum()
}
pub fn dimension(&self) -> Option<usize> {
self.cells
.iter()
.enumerate()
.rev()
.find(|(_, &c)| c > 0)
.map(|(k, _)| k)
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct DiscreteMorseFunction {
pub cells: Vec<usize>,
pub critical_cells: Vec<Vec<usize>>,
pub gradient_pairs: Vec<(usize, usize, usize, usize)>,
}
#[allow(dead_code)]
impl DiscreteMorseFunction {
pub fn new(cells: Vec<usize>) -> Self {
let n = cells.len();
DiscreteMorseFunction {
cells,
critical_cells: vec![Vec::new(); n],
gradient_pairs: Vec::new(),
}
}
pub fn mark_critical(&mut self, dim: usize, idx: usize) {
if dim < self.critical_cells.len() {
self.critical_cells[dim].push(idx);
}
}
pub fn add_gradient_pair(&mut self, dim: usize, lower_idx: usize, upper_idx: usize) {
if dim + 1 < self.cells.len() {
self.gradient_pairs
.push((dim, lower_idx, dim + 1, upper_idx));
}
}
pub fn morse_inequalities_lhs(&self) -> Vec<usize> {
self.critical_cells.iter().map(|v| v.len()).collect()
}
pub fn check_weak_morse_inequality(&self, betti: &[usize]) -> bool {
let critical = self.morse_inequalities_lhs();
betti
.iter()
.enumerate()
.all(|(k, &b)| critical.get(k).copied().unwrap_or(0) >= b)
}
pub fn euler_characteristic(&self) -> i64 {
self.critical_cells
.iter()
.enumerate()
.map(|(k, cells)| {
let sign: i64 = if k % 2 == 0 { 1 } else { -1 };
sign * cells.len() as i64
})
.sum()
}
}
pub struct UniformContinuityChecker<'a> {
pub source: &'a MetricSpace,
pub target: &'a MetricSpace,
pub mapping: Vec<usize>,
}
impl<'a> UniformContinuityChecker<'a> {
pub fn new(source: &'a MetricSpace, target: &'a MetricSpace, mapping: Vec<usize>) -> Self {
UniformContinuityChecker {
source,
target,
mapping,
}
}
pub fn is_uniformly_continuous(&self, epsilon: f64, delta: f64) -> bool {
let n = self.source.n;
for i in 0..n {
for j in 0..n {
if self.source.dist[i][j] < delta {
let fi = self.mapping[i];
let fj = self.mapping[j];
if self.target.dist[fi][fj] >= epsilon {
return false;
}
}
}
}
true
}
}
pub struct MetricSpace {
pub n: usize,
pub dist: Vec<Vec<f64>>,
}
impl MetricSpace {
pub fn new(dist: Vec<Vec<f64>>) -> Self {
let n = dist.len();
for row in &dist {
assert_eq!(row.len(), n, "distance matrix must be square");
}
MetricSpace { n, dist }
}
pub fn distance(&self, i: usize, j: usize) -> f64 {
self.dist[i][j]
}
pub fn is_totally_bounded(&self, epsilon: f64) -> bool {
if self.n == 0 {
return true;
}
let net = self.greedy_epsilon_net(epsilon);
(0..self.n).all(|i| net.iter().any(|&j| self.dist[i][j] <= epsilon))
}
pub fn greedy_epsilon_net(&self, epsilon: f64) -> Vec<usize> {
let mut net: Vec<usize> = Vec::new();
for i in 0..self.n {
if !net.iter().any(|&j| self.dist[i][j] <= epsilon) {
net.push(i);
}
}
net
}
pub fn looks_complete(&self, epsilon: f64) -> bool {
self.is_totally_bounded(epsilon)
}
}
pub struct ProObjectLimit {
pub levels: Vec<Vec<u32>>,
pub projections: Vec<Vec<usize>>,
}
impl ProObjectLimit {
pub fn new(levels: Vec<Vec<u32>>, projections: Vec<Vec<usize>>) -> Self {
ProObjectLimit {
levels,
projections,
}
}
pub fn compute_limit(&self) -> Vec<Vec<usize>> {
if self.levels.is_empty() {
return vec![];
}
let mut threads: Vec<Vec<usize>> = (0..self.levels[0].len()).map(|i| vec![i]).collect();
for (level_idx, proj) in self.projections.iter().enumerate() {
let next_level_size = if level_idx + 1 < self.levels.len() {
self.levels[level_idx + 1].len()
} else {
break;
};
let mut new_threads: Vec<Vec<usize>> = Vec::new();
for j in 0..next_level_size {
let parent = proj[j];
for thread in &threads {
if *thread
.last()
.expect("threads are always non-empty: initialized with vec![i]")
== parent
{
let mut new_thread = thread.clone();
new_thread.push(j);
new_threads.push(new_thread);
}
}
}
threads = new_threads;
}
threads
}
pub fn limit_size(&self) -> usize {
self.compute_limit().len()
}
}
pub struct ShapeEquivalenceChecker {
pub betti_x: Vec<usize>,
pub betti_y: Vec<usize>,
}
impl ShapeEquivalenceChecker {
pub fn from_complexes(cx: &SimplicialComplex, cy: &SimplicialComplex, max_dim: usize) -> Self {
ShapeEquivalenceChecker {
betti_x: homology_ranks(cx, max_dim),
betti_y: homology_ranks(cy, max_dim),
}
}
fn euler_from_betti(betti: &[usize]) -> i64 {
betti
.iter()
.enumerate()
.map(|(k, &b)| if k % 2 == 0 { b as i64 } else { -(b as i64) })
.sum()
}
pub fn euler_agrees(&self) -> bool {
Self::euler_from_betti(&self.betti_x) == Self::euler_from_betti(&self.betti_y)
}
pub fn betti_agree(&self) -> bool {
let len = self.betti_x.len().max(self.betti_y.len());
for k in 0..len {
let bx = self.betti_x.get(k).copied().unwrap_or(0);
let by = self.betti_y.get(k).copied().unwrap_or(0);
if bx != by {
return false;
}
}
true
}
pub fn are_shape_equivalent(&self) -> bool {
self.betti_agree()
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct KnotInvariant {
pub name: String,
pub alexander_poly: Vec<i64>,
pub jones_data: Vec<(i32, i64)>,
pub signature: i64,
pub determinant: u64,
}
#[allow(dead_code)]
impl KnotInvariant {
pub fn new(name: &str) -> Self {
KnotInvariant {
name: name.to_string(),
alexander_poly: Vec::new(),
jones_data: Vec::new(),
signature: 0,
determinant: 1,
}
}
pub fn with_alexander(mut self, coeffs: Vec<i64>) -> Self {
self.alexander_poly = coeffs;
self
}
pub fn with_signature(mut self, sig: i64) -> Self {
self.signature = sig;
self
}
pub fn with_determinant(mut self, det: u64) -> Self {
self.determinant = det;
self
}
pub fn alexander_at_one(&self) -> i64 {
self.alexander_poly.iter().sum()
}
pub fn alexander_degree(&self) -> usize {
self.alexander_poly.len().saturating_sub(1)
}
pub fn potentially_fibered(&self) -> bool {
if let (Some(&first), Some(&last)) =
(self.alexander_poly.first(), self.alexander_poly.last())
{
first.abs() == 1 && last.abs() == 1
} else {
false
}
}
}
#[derive(Debug, Clone)]
pub struct Cell {
pub dim: usize,
pub label: String,
pub attaching_map: Vec<usize>,
}
#[allow(dead_code)]
#[derive(Debug, Clone, Default)]
pub struct PersistenceDiagram {
pub points: Vec<PersistencePoint>,
}
#[allow(dead_code)]
impl PersistenceDiagram {
pub fn new() -> Self {
PersistenceDiagram { points: Vec::new() }
}
pub fn add(&mut self, p: PersistencePoint) {
self.points.push(p);
}
pub fn in_dimension(&self, dim: usize) -> Vec<&PersistencePoint> {
self.points.iter().filter(|p| p.dimension == dim).collect()
}
pub fn betti(&self, dim: usize) -> usize {
self.points
.iter()
.filter(|p| p.dimension == dim && p.is_essential())
.count()
}
pub fn bottleneck_distance_approx(&self, other: &PersistenceDiagram) -> f64 {
let mut max_dist: f64 = 0.0;
for p in &self.points {
let d = other
.points
.iter()
.map(|q| ((p.birth - q.birth).abs()).max((p.death - q.death).abs()))
.fold(f64::INFINITY, f64::min);
max_dist = max_dist.max(d);
}
max_dist
}
pub fn total_persistence(&self) -> f64 {
self.points
.iter()
.filter(|p| !p.is_essential())
.map(|p| p.persistence())
.sum()
}
pub fn significant_features(&self, threshold: f64) -> Vec<&PersistencePoint> {
self.points
.iter()
.filter(|p| p.persistence() > threshold || p.is_essential())
.collect()
}
}
pub struct CWComplexBuilder {
pub cells: Vec<Cell>,
}
impl CWComplexBuilder {
pub fn new() -> Self {
CWComplexBuilder { cells: Vec::new() }
}
pub fn add_cell(
&mut self,
dim: usize,
label: impl Into<String>,
attaching: Vec<usize>,
) -> usize {
let idx = self.cells.len();
self.cells.push(Cell {
dim,
label: label.into(),
attaching_map: attaching,
});
idx
}
pub fn cell_counts(&self, max_dim: usize) -> Vec<usize> {
let mut counts = vec![0usize; max_dim + 1];
for cell in &self.cells {
if cell.dim <= max_dim {
counts[cell.dim] += 1;
}
}
counts
}
pub fn euler_characteristic(&self) -> i64 {
let max_dim = self.cells.iter().map(|c| c.dim).max().unwrap_or(0);
let counts = self.cell_counts(max_dim);
counts
.iter()
.enumerate()
.map(|(k, &c)| if k % 2 == 0 { c as i64 } else { -(c as i64) })
.sum()
}
}
#[derive(Debug)]
pub struct BaireCategoryGame {
pub grid_size: usize,
pub left: usize,
pub right: usize,
pub round: usize,
}
impl BaireCategoryGame {
pub fn new(grid_size: usize) -> Self {
assert!(grid_size >= 2, "grid must have at least 2 points");
BaireCategoryGame {
grid_size,
left: 0,
right: grid_size - 1,
round: 0,
}
}
pub fn player_one_move(&mut self) {
let width = self.right - self.left;
let new_right = self.left + width / 3;
if new_right > self.left {
self.right = new_right;
}
self.round += 1;
}
pub fn player_two_move(&mut self) {
let width = self.right - self.left;
let third = width / 3;
self.left += third;
self.right = self.right.saturating_sub(third);
self.round += 1;
}
pub fn player_two_winning(&self) -> bool {
self.left <= self.right
}
pub fn simulate(&mut self, rounds: usize) -> bool {
for _ in 0..rounds {
self.player_one_move();
if !self.player_two_winning() {
return false;
}
self.player_two_move();
if !self.player_two_winning() {
return false;
}
}
self.player_two_winning()
}
}