1use std::fmt;
25
26use super::ComplexElement;
27
28#[derive(Debug, Clone, PartialEq, Eq, Hash)]
41pub struct Cube {
42 pub vertices: Vec<usize>,
44 pub dimension: usize,
46 pub id: Option<usize>,
48}
49
50impl Cube {
51 pub fn new(dimension: usize, vertices: Vec<usize>) -> Self {
63 let expected_vertex_count = 1 << dimension; assert_eq!(
65 vertices.len(),
66 expected_vertex_count,
67 "A {}-cube must have exactly {} vertices, got {}",
68 dimension,
69 expected_vertex_count,
70 vertices.len()
71 );
72 Self { vertices, dimension, id: None }
73 }
74
75 pub fn vertex(vertex: usize) -> Self { Self::new(0, vec![vertex]) }
77
78 pub fn edge(v0: usize, v1: usize) -> Self { Self::new(1, vec![v0, v1]) }
80
81 pub fn square(vertices: [usize; 4]) -> Self { Self::new(2, vertices.to_vec()) }
85
86 const fn with_id(mut self, new_id: usize) -> Self {
88 self.id = Some(new_id);
89 self
90 }
91
92 pub fn vertices(&self) -> &[usize] { &self.vertices }
94
95 pub const fn dimension(&self) -> usize { self.dimension }
97
98 pub const fn id(&self) -> Option<usize> { self.id }
100
101 pub fn same_content(&self, other: &Self) -> bool {
103 self.dimension == other.dimension && self.vertices == other.vertices
104 }
105}
106
107impl PartialOrd for Cube {
108 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { Some(self.cmp(other)) }
109}
110
111impl Ord for Cube {
112 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
113 self.dimension.cmp(&other.dimension).then_with(|| self.vertices.cmp(&other.vertices))
115 }
116}
117
118impl fmt::Display for Cube {
119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 write!(f, "Cube{}(vertices:{:?})", self.dimension, self.vertices)
121 }
122}
123
124impl ComplexElement for Cube {
125 fn dimension(&self) -> usize { self.dimension }
126
127 fn faces(&self) -> Vec<Self> {
128 if self.dimension == 0 {
129 return Vec::new(); }
131
132 let mut faces = Vec::new();
133 let k = self.dimension;
134
135 for coord_idx in 0..k {
138 for bit_value in [0, 1] {
141 let mut face_vertices = Vec::new();
142
143 for (vertex_idx, &vertex) in self.vertices.iter().enumerate() {
145 if ((vertex_idx >> coord_idx) & 1) == bit_value {
146 face_vertices.push(vertex);
147 }
148 }
149
150 if !face_vertices.is_empty() && face_vertices.len() == (1 << (k - 1)) {
152 let face = Self::new(k - 1, face_vertices);
153 faces.push(face);
154 }
155 }
156 }
157
158 faces
159 }
160
161 fn boundary_with_orientations(&self) -> Vec<(Self, i32)> {
162 if self.dimension == 0 {
163 return Vec::new();
164 }
165
166 let mut faces_with_orientations = Vec::new();
167 let k = self.dimension;
168
169 for coord_idx in 0..k {
171 for (bit_value, base_sign) in [(0, -1), (1, 1)] {
173 let mut face_vertices = Vec::new();
174
175 for (vertex_idx, &vertex) in self.vertices.iter().enumerate() {
177 if ((vertex_idx >> coord_idx) & 1) == bit_value {
178 face_vertices.push(vertex);
179 }
180 }
181
182 if !face_vertices.is_empty() && face_vertices.len() == (1 << (k - 1)) {
184 let face = Self::new(k - 1, face_vertices);
185 let orientation = base_sign * if coord_idx % 2 == 0 { 1 } else { -1 };
187 faces_with_orientations.push((face, orientation));
188 }
189 }
190 }
191
192 faces_with_orientations
193 }
194
195 fn id(&self) -> Option<usize> { self.id }
196
197 fn same_content(&self, other: &Self) -> bool { self.same_content(other) }
198
199 fn with_id(&self, new_id: usize) -> Self { self.clone().with_id(new_id) }
200}
201
202#[cfg(test)]
203mod tests {
204 use harness_algebra::algebras::boolean::Boolean;
205
206 use super::*;
207 use crate::{complexes::Complex, homology::Chain};
208
209 #[test]
210 fn test_cube_creation() {
211 let vertex = Cube::vertex(42);
212 assert_eq!(vertex.dimension(), 0);
213 assert_eq!(vertex.vertices(), &[42]);
214 assert_eq!(vertex.id(), None);
215
216 let edge = Cube::edge(10, 11);
217 assert_eq!(edge.dimension(), 1);
218 assert_eq!(edge.vertices(), &[10, 11]);
219 assert_eq!(edge.id(), None);
220
221 let square = Cube::square([0, 1, 2, 3]);
222 assert_eq!(square.dimension(), 2);
223 assert_eq!(square.vertices(), &[0, 1, 2, 3]);
224 assert_eq!(square.id(), None);
225 }
226
227 #[test]
228 fn test_cube_with_id() {
229 let cube = Cube::edge(10, 11);
230 assert_eq!(cube.id(), None);
231
232 let cube_with_id = cube.with_id(42);
233 assert_eq!(cube_with_id.id(), Some(42));
234 assert_eq!(cube_with_id.vertices(), &[10, 11]); }
236
237 #[test]
238 fn test_cube_same_content() {
239 let cube1 = Cube::edge(10, 11);
240 let cube2 = Cube::edge(10, 11); let cube3 = Cube::edge(10, 12); let cube4 = cube1.clone().with_id(42); assert!(cube1.same_content(&cube2));
245 assert!(!cube1.same_content(&cube3));
246 assert!(cube1.same_content(&cube4)); }
248
249 #[test]
250 fn test_cube_ordering() {
251 let v1 = Cube::vertex(0);
252 let v2 = Cube::vertex(1);
253 let e1 = Cube::edge(0, 1);
254
255 assert!(v1 < v2); assert!(v1 < e1); }
258
259 #[test]
260 fn test_cube_faces() {
261 let vertex = Cube::vertex(42);
263 let vertex_faces = vertex.faces();
264 assert_eq!(vertex_faces.len(), 0); let edge = Cube::edge(10, 11);
268 let edge_faces = edge.faces();
269 assert_eq!(edge_faces.len(), 2); for face in &edge_faces {
273 assert_eq!(face.dimension(), 0);
274 assert_eq!(face.vertices().len(), 1);
275 }
276
277 let face_vertices: Vec<usize> = edge_faces.iter().flat_map(Cube::vertices).copied().collect();
279 assert!(face_vertices.contains(&10));
280 assert!(face_vertices.contains(&11));
281
282 let square = Cube::square([0, 1, 2, 3]);
284 let square_faces = square.faces();
285 assert_eq!(square_faces.len(), 4); for face in &square_faces {
289 assert_eq!(face.dimension(), 1);
290 assert_eq!(face.vertices().len(), 2);
291 }
292 }
293
294 #[test]
295 #[should_panic = "A 1-cube must have exactly 2 vertices, got 3"]
296 fn test_invalid_vertex_count() {
297 Cube::new(1, vec![0, 1, 2]);
299 }
300
301 #[test]
302 fn test_cubical_complex_basic() {
303 let mut complex: Complex<Cube> = Complex::new();
304 let square = Cube::square([0, 1, 2, 3]);
305 complex.join_element(square);
306
307 assert_eq!(complex.elements_of_dimension(2).len(), 1); assert_eq!(complex.elements_of_dimension(1).len(), 4); assert_eq!(complex.elements_of_dimension(0).len(), 4); }
311
312 #[test]
313 fn test_cubical_chain_operations() {
314 let mut complex: Complex<Cube> = Complex::new();
315
316 let edge1 = Cube::edge(0, 1);
318 let edge2 = Cube::edge(1, 2);
319 let added_edge1 = complex.join_element(edge1);
320 let added_edge2 = complex.join_element(edge2);
321
322 let chain1 = Chain::from_item_and_coeff(&complex, added_edge1, 1_i32);
323 let chain2 = Chain::from_item_and_coeff(&complex, added_edge2, 2_i32);
324
325 let result = chain1 + chain2;
326
327 assert_eq!(result.items.len(), 2);
328 assert_eq!(result.coefficients, vec![1, 2]);
329 }
330
331 #[test]
332 fn test_cubical_boundary_operations() {
333 let mut complex: Complex<Cube> = Complex::new();
334
335 let edge = Cube::edge(0, 1);
337 let added_edge = complex.join_element(edge);
338 let chain = Chain::from_item_and_coeff(&complex, added_edge, 1);
339
340 let boundary = chain.boundary();
341 assert_eq!(boundary.items.len(), 2); let square = Cube::square([0, 1, 2, 3]);
345 let added_square = complex.join_element(square);
346 let square_chain = Chain::from_item_and_coeff(&complex, added_square, 1);
347
348 let square_boundary = square_chain.boundary();
349 assert_eq!(square_boundary.items.len(), 4); }
351
352 #[test]
353 fn test_cubical_boundary_squared_is_zero() {
354 let mut complex: Complex<Cube> = Complex::new();
355
356 let square = Cube::square([0, 1, 2, 3]);
357 let added_square = complex.join_element(square);
358 let chain = Chain::from_item_and_coeff(&complex, added_square, 1);
359
360 let boundary = chain.boundary();
361 let boundary_squared = boundary.boundary();
362
363 assert_eq!(boundary_squared.items.len(), 0);
365 assert_eq!(boundary_squared.coefficients.len(), 0);
366 }
367
368 #[test]
369 fn test_cubical_incidence_poset_condition_1() {
370 let mut complex: Complex<Cube> = Complex::new();
372
373 let square = Cube::square([0, 1, 2, 3]);
374 let added_square = complex.join_element(square);
375
376 let vertices = complex.elements_of_dimension(0);
377 let edges = complex.elements_of_dimension(1);
378
379 let boundary_with_orientations = added_square.boundary_with_orientations();
380 for (face, _orientation) in boundary_with_orientations {
381 assert_eq!(face.dimension(), 1);
382 assert!(edges.iter().any(|e| e.same_content(&face)));
383 }
384
385 for edge in &edges {
386 let edge_boundary = edge.boundary_with_orientations();
387 for (vertex_face, _orientation) in edge_boundary {
388 assert_eq!(vertex_face.dimension(), 0);
389 assert!(vertices.iter().any(|v| v.same_content(&vertex_face)));
390 }
391 }
392 }
393
394 #[test]
395 fn test_cubical_incidence_poset_condition_2() {
396 let mut complex: Complex<Cube> = Complex::new();
398
399 let square = Cube::square([0, 1, 2, 3]);
401 let added_square = complex.join_element(square);
402
403 let square_chain = Chain::from_item_and_coeff(&complex, added_square, 1);
405
406 let boundary_1 = square_chain.boundary();
408
409 let boundary_2 = boundary_1.boundary();
411
412 assert_eq!(boundary_2.items.len(), 0, "∂² should be zero for cubical complex");
414 assert_eq!(boundary_2.coefficients.len(), 0, "∂² should have no coefficients");
415
416 let cube = Cube::new(3, vec![0, 1, 2, 3, 4, 5, 6, 7]);
418 let mut cube_complex: Complex<Cube> = Complex::new();
419 let added_cube = cube_complex.join_element(cube);
420
421 let cube_chain = Chain::from_item_and_coeff(&cube_complex, added_cube, 1);
422 let cube_boundary_1 = cube_chain.boundary();
423 let cube_boundary_2 = cube_boundary_1.boundary();
424
425 assert_eq!(cube_boundary_2.items.len(), 0, "∂² should be zero for 3D cube");
426 }
427
428 #[test]
429 fn test_cubical_homology_point() {
430 let mut complex: Complex<Cube> = Complex::new();
431 let vertex = Cube::vertex(0);
432 complex.join_element(vertex);
433
434 let h0 = complex.homology::<Boolean>(0);
435 let h1 = complex.homology::<Boolean>(1);
436
437 assert_eq!(h0.betti_number, 1); assert_eq!(h1.betti_number, 0); }
440
441 #[test]
442 fn test_cubical_homology_edge() {
443 let mut complex: Complex<Cube> = Complex::new();
444 let edge = Cube::edge(0, 1);
445 complex.join_element(edge);
446
447 let h0 = complex.homology::<Boolean>(0);
448 let h1 = complex.homology::<Boolean>(1);
449
450 assert_eq!(h0.betti_number, 1); assert_eq!(h1.betti_number, 0); }
453
454 #[test]
455 fn test_cubical_homology_square_boundary() {
456 let mut complex: Complex<Cube> = Complex::new();
457
458 let edge1 = Cube::edge(0, 1);
460 let edge2 = Cube::edge(1, 2);
461 let edge3 = Cube::edge(2, 3);
462 let edge4 = Cube::edge(3, 0);
463
464 complex.join_element(edge1);
465 complex.join_element(edge2);
466 complex.join_element(edge3);
467 complex.join_element(edge4);
468
469 let h0 = complex.homology::<Boolean>(0);
470 let h1 = complex.homology::<Boolean>(1);
471
472 assert_eq!(h0.betti_number, 1); assert_eq!(h1.betti_number, 1); }
475
476 #[test]
477 fn test_cubical_homology_filled_square() {
478 let mut complex: Complex<Cube> = Complex::new();
479 let square = Cube::square([0, 1, 2, 3]);
480 complex.join_element(square);
481
482 let h0 = complex.homology::<Boolean>(0);
483 let h1 = complex.homology::<Boolean>(1);
484 let h2 = complex.homology::<Boolean>(2);
485
486 assert_eq!(h0.betti_number, 1); assert_eq!(h1.betti_number, 0); assert_eq!(h2.betti_number, 0); }
490
491 #[test]
492 fn test_cubical_homology_two_disjoint_squares() {
493 let mut complex: Complex<Cube> = Complex::new();
494
495 let square1 = Cube::square([0, 1, 2, 3]);
496 let square2 = Cube::square([4, 5, 6, 7]);
497 complex.join_element(square1);
498 complex.join_element(square2);
499
500 let h0 = complex.homology::<Boolean>(0);
501 let h1 = complex.homology::<Boolean>(1);
502
503 assert_eq!(h0.betti_number, 2); assert_eq!(h1.betti_number, 0); }
506}