use std::collections::{HashMap, HashSet};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Simplex {
pub vertices: Vec<usize>,
}
impl Simplex {
pub fn new(mut vertices: Vec<usize>) -> Self {
vertices.sort_unstable();
vertices.dedup();
Self { vertices }
}
pub fn vertex(v: usize) -> Self {
Self { vertices: vec![v] }
}
pub fn edge(v0: usize, v1: usize) -> Self {
Self::new(vec![v0, v1])
}
pub fn triangle(v0: usize, v1: usize, v2: usize) -> Self {
Self::new(vec![v0, v1, v2])
}
pub fn dim(&self) -> usize {
if self.vertices.is_empty() {
0
} else {
self.vertices.len() - 1
}
}
pub fn is_vertex(&self) -> bool {
self.vertices.len() == 1
}
pub fn is_edge(&self) -> bool {
self.vertices.len() == 2
}
pub fn faces(&self) -> Vec<Simplex> {
if self.vertices.len() <= 1 {
return vec![];
}
(0..self.vertices.len())
.map(|i| {
let face_verts: Vec<usize> = self
.vertices
.iter()
.enumerate()
.filter(|&(j, _)| j != i)
.map(|(_, &v)| v)
.collect();
Simplex::new(face_verts)
})
.collect()
}
pub fn is_face_of(&self, other: &Simplex) -> bool {
if self.vertices.len() >= other.vertices.len() {
return false;
}
self.vertices.iter().all(|v| other.vertices.contains(v))
}
pub fn shares_face_with(&self, other: &Simplex) -> bool {
let intersection: Vec<usize> = self
.vertices
.iter()
.filter(|v| other.vertices.contains(v))
.copied()
.collect();
!intersection.is_empty()
}
}
#[derive(Debug, Clone)]
pub struct SimplicialComplex {
simplices: Vec<HashSet<Simplex>>,
max_dim: usize,
}
impl SimplicialComplex {
pub fn new() -> Self {
Self {
simplices: vec![HashSet::new()],
max_dim: 0,
}
}
pub fn from_simplices(simplices: Vec<Simplex>) -> Self {
let mut complex = Self::new();
for s in simplices {
complex.add(s);
}
complex
}
pub fn add(&mut self, simplex: Simplex) {
let dim = simplex.dim();
while self.simplices.len() <= dim {
self.simplices.push(HashSet::new());
}
self.max_dim = self.max_dim.max(dim);
self.add_with_faces(simplex);
}
fn add_with_faces(&mut self, simplex: Simplex) {
let dim = simplex.dim();
if self.simplices[dim].contains(&simplex) {
return; }
for face in simplex.faces() {
self.add_with_faces(face);
}
self.simplices[dim].insert(simplex);
}
pub fn contains(&self, simplex: &Simplex) -> bool {
let dim = simplex.dim();
if dim >= self.simplices.len() {
return false;
}
self.simplices[dim].contains(simplex)
}
pub fn simplices_of_dim(&self, d: usize) -> impl Iterator<Item = &Simplex> {
self.simplices.get(d).into_iter().flat_map(|s| s.iter())
}
pub fn all_simplices(&self) -> impl Iterator<Item = &Simplex> {
self.simplices.iter().flat_map(|s| s.iter())
}
pub fn count_dim(&self, d: usize) -> usize {
self.simplices.get(d).map(|s| s.len()).unwrap_or(0)
}
pub fn size(&self) -> usize {
self.simplices.iter().map(|s| s.len()).sum()
}
pub fn dimension(&self) -> usize {
self.max_dim
}
pub fn f_vector(&self) -> Vec<usize> {
self.simplices.iter().map(|s| s.len()).collect()
}
pub fn euler_characteristic(&self) -> i64 {
self.simplices
.iter()
.enumerate()
.map(|(d, s)| {
let sign = if d % 2 == 0 { 1 } else { -1 };
sign * s.len() as i64
})
.sum()
}
pub fn vertices(&self) -> HashSet<usize> {
self.simplices_of_dim(0)
.flat_map(|s| s.vertices.iter().copied())
.collect()
}
pub fn edges(&self) -> Vec<(usize, usize)> {
self.simplices_of_dim(1)
.filter_map(|s| {
if s.vertices.len() == 2 {
Some((s.vertices[0], s.vertices[1]))
} else {
None
}
})
.collect()
}
}
impl Default for SimplicialComplex {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simplex_creation() {
let vertex = Simplex::vertex(0);
assert_eq!(vertex.dim(), 0);
let edge = Simplex::edge(0, 1);
assert_eq!(edge.dim(), 1);
let triangle = Simplex::triangle(0, 1, 2);
assert_eq!(triangle.dim(), 2);
}
#[test]
fn test_simplex_faces() {
let triangle = Simplex::triangle(0, 1, 2);
let faces = triangle.faces();
assert_eq!(faces.len(), 3);
assert!(faces.contains(&Simplex::edge(0, 1)));
assert!(faces.contains(&Simplex::edge(0, 2)));
assert!(faces.contains(&Simplex::edge(1, 2)));
}
#[test]
fn test_simplicial_complex() {
let mut complex = SimplicialComplex::new();
complex.add(Simplex::triangle(0, 1, 2));
assert_eq!(complex.count_dim(0), 3);
assert_eq!(complex.count_dim(1), 3);
assert_eq!(complex.count_dim(2), 1);
assert_eq!(complex.euler_characteristic(), 1); }
#[test]
fn test_f_vector() {
let complex = SimplicialComplex::from_simplices(vec![
Simplex::triangle(0, 1, 2),
Simplex::triangle(1, 2, 3),
]);
let f = complex.f_vector();
assert_eq!(f[0], 4); assert_eq!(f[1], 5); assert_eq!(f[2], 2); }
#[test]
fn test_is_face_of() {
let edge = Simplex::edge(0, 1);
let triangle = Simplex::triangle(0, 1, 2);
assert!(edge.is_face_of(&triangle));
assert!(!triangle.is_face_of(&edge));
}
}