use std::collections::HashMap;
#[allow(dead_code)]
pub struct Simplex0 {
pub vertex: usize,
}
#[allow(dead_code)]
pub struct Simplex1 {
pub v0: usize,
pub v1: usize,
}
#[allow(dead_code)]
pub struct Simplex2 {
pub v0: usize,
pub v1: usize,
pub v2: usize,
}
#[allow(dead_code)]
pub struct SimplicialComplex {
pub vertices: Vec<Simplex0>,
pub edges: Vec<Simplex1>,
pub triangles: Vec<Simplex2>,
pub vertex_count: usize,
}
#[allow(dead_code)]
pub fn new_simplicial_complex(vertex_count: usize) -> SimplicialComplex {
let vertices = (0..vertex_count).map(|i| Simplex0 { vertex: i }).collect();
SimplicialComplex {
vertices,
edges: Vec::new(),
triangles: Vec::new(),
vertex_count,
}
}
fn edge_key(a: usize, b: usize) -> (usize, usize) {
if a < b {
(a, b)
} else {
(b, a)
}
}
#[allow(dead_code)]
pub fn add_simplex1(complex: &mut SimplicialComplex, v0: usize, v1: usize) {
let key = edge_key(v0, v1);
let already = complex.edges.iter().any(|e| edge_key(e.v0, e.v1) == key);
if !already {
complex.edges.push(Simplex1 { v0, v1 });
}
}
#[allow(dead_code)]
pub fn add_simplex2(complex: &mut SimplicialComplex, v0: usize, v1: usize, v2: usize) {
add_simplex1(complex, v0, v1);
add_simplex1(complex, v1, v2);
add_simplex1(complex, v0, v2);
let already = complex.triangles.iter().any(|t| {
let mut tv = [t.v0, t.v1, t.v2];
let mut nv = [v0, v1, v2];
tv.sort();
nv.sort();
tv == nv
});
if !already {
complex.triangles.push(Simplex2 { v0, v1, v2 });
}
}
#[allow(dead_code)]
pub fn boundary_of_edge(e: &Simplex1) -> [Simplex0; 2] {
[Simplex0 { vertex: e.v0 }, Simplex0 { vertex: e.v1 }]
}
#[allow(dead_code)]
pub fn boundary_of_triangle(t: &Simplex2) -> [Simplex1; 3] {
let (a, b, c) = (t.v0, t.v1, t.v2);
[
Simplex1 {
v0: a.min(b),
v1: a.max(b),
},
Simplex1 {
v0: b.min(c),
v1: b.max(c),
},
Simplex1 {
v0: a.min(c),
v1: a.max(c),
},
]
}
#[allow(dead_code)]
pub fn euler_characteristic(complex: &SimplicialComplex) -> i32 {
complex.vertex_count as i32 - complex.edges.len() as i32 + complex.triangles.len() as i32
}
#[allow(dead_code)]
pub fn betti_0(complex: &SimplicialComplex) -> usize {
let n = complex.vertex_count;
if n == 0 {
return 0;
}
let mut parent: Vec<usize> = (0..n).collect();
fn find(parent: &mut Vec<usize>, x: usize) -> usize {
if parent[x] != x {
parent[x] = find(parent, parent[x]);
}
parent[x]
}
fn union(parent: &mut Vec<usize>, a: usize, b: usize) {
let ra = find(parent, a);
let rb = find(parent, b);
if ra != rb {
parent[ra] = rb;
}
}
for e in &complex.edges {
if e.v0 < n && e.v1 < n {
union(&mut parent, e.v0, e.v1);
}
}
let mut roots = std::collections::HashSet::new();
for i in 0..n {
roots.insert(find(&mut parent, i));
}
roots.len()
}
#[allow(dead_code)]
pub fn from_mesh_indices(vertex_count: usize, indices: &[u32]) -> SimplicialComplex {
let mut complex = new_simplicial_complex(vertex_count);
let face_count = indices.len() / 3;
for f in 0..face_count {
let v0 = indices[f * 3] as usize;
let v1 = indices[f * 3 + 1] as usize;
let v2 = indices[f * 3 + 2] as usize;
add_simplex2(&mut complex, v0, v1, v2);
}
complex
}
#[allow(dead_code)]
pub fn simplex1_count(complex: &SimplicialComplex) -> usize {
complex.edges.len()
}
#[allow(dead_code)]
pub fn simplex2_count(complex: &SimplicialComplex) -> usize {
complex.triangles.len()
}
#[allow(dead_code)]
pub fn is_manifold_simplex(complex: &SimplicialComplex) -> bool {
let mut edge_face_count: HashMap<(usize, usize), usize> = HashMap::new();
for t in &complex.triangles {
let edges = [
edge_key(t.v0, t.v1),
edge_key(t.v1, t.v2),
edge_key(t.v0, t.v2),
];
for ek in edges {
*edge_face_count.entry(ek).or_insert(0) += 1;
}
}
edge_face_count.values().all(|&c| c <= 2)
}
#[allow(dead_code)]
pub fn boundary_edges_simplex(complex: &SimplicialComplex) -> Vec<&Simplex1> {
let mut edge_face_count: HashMap<(usize, usize), usize> = HashMap::new();
for t in &complex.triangles {
let edges = [
edge_key(t.v0, t.v1),
edge_key(t.v1, t.v2),
edge_key(t.v0, t.v2),
];
for ek in edges {
*edge_face_count.entry(ek).or_insert(0) += 1;
}
}
complex
.edges
.iter()
.filter(|e| {
let k = edge_key(e.v0, e.v1);
edge_face_count.get(&k).copied().unwrap_or(0) == 1
})
.collect()
}
#[allow(dead_code)]
pub fn dual_graph_simplex(complex: &SimplicialComplex) -> Vec<Vec<usize>> {
let nf = complex.triangles.len();
let mut edge_to_faces: HashMap<(usize, usize), Vec<usize>> = HashMap::new();
for (fi, t) in complex.triangles.iter().enumerate() {
let edges = [
edge_key(t.v0, t.v1),
edge_key(t.v1, t.v2),
edge_key(t.v0, t.v2),
];
for ek in edges {
edge_to_faces.entry(ek).or_default().push(fi);
}
}
let mut adj = vec![Vec::new(); nf];
for faces in edge_to_faces.values() {
if faces.len() == 2 {
adj[faces[0]].push(faces[1]);
adj[faces[1]].push(faces[0]);
}
}
adj
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_simplicial_complex() {
let c = new_simplicial_complex(5);
assert_eq!(c.vertex_count, 5);
assert_eq!(c.vertices.len(), 5);
assert!(c.edges.is_empty());
assert!(c.triangles.is_empty());
}
#[test]
fn test_add_simplex1_no_duplicate() {
let mut c = new_simplicial_complex(4);
add_simplex1(&mut c, 0, 1);
add_simplex1(&mut c, 0, 1);
add_simplex1(&mut c, 1, 0); assert_eq!(c.edges.len(), 1);
}
#[test]
fn test_add_simplex2_adds_edges() {
let mut c = new_simplicial_complex(4);
add_simplex2(&mut c, 0, 1, 2);
assert_eq!(c.triangles.len(), 1);
assert_eq!(c.edges.len(), 3);
}
#[test]
fn test_add_simplex2_no_duplicate_triangle() {
let mut c = new_simplicial_complex(4);
add_simplex2(&mut c, 0, 1, 2);
add_simplex2(&mut c, 1, 2, 0); assert_eq!(c.triangles.len(), 1);
}
#[test]
fn test_boundary_of_edge() {
let e = Simplex1 { v0: 3, v1: 7 };
let b = boundary_of_edge(&e);
assert_eq!(b[0].vertex, 3);
assert_eq!(b[1].vertex, 7);
}
#[test]
fn test_boundary_of_triangle_three_edges() {
let t = Simplex2 {
v0: 0,
v1: 1,
v2: 2,
};
let edges = boundary_of_triangle(&t);
assert_eq!(edges.len(), 3);
for e in &edges {
assert!(e.v0 <= e.v1);
}
}
#[test]
fn test_euler_characteristic_single_triangle() {
let mut c = new_simplicial_complex(3);
add_simplex2(&mut c, 0, 1, 2);
assert_eq!(euler_characteristic(&c), 1);
}
#[test]
fn test_euler_characteristic_tetrahedron() {
let mut c = new_simplicial_complex(4);
add_simplex2(&mut c, 0, 1, 2);
add_simplex2(&mut c, 0, 1, 3);
add_simplex2(&mut c, 0, 2, 3);
add_simplex2(&mut c, 1, 2, 3);
assert_eq!(euler_characteristic(&c), 2);
}
#[test]
fn test_betti_0_connected() {
let mut c = new_simplicial_complex(4);
add_simplex1(&mut c, 0, 1);
add_simplex1(&mut c, 1, 2);
add_simplex1(&mut c, 2, 3);
assert_eq!(betti_0(&c), 1);
}
#[test]
fn test_betti_0_two_components() {
let mut c = new_simplicial_complex(4);
add_simplex1(&mut c, 0, 1);
add_simplex1(&mut c, 2, 3);
assert_eq!(betti_0(&c), 2);
}
#[test]
fn test_from_mesh_indices() {
let indices: Vec<u32> = vec![0, 1, 2, 0, 2, 3];
let c = from_mesh_indices(4, &indices);
assert_eq!(simplex2_count(&c), 2);
assert_eq!(simplex1_count(&c), 5); }
#[test]
fn test_is_manifold_simplex_true() {
let mut c = new_simplicial_complex(4);
add_simplex2(&mut c, 0, 1, 2);
add_simplex2(&mut c, 0, 2, 3);
assert!(is_manifold_simplex(&c));
}
#[test]
fn test_is_manifold_simplex_false() {
let mut c = new_simplicial_complex(5);
add_simplex2(&mut c, 0, 1, 2);
add_simplex2(&mut c, 0, 1, 3);
add_simplex2(&mut c, 0, 1, 4);
assert!(!is_manifold_simplex(&c));
}
#[test]
fn test_boundary_edges_simplex() {
let indices: Vec<u32> = vec![0, 1, 2, 0, 2, 3];
let c = from_mesh_indices(4, &indices);
let boundary = boundary_edges_simplex(&c);
assert_eq!(boundary.len(), 4);
}
#[test]
fn test_dual_graph_simplex_adjacency() {
let mut c = new_simplicial_complex(4);
add_simplex2(&mut c, 0, 1, 2);
add_simplex2(&mut c, 0, 2, 3);
let adj = dual_graph_simplex(&c);
assert_eq!(adj.len(), 2);
assert!(adj[0].contains(&1));
assert!(adj[1].contains(&0));
}
#[test]
fn test_simplex1_count_and_simplex2_count() {
let mut c = new_simplicial_complex(4);
add_simplex2(&mut c, 0, 1, 2);
assert_eq!(simplex1_count(&c), 3);
assert_eq!(simplex2_count(&c), 1);
}
}