Skip to main content

cova_space/complexes/
mod.rs

1//! # Complexes Module
2//!
3//! This module provides data structures and algorithms for working with various types of
4//! topological complexes, which are fundamental tools in algebraic topology for representing
5//! and analyzing the structure of topological spaces.
6//!
7//! ## Mathematical Background
8//!
9//! A **cell complex** (or CW complex) is a topological space constructed by gluing together
10//! cells of various dimensions. The key insight is that complex topological spaces can be
11//! built up systematically from simple pieces:
12//!
13//! - **0-cells**: Points (vertices)
14//! - **1-cells**: Edges connecting vertices
15//! - **2-cells**: Faces (triangles, squares) with edges as boundaries
16//! - **k-cells**: Higher-dimensional analogs
17//!
18//! This module focuses on two important special cases:
19//! - **Simplicial complexes**: Built from simplices (points, edges, triangles, tetrahedra, ...)
20//! - **Cubical complexes**: Built from cubes (points, edges, squares, cubes, ...)
21//!
22//! ## Core Abstractions
23//!
24//! ### Generic Complex Structure
25//!
26//! The [`Complex<T>`] type provides a generic container that can work with any element type
27//! implementing [`ComplexElement`]. This allows the same algorithms (homology computation,
28//! boundary operators, etc.) to work on both simplicial and cubical complexes.
29//!
30//! ### Face Relations and Closure Property
31//!
32//! All complexes satisfy the **closure property**: if a k-cell is in the complex, then all
33//! of its faces (boundary cells) must also be in the complex. This is automatically
34//! enforced when adding elements via [`Complex::join_element`].
35//!
36//! ### Orientation and Boundary Operators
37//!
38//! Each element type implements its own boundary operator via
39//! [`ComplexElement::boundary_with_orientations`], which returns faces with their correct
40//! orientation coefficients. This enables:
41//! - Computation of boundary matrices for homology
42//! - Chain complex structure (∂² = 0)
43//! - Proper handling of orientation in both simplicial and cubical settings
44//!
45//! ## Usage Patterns
46//!
47//! ### Basic Complex Construction
48//!
49//! ```rust
50//! use cova_space::complexes::{Complex, Simplex};
51//!
52//! let mut complex = Complex::new();
53//!
54//! // Add a triangle - automatically includes all faces
55//! let triangle = Simplex::new(2, vec![0, 1, 2]);
56//! let added_triangle = complex.join_element(triangle);
57//!
58//! // Complex now contains: 1 triangle, 3 edges, 3 vertices
59//! assert_eq!(complex.elements_of_dimension(2).len(), 1);
60//! assert_eq!(complex.elements_of_dimension(1).len(), 3);
61//! assert_eq!(complex.elements_of_dimension(0).len(), 3);
62//! ```
63//!
64//! ### Homology Computation
65//!
66//! ```rust
67//! use cova_algebra::algebras::boolean::Boolean;
68//! use cova_space::{
69//!   complexes::{Simplex, SimplicialComplex},
70//!   prelude::*,
71//! };
72//!
73//! let mut complex = SimplicialComplex::new();
74//! let triangle = Simplex::new(2, vec![0, 1, 2]);
75//!
76//! // Compute homology over Z/2Z
77//! let h0 = complex.homology::<Boolean>(0); // Connected components
78//! let h1 = complex.homology::<Boolean>(1); // 1D holes
79//!
80//! println!("β₀ = {}, β₁ = {}", h0.betti_number, h1.betti_number);
81//! ```
82//!
83//! ### Working with Different Element Types
84//!
85//! ```rust
86//! use cova_space::complexes::{Cube, CubicalComplex, Simplex, SimplicialComplex};
87//!
88//! // Simplicial complex with triangles
89//! let mut simplicial = SimplicialComplex::new();
90//! let triangle = Simplex::new(2, vec![0, 1, 2]);
91//! simplicial.join_element(triangle);
92//!
93//! // Cubical complex with squares
94//! let mut cubical = CubicalComplex::new();
95//! let square = Cube::square([0, 1, 2, 3]);
96//! cubical.join_element(square);
97//!
98//! // Both support the same operations
99//! assert_eq!(simplicial.max_dimension(), 2);
100//! assert_eq!(cubical.max_dimension(), 2);
101//! ```
102//!
103//! ## Implementation Details
104//!
105//! ### Efficient Storage and Lookup
106//!
107//! - Elements are stored in a `HashMap<usize, T>` keyed by unique IDs
108//! - Face relationships are tracked in a `Lattice<usize>` using IDs for efficiency
109//! - Duplicate elements (same mathematical content) are automatically deduplicated
110//! - ID assignment is automatic but can be controlled when needed
111//!
112//! ### Poset Structure
113//!
114//! Complexes implement [`Poset`] where the partial order represents the face relation:
115//! `a ≤ b` means "a is a face of b". This enables:
116//! - Efficient computation of upsets/downsets (star/closure operations)
117//! - Join/meet operations for common faces/cofaces
118//! - Integration with other algebraic structures
119//!
120//! ### Topology Interface
121//!
122//! Complexes implement [`Topology`] providing:
123//! - Neighborhood operations (finding cofaces)
124//! - Boundary operators returning [`Chain`] objects
125//! - Integration with homology and sheaf computations
126//!
127//! ## Submodules
128//!
129//! - [`simplicial`]: Definitions for [`Simplex`] and simplicial complex operations
130//! - [`cubical`]: Definitions for [`Cube`] and cubical complex operations
131//!
132//! ## Examples
133//!
134//! See the extensive test suite at the bottom of this module for examples of:
135//! - Constructing various complex types
136//! - Computing homology of standard spaces
137//! - Working with the poset and topology interfaces
138//! - Verifying fundamental properties like ∂² = 0
139
140use std::collections::HashMap;
141
142use cova_algebra::{
143  rings::Field,
144  tensors::{DMatrix, DVector, compute_quotient_basis, image, kernel},
145};
146
147use super::*;
148use crate::{
149  definitions::Topology,
150  homology::{Chain, Homology},
151  lattice::Lattice,
152  set::{Collection, Poset},
153};
154
155pub mod cubical;
156pub mod simplicial;
157
158pub use cubical::Cube;
159pub use simplicial::Simplex;
160
161/// A type alias for a simplicial complex.
162pub type SimplicialComplex = Complex<Simplex>;
163
164/// A type alias for a cubical complex.
165pub type CubicalComplex = Complex<Cube>;
166
167/// Trait for elements that can be part of a topological complex.
168///
169/// This trait captures the essential behavior needed for elements (simplices, cubes, cells, etc.)
170/// to work with the generic [`Complex<T>`] structure. It abstracts over the common operations
171/// that any type of cell complex element must support.
172///
173/// # Mathematical Foundation
174///
175/// In algebraic topology, a complex is built from cells of various dimensions with specific
176/// face relations. This trait encapsulates the core properties that any cell type must have:
177///
178/// 1. **Dimension**: Each cell has an intrinsic dimension (0 for vertices, 1 for edges, etc.)
179/// 2. **Boundary Structure**: Each cell has well-defined boundary cells (faces)
180/// 3. **Orientation**: Boundary relationships include orientation information for chain complexes
181/// 4. **Identity**: Cells can be uniquely identified when added to complexes
182/// 5. **Content Equality**: Mathematical content can be compared regardless of ID assignment
183///
184/// # Design Philosophy
185///
186/// This trait is designed to be:
187/// - **Generic**: Works with simplices, cubes, and other cell types
188/// - **Efficient**: Supports ID-based operations for large complexes
189/// - **Mathematically Correct**: Preserves orientation and boundary relationships
190/// - **Flexible**: Allows different boundary operator conventions per element type
191///
192/// # Implementation Requirements
193///
194/// Types implementing this trait must:
195/// - Be cloneable, hashable, and orderable for use in collections
196/// - Compute their own faces and boundary operators correctly
197/// - Handle ID assignment and content comparison properly
198/// - Maintain mathematical consistency in their face relationships
199///
200/// # Examples
201///
202/// ```rust
203/// use cova_space::complexes::{ComplexElement, Simplex};
204///
205/// // Create a triangle (2-simplex)
206/// let triangle = Simplex::new(2, vec![0, 1, 2]);
207///
208/// // Basic properties
209/// assert_eq!(triangle.dimension(), 2);
210/// assert_eq!(triangle.id(), None); // No ID until added to complex
211///
212/// // Compute faces (should be 3 edges)
213/// let faces = triangle.faces();
214/// assert_eq!(faces.len(), 3);
215/// assert!(faces.iter().all(|face| face.dimension() == 1));
216///
217/// // Compute boundary with orientations
218/// let boundary = triangle.boundary_with_orientations();
219/// assert_eq!(boundary.len(), 3);
220/// // Each face has orientation ±1
221/// assert!(boundary.iter().all(|(_, orient)| orient.abs() == 1));
222/// ```
223pub trait ComplexElement: Clone + std::hash::Hash + Eq + PartialOrd + Ord {
224  /// Returns the intrinsic dimension of this element.
225  ///
226  /// The dimension determines the element's place in the chain complex:
227  /// - 0-dimensional: vertices/points
228  /// - 1-dimensional: edges/curves
229  /// - 2-dimensional: faces/surfaces
230  /// - k-dimensional: k-cells
231  ///
232  /// # Mathematical Note
233  ///
234  /// For a k-dimensional element, its faces are (k-1)-dimensional, and it can be
235  /// a face of (k+1)-dimensional elements. This creates the graded structure
236  /// essential for homological computations.
237  ///
238  /// # Examples
239  ///
240  /// ```rust
241  /// # use cova_space::complexes::{ComplexElement, Simplex, Cube};
242  /// let vertex = Simplex::new(0, vec![42]);
243  /// assert_eq!(vertex.dimension(), 0);
244  ///
245  /// let edge = Cube::edge(0, 1);
246  /// assert_eq!(edge.dimension(), 1);
247  /// ```
248  fn dimension(&self) -> usize;
249
250  /// Returns all faces (boundary elements) of this element.
251  ///
252  /// For a k-dimensional element, this returns all (k-1)-dimensional faces that
253  /// form its boundary. This is the **combinatorial boundary** - it captures the
254  /// face structure without orientation information.
255  ///
256  /// # Mathematical Background
257  ///
258  /// In topology, the boundary ∂σ of a cell σ consists of all the cells in its
259  /// boundary. For example:
260  /// - Triangle faces: the three edges forming its boundary
261  /// - Tetrahedron faces: the four triangular faces
262  /// - Square faces: the four edges forming its boundary
263  ///
264  /// # Implementation Notes
265  ///
266  /// - Returned faces should have no ID assigned (will be assigned when added to complex)
267  /// - The order may matter for orientation in [`ComplexElement::boundary_with_orientations`]
268  /// - All faces must have dimension = `self.dimension() - 1`
269  /// - 0-dimensional elements return empty vector (no (-1)-dimensional faces)
270  ///
271  /// # Examples
272  ///
273  /// ```rust
274  /// # use cova_space::complexes::{ComplexElement, Simplex};
275  /// // Triangle has 3 edge faces
276  /// let triangle = Simplex::new(2, vec![0, 1, 2]);
277  /// let faces = triangle.faces();
278  /// assert_eq!(faces.len(), 3);
279  /// assert!(faces.iter().all(|f| f.dimension() == 1));
280  ///
281  /// // Vertex has no faces
282  /// let vertex = Simplex::new(0, vec![0]);
283  /// assert_eq!(vertex.faces().len(), 0);
284  /// ```
285  fn faces(&self) -> Vec<Self>;
286
287  /// Returns the faces with their correct orientation coefficients for boundary computation.
288  ///
289  /// This is the **geometric boundary operator** that includes orientation information
290  /// necessary for chain complex computations. Each face comes with an integer coefficient
291  /// (typically ±1) indicating its orientation in the boundary.
292  ///
293  /// # Mathematical Foundation
294  ///
295  /// In algebraic topology, the boundary operator ∂ₖ: Cₖ → Cₖ₋₁ is defined as:
296  ///
297  /// ```text
298  /// ∂ₖ(σ) = Σᵢ (-1)ⁱ τᵢ
299  /// ```
300  ///
301  /// where the τᵢ are the faces of σ with appropriate orientation signs. The key
302  /// property is that ∂² = 0 (boundary of boundary is zero), which requires
303  /// careful orientation handling.
304  ///
305  /// # Orientation Conventions
306  ///
307  /// Different element types may use different orientation conventions:
308  /// - **Simplicial**: Alternating signs based on vertex position
309  /// - **Cubical**: Signs based on coordinate directions
310  /// - **General CW**: Depends on attaching maps
311  ///
312  /// # Return Format
313  ///
314  /// Returns `Vec<(face, orientation)>` where:
315  /// - `face`: A (k-1)-dimensional face element (without ID)
316  /// - `orientation`: Integer coefficient (usually ±1, could be 0)
317  ///
318  /// # Examples
319  ///
320  /// ```rust
321  /// # use cova_space::complexes::{ComplexElement, Simplex};
322  /// let triangle = Simplex::new(2, vec![0, 1, 2]);
323  /// let boundary = triangle.boundary_with_orientations();
324  ///
325  /// // Triangle boundary: [v₁,v₂] - [v₀,v₂] + [v₀,v₁]
326  /// assert_eq!(boundary.len(), 3);
327  /// assert_eq!(boundary[0].1, 1); // +[v₁,v₂]
328  /// assert_eq!(boundary[1].1, -1); // -[v₀,v₂]
329  /// assert_eq!(boundary[2].1, 1); // +[v₀,v₁]
330  /// ```
331  fn boundary_with_orientations(&self) -> Vec<(Self, i32)>;
332
333  /// Returns the ID if this element has been assigned to a complex, `None` otherwise.
334  ///
335  /// IDs are automatically assigned when elements are added to a [`Complex`] via
336  /// [`Complex::join_element`]. They serve as unique identifiers for efficient
337  /// storage and lookup operations.
338  ///
339  /// # ID Assignment Lifecycle
340  ///
341  /// 1. **Created**: Element starts with `id() = None`
342  /// 2. **Added**: Complex assigns unique ID via [`ComplexElement::with_id`]
343  /// 3. **Stored**: Element with ID is stored in complex's HashMap
344  /// 4. **Referenced**: ID used for lattice operations and lookups
345  ///
346  /// # Examples
347  ///
348  /// ```rust
349  /// # use cova_space::complexes::{Complex, Simplex};
350  /// let simplex = Simplex::new(1, vec![0, 1]);
351  /// assert_eq!(simplex.id(), None);
352  ///
353  /// let mut complex = Complex::new();
354  /// let added = complex.join_element(simplex);
355  /// assert!(added.id().is_some());
356  /// ```
357  fn id(&self) -> Option<usize>;
358
359  /// Checks if this element has the same mathematical content as another.
360  ///
361  /// This comparison ignores ID assignment and focuses purely on the mathematical
362  /// structure of the elements. It's used for deduplication when adding elements
363  /// to complexes.
364  ///
365  /// # Mathematical Equality
366  ///
367  /// Two elements are considered to have the same content if they represent
368  /// the same mathematical object, regardless of:
369  /// - ID assignment (internal bookkeeping)
370  /// - Order of discovery (when added to complex)
371  /// - Memory location or other implementation details
372  ///
373  /// # Usage in Complexes
374  ///
375  /// When [`Complex::join_element`] is called, it first checks if an element
376  /// with the same content already exists using this method. If found, it
377  /// returns the existing element rather than creating a duplicate.
378  ///
379  /// # Examples
380  ///
381  /// ```rust
382  /// # use cova_space::complexes::{ComplexElement, Simplex};
383  /// let simplex1 = Simplex::new(1, vec![0, 1]);
384  /// let simplex2 = Simplex::new(1, vec![0, 1]);
385  /// let simplex3 = simplex1.clone().with_id(42);
386  ///
387  /// assert!(simplex1.same_content(&simplex2)); // Same mathematical content
388  /// assert!(simplex1.same_content(&simplex3)); // ID differences ignored
389  ///
390  /// let different = Simplex::new(1, vec![0, 2]);
391  /// assert!(!simplex1.same_content(&different)); // Different content
392  /// ```
393  fn same_content(&self, other: &Self) -> bool;
394
395  /// Creates a new element with the same content but a specific ID.
396  ///
397  /// This is used internally by [`Complex`] to assign IDs when adding elements.
398  /// The returned element should be identical in all mathematical properties
399  /// but have the specified ID assigned.
400  ///
401  /// # Implementation Requirements
402  ///
403  /// The returned element must satisfy:
404  /// - `result.same_content(self) == true`
405  /// - `result.id() == Some(new_id)`
406  /// - All other properties unchanged (dimension, faces, etc.)
407  ///
408  /// # Usage
409  ///
410  /// This method is primarily used internally by the complex management system.
411  /// Users typically don't need to call it directly.
412  ///
413  /// # Examples
414  ///
415  /// ```rust
416  /// # use cova_space::complexes::{ComplexElement, Simplex};
417  /// let original = Simplex::new(1, vec![0, 1]);
418  /// assert_eq!(original.id(), None);
419  ///
420  /// let with_id = original.with_id(42);
421  /// assert_eq!(with_id.id(), Some(42));
422  /// assert!(original.same_content(&with_id));
423  /// assert_eq!(original.dimension(), with_id.dimension());
424  /// ```
425  fn with_id(&self, new_id: usize) -> Self;
426}
427
428/// A generic topological complex that can work with any type implementing [`ComplexElement`].
429///
430/// This structure provides a unified framework for working with various types of cell complexes
431/// (simplicial, cubical, etc.) while maintaining the essential topological and algebraic
432/// properties needed for homological computations.
433///
434/// # Mathematical Foundation
435///
436/// A **cell complex** K is a topological space built by attaching cells of various dimensions
437/// according to specific rules:
438///
439/// 1. **Closure Property**: If σ ∈ K, then all faces of σ are also in K
440/// 2. **Face Relations**: Form a partial order where τ ≤ σ means "τ is a face of σ"
441/// 3. **Dimension Stratification**: K = K⁰ ∪ K¹ ∪ K² ∪ ... where Kⁱ contains all i-cells
442/// 4. **Chain Complex Structure**: Boundary operators ∂ᵢ: Cᵢ → Cᵢ₋₁ with ∂² = 0
443///
444/// # Implementation Architecture
445///
446/// This implementation uses a **dual-structure approach**:
447///
448/// ```text
449/// ┌─────────────────┐    ┌──────────────────┐
450/// │ attachment_     │    │ elements:        │
451/// │ lattice:        │◄──►│ HashMap<usize,T> │
452/// │ Lattice<usize>  │    │                  │  
453/// └─────────────────┘    └──────────────────┘
454///        ▲                        ▲
455///        │ IDs only               │ Full elements
456///        ▼                        ▼
457///   Fast operations           Rich operations
458/// ```
459///
460/// - **Elements HashMap**: Stores actual elements indexed by unique IDs
461/// - **Attachment Lattice**: Tracks face relationships using IDs for efficiency
462/// - **ID Management**: Automatic assignment with deduplication support
463///
464/// # Key Properties
465///
466/// ## Closure Property Enforcement
467///
468/// The [`Complex::join_element`] method ensures closure: adding any element automatically
469/// includes all its faces. This maintains the fundamental property that distinguishes
470/// complexes from arbitrary cell collections.
471///
472/// ## Efficient Face Queries
473///
474/// Face relationships are stored in a [`Lattice<usize>`] structure, enabling:
475/// - O(1) face/coface lookups after preprocessing
476/// - Efficient upset/downset computations
477/// - Support for meet/join operations on cells
478///
479/// ## Deduplication
480///
481/// Elements with identical mathematical content are automatically deduplicated
482/// using [`ComplexElement::same_content`], preventing redundant storage and
483/// maintaining well-defined structure.
484///
485/// # Usage Patterns
486///
487/// ## Basic Construction
488///
489/// ```rust
490/// use cova_space::complexes::{Complex, Simplex};
491///
492/// let mut complex = Complex::new();
493/// let triangle = Simplex::new(2, vec![0, 1, 2]);
494/// let added = complex.join_element(triangle);
495///
496/// // Automatically includes all faces: 1 triangle + 3 edges + 3 vertices
497/// assert_eq!(complex.elements_of_dimension(2).len(), 1);
498/// assert_eq!(complex.elements_of_dimension(1).len(), 3);
499/// assert_eq!(complex.elements_of_dimension(0).len(), 3);
500/// ```
501///
502/// ## Homology Computation
503///
504/// ```rust
505/// use cova_algebra::algebras::boolean::Boolean;
506/// use cova_space::{
507///   complexes::{Simplex, SimplicialComplex},
508///   prelude::*,
509/// };
510///
511/// let mut complex = SimplicialComplex::new();
512///
513/// // Create a circle (1-dimensional hole)
514/// let edge1 = Simplex::new(1, vec![0, 1]);
515/// let edge2 = Simplex::new(1, vec![1, 2]);
516/// let edge3 = Simplex::new(1, vec![2, 0]);
517/// complex.join_element(edge1);
518/// complex.join_element(edge2);
519/// complex.join_element(edge3);
520///
521/// let h1 = complex.homology::<Boolean>(1);
522/// assert_eq!(h1.betti_number, 1); // One 1D hole
523/// ```
524///
525/// ## Working with Face Relations
526///
527/// ```rust
528/// use cova_space::{
529///   complexes::{Simplex, SimplicialComplex},
530///   prelude::*,
531/// };
532///
533/// let mut complex = SimplicialComplex::new();
534/// let triangle = Simplex::new(2, vec![0, 1, 2]);
535/// let added = complex.join_element(triangle);
536///
537/// // Query face relationships
538/// let faces = complex.faces(&added); // Direct faces only
539/// let cofaces = complex.cofaces(&added); // Direct cofaces only
540/// let downset = complex.downset(added); // All faces (transitive)
541/// ```
542///
543/// # Performance Characteristics
544///
545/// - **Element Access**: O(1) by ID, O(n) by content search
546/// - **Face Queries**: O(1) for direct faces, O(k) for k-dimensional queries
547/// - **Adding Elements**: O(f) where f is the number of faces to add
548/// - **Homology**: O(n³) where n is the number of elements in relevant dimensions
549///
550/// # Type Parameters
551///
552/// * `T`: The element type, must implement [`ComplexElement`]
553///
554/// # Examples
555///
556/// See the extensive test suite for examples including:
557/// - Construction of standard complexes (simplicial, cubical)
558/// - Homology computations for various topological spaces
559/// - Integration with poset and topology interfaces
560#[derive(Debug, Clone)]
561pub struct Complex<T: ComplexElement> {
562  /// The attachment relationships between elements, represented as a lattice of element IDs.
563  ///
564  /// This lattice encodes the face relation: if element `a` is a face of element `b`,
565  /// then `attachment_lattice.leq(a.id(), b.id())` returns `Some(true)`.
566  ///
567  /// Using IDs rather than full elements provides:
568  /// - **Memory efficiency**: IDs are much smaller than full elements
569  /// - **Performance**: Integer comparisons are faster than element comparisons
570  /// - **Flexibility**: Lattice operations independent of element type
571  pub attachment_lattice: Lattice<usize>,
572
573  /// A map storing all elements in the complex, keyed by their assigned ID.
574  ///
575  /// This provides:
576  /// - **Fast lookup**: O(1) access to elements by ID
577  /// - **Rich operations**: Access to full element data and methods
578  /// - **Content queries**: Iteration over elements by dimension, type, etc.
579  pub elements: HashMap<usize, T>,
580
581  /// The counter for the next available unique element identifier.
582  ///
583  /// This ensures that:
584  /// - Each element gets a unique ID when added
585  /// - IDs are assigned sequentially for predictable behavior
586  /// - The complex can manage arbitrary numbers of elements
587  pub next_id: usize,
588}
589
590impl<T: ComplexElement> Complex<T> {
591  /// Creates a new, empty complex.
592  ///
593  /// The empty complex contains no elements and has trivial homology:
594  /// - H₀ = 0 (no connected components)
595  /// - Hₖ = 0 for all k > 0 (no higher-dimensional features)
596  ///
597  /// # Examples
598  ///
599  /// ```rust
600  /// use cova_space::{
601  ///   complexes::{Complex, Simplex},
602  ///   prelude::*,
603  /// };
604  ///
605  /// let complex: Complex<Simplex> = Complex::new();
606  /// assert!(complex.is_empty());
607  /// assert_eq!(complex.max_dimension(), 0);
608  /// ```
609  pub fn new() -> Self {
610    Self {
611      attachment_lattice: Lattice::new(),
612      elements:           HashMap::new(),
613      next_id:            0,
614    }
615  }
616
617  /// Adds an element to the complex along with all its faces.
618  ///
619  /// This is the **fundamental operation** for building complexes. It ensures the closure
620  /// property by recursively adding all faces of the given element. If an element with
621  /// equivalent mathematical content already exists, returns the existing element without
622  /// modification.
623  ///
624  /// # Mathematical Foundation
625  ///
626  /// In topology, a complex must satisfy the **closure property**:
627  ///
628  /// > If σ ∈ K, then ∂σ ⊆ K
629  ///
630  /// This method enforces this property by:
631  /// 1. Computing all faces of the input element via [`ComplexElement::faces`]
632  /// 2. Recursively adding each face (which adds their faces, etc.)
633  /// 3. Establishing face relationships in the attachment lattice
634  /// 4. Deduplicating based on mathematical content
635  ///
636  /// # Algorithm
637  ///
638  /// ```text
639  /// join_element(σ):
640  ///   1. Check if equivalent element already exists → return existing
641  ///   2. Assign ID to σ (reuse existing ID if valid, else assign next_id)
642  ///   3. For each face τ in faces(σ):
643  ///        added_τ ← join_element(τ)  // Recursive call
644  ///        Add relation: added_τ ≤ σ to lattice
645  ///   4. Store σ in elements map
646  ///   5. Return σ with assigned ID
647  /// ```
648  ///
649  /// # ID Assignment Strategy
650  ///
651  /// - **No ID**: Assigns `next_id` and increments counter
652  /// - **Has unused ID**: Preserves existing ID if not taken
653  /// - **Has conflicting ID**: Assigns new ID to avoid conflicts
654  ///
655  /// # Deduplication Logic
656  ///
657  /// Uses [`ComplexElement::same_content`] to check for existing equivalent elements.
658  /// This ensures that mathematically identical elements (regardless of ID) are not
659  /// duplicated in the complex.
660  ///
661  /// # Face Relationship Management
662  ///
663  /// Establishes `face_id ≤ element_id` relationships in the attachment lattice for
664  /// all direct faces. Transitive relationships are computed automatically by the
665  /// lattice structure.
666  ///
667  /// # Return Value
668  ///
669  /// Returns the element as it exists in the complex (with assigned ID). This may be:
670  /// - The input element with a newly assigned ID
671  /// - An existing equivalent element if content matches
672  ///
673  /// # Performance Notes
674  ///
675  /// - **Time**: O(f) where f is the total number of faces to add (including recursive)
676  /// - **Space**: O(n) additional storage where n is the number of new elements
677  /// - **Deduplication**: O(k) check where k is the number of existing elements
678  ///
679  /// # Examples
680  ///
681  /// ## Basic Usage
682  ///
683  /// ```rust
684  /// use cova_space::complexes::{Complex, Simplex};
685  ///
686  /// let mut complex = Complex::new();
687  /// let triangle = Simplex::new(2, vec![0, 1, 2]);
688  ///
689  /// let added = complex.join_element(triangle);
690  /// assert!(added.id().is_some());
691  ///
692  /// // Complex now contains triangle + 3 edges + 3 vertices
693  /// assert_eq!(complex.elements_of_dimension(2).len(), 1);
694  /// assert_eq!(complex.elements_of_dimension(1).len(), 3);
695  /// assert_eq!(complex.elements_of_dimension(0).len(), 3);
696  /// ```
697  ///
698  /// ## Deduplication
699  ///
700  /// ```rust
701  /// # use cova_space::complexes::{Complex, Simplex};
702  /// # let mut complex = Complex::new();
703  /// let edge1 = Simplex::new(1, vec![0, 1]);
704  /// let edge2 = Simplex::new(1, vec![0, 1]); // Same content
705  ///
706  /// let added1 = complex.join_element(edge1);
707  /// let added2 = complex.join_element(edge2);
708  ///
709  /// // Returns same element (by ID)
710  /// assert_eq!(added1.id(), added2.id());
711  /// assert_eq!(complex.elements_of_dimension(1).len(), 1);
712  /// ```
713  ///
714  /// ## Building Complex Structures
715  ///
716  /// ```rust
717  /// # use cova_space::complexes::{Complex, Simplex};
718  /// let mut complex = Complex::new();
719  ///
720  /// // Add multiple triangles sharing edges
721  /// let triangle1 = Simplex::new(2, vec![0, 1, 2]);
722  /// let triangle2 = Simplex::new(2, vec![1, 2, 3]);
723  ///
724  /// complex.join_element(triangle1);
725  /// complex.join_element(triangle2);
726  ///
727  /// // Shared edge [1,2] is not duplicated
728  /// assert_eq!(complex.elements_of_dimension(2).len(), 2); // 2 triangles
729  /// assert_eq!(complex.elements_of_dimension(1).len(), 5); // 5 unique edges
730  /// assert_eq!(complex.elements_of_dimension(0).len(), 4); // 4 vertices
731  /// ```
732  pub fn join_element(&mut self, element: T) -> T {
733    // Check if we already have this element (by mathematical content)
734    if let Some(existing) = self.find_equivalent_element(&element) {
735      return existing;
736    }
737
738    // Assign a new ID to this element
739    let element_with_id =
740      if element.id().is_some() && !self.elements.contains_key(&element.id().unwrap()) {
741        // Element already has an ID and it's not taken
742        if element.id().unwrap() >= self.next_id {
743          self.next_id = element.id().unwrap() + 1;
744        }
745        element
746      } else {
747        // Assign a new ID
748        let new_id = self.next_id;
749        self.next_id += 1;
750        element.with_id(new_id)
751      };
752
753    let mut face_ids = Vec::new();
754    for face in element_with_id.faces() {
755      let added_face = self.join_element(face);
756      face_ids.push(added_face.id().unwrap()); // Safe because we just added it
757    }
758
759    let element_id = element_with_id.id().unwrap();
760    self.attachment_lattice.add_element(element_id);
761
762    for face_id in face_ids {
763      self.attachment_lattice.add_relation(face_id, element_id);
764    }
765
766    self.elements.insert(element_id, element_with_id.clone());
767    element_with_id
768  }
769
770  /// Finds an element in the complex with equivalent mathematical content.
771  ///
772  /// This is used internally by [`join_element`] for deduplication. Returns the first
773  /// element found with matching content, or `None` if no match exists.
774  ///
775  /// # Performance
776  ///
777  /// This performs a linear search through all elements, so it's O(n) where n is the
778  /// total number of elements in the complex. For large complexes, this can be a
779  /// bottleneck in construction.
780  ///
781  /// # Future Optimizations
782  ///
783  /// Potential improvements could include:
784  /// - Content-based hashing for faster lookup
785  /// - Spatial indexing for geometric elements
786  /// - Dimension-stratified search to reduce search space
787  fn find_equivalent_element(&self, element: &T) -> Option<T> {
788    self.elements.values().find(|existing| element.same_content(existing)).cloned()
789  }
790
791  /// Retrieves an element by its unique ID.
792  ///
793  /// Returns `None` if no element with the given ID exists in the complex.
794  /// This is the primary method for accessing elements when you have their ID.
795  ///
796  /// # Performance
797  ///
798  /// O(1) HashMap lookup.
799  ///
800  /// # Examples
801  ///
802  /// ```rust
803  /// # use cova_space::complexes::{Complex, Simplex};
804  /// # let mut complex = Complex::new();
805  /// let simplex = Simplex::new(1, vec![0, 1]);
806  /// let added = complex.join_element(simplex);
807  /// let id = added.id().unwrap();
808  ///
809  /// let retrieved = complex.get_element(id).unwrap();
810  /// assert!(added.same_content(retrieved));
811  /// ```
812  pub fn get_element(&self, id: usize) -> Option<&T> { self.elements.get(&id) }
813
814  /// Returns all elements of a specific dimension.
815  ///
816  /// This is useful for:
817  /// - Constructing basis sets for homology computations
818  /// - Analyzing the dimensional structure of the complex
819  /// - Iterating over elements by type (vertices, edges, faces, etc.)
820  ///
821  /// # Performance
822  ///
823  /// O(n) where n is the total number of elements, as it must check the dimension
824  /// of every element.
825  ///
826  /// # Examples
827  ///
828  /// ```rust
829  /// # use cova_space::complexes::{Complex, Simplex};
830  /// # let mut complex = Complex::new();
831  /// # let triangle = Simplex::new(2, vec![0, 1, 2]);
832  /// # complex.join_element(triangle);
833  ///
834  /// let vertices = complex.elements_of_dimension(0); // All 0-cells
835  /// let edges = complex.elements_of_dimension(1); // All 1-cells
836  /// let faces = complex.elements_of_dimension(2); // All 2-cells
837  ///
838  /// assert_eq!(vertices.len(), 3);
839  /// assert_eq!(edges.len(), 3);
840  /// assert_eq!(faces.len(), 1);
841  /// ```
842  pub fn elements_of_dimension(&self, dimension: usize) -> Vec<T> {
843    self.elements.values().filter(|element| element.dimension() == dimension).cloned().collect()
844  }
845
846  /// Returns the maximum dimension of any element in the complex.
847  ///
848  /// For an empty complex, returns 0. This is useful for determining the
849  /// "dimension" of the complex as a topological space.
850  ///
851  /// # Examples
852  ///
853  /// ```rust
854  /// # use cova_space::complexes::{Complex, Simplex};
855  /// # let mut complex = Complex::new();
856  /// assert_eq!(complex.max_dimension(), 0); // Empty complex
857  ///
858  /// let triangle = Simplex::new(2, vec![0, 1, 2]);
859  /// complex.join_element(triangle);
860  /// assert_eq!(complex.max_dimension(), 2); // 2D complex
861  /// ```
862  pub fn max_dimension(&self) -> usize {
863    self.elements.values().map(ComplexElement::dimension).max().unwrap_or(0)
864  }
865
866  /// Returns the direct faces of an element within this complex.
867  ///
868  /// This differs from [`ComplexElement::faces`] in that it returns elements that
869  /// actually exist in the complex (with assigned IDs) rather than abstract face
870  /// descriptions.
871  ///
872  /// # Relationship to Lattice Operations
873  ///
874  /// This is equivalent to finding the immediate predecessors of the element in
875  /// the face lattice, but returns the full element objects rather than just IDs.
876  ///
877  /// # Examples
878  ///
879  /// ```rust
880  /// # use cova_space::complexes::{Complex, Simplex};
881  /// # let mut complex = Complex::new();
882  /// let triangle = Simplex::new(2, vec![0, 1, 2]);
883  /// let added_triangle = complex.join_element(triangle);
884  ///
885  /// let faces = complex.faces(&added_triangle);
886  /// assert_eq!(faces.len(), 3); // Three edges
887  /// assert!(faces.iter().all(|face| face.dimension() == 1));
888  /// assert!(faces.iter().all(|face| face.id().is_some()));
889  /// ```
890  pub fn faces(&self, element: &T) -> Vec<T> {
891    element.id().map_or_else(Vec::new, |id| {
892      self
893        .attachment_lattice
894        .predecessors(id)
895        .into_iter()
896        .filter_map(|face_id| self.get_element(face_id).cloned())
897        .collect()
898    })
899  }
900
901  /// Returns the direct cofaces of an element within this complex.
902  ///
903  /// Cofaces are elements that have the given element as a face. This is the
904  /// "upward" direction in the face lattice.
905  ///
906  /// # Examples
907  ///
908  /// ```rust
909  /// # use cova_space::complexes::{Complex, Simplex};
910  /// # let mut complex = Complex::new();
911  /// # let triangle = Simplex::new(2, vec![0, 1, 2]);
912  /// # let added_triangle = complex.join_element(triangle);
913  /// # let edges = complex.elements_of_dimension(1);
914  /// # let edge = edges[0].clone();
915  ///
916  /// let cofaces = complex.cofaces(&edge);
917  /// assert_eq!(cofaces.len(), 1); // Edge is face of triangle
918  /// assert!(cofaces[0].same_content(&added_triangle));
919  /// ```
920  pub fn cofaces(&self, element: &T) -> Vec<T> {
921    element.id().map_or_else(Vec::new, |id| {
922      self
923        .attachment_lattice
924        .successors(id)
925        .into_iter()
926        .filter_map(|coface_id| self.get_element(coface_id).cloned())
927        .collect()
928    })
929  }
930
931  /// Computes the k-dimensional homology of the complex over a field F.
932  ///
933  /// Homology measures the "holes" in a topological space at different dimensions:
934  /// - **H₀**: Connected components (0-dimensional holes)
935  /// - **H₁**: Loops/cycles (1-dimensional holes)
936  /// - **H₂**: Voids/cavities (2-dimensional holes)
937  /// - **Hₖ**: k-dimensional holes (higher-dimensional features)
938  ///
939  /// # Mathematical Foundation
940  ///
941  /// Homology is defined as the quotient of cycles by boundaries:
942  ///
943  /// ```text
944  /// Hₖ(K) = Zₖ(K) / Bₖ(K) = ker(∂ₖ) / im(∂ₖ₊₁)
945  /// ```
946  ///
947  /// Where:
948  /// - **Zₖ(K) = ker(∂ₖ)**: k-cycles (chains with no boundary)
949  /// - **Bₖ(K) = im(∂ₖ₊₁)**: k-boundaries (boundaries of (k+1)-chains)
950  /// - **∂ₖ**: Boundary operator from k-chains to (k-1)-chains
951  ///
952  /// The key insight is that "holes" are cycles that are not boundaries of higher-dimensional
953  /// chains.
954  ///
955  /// # Algorithm
956  ///
957  /// 1. **Compute Cycles**: Find kernel of boundary operator ∂ₖ: Cₖ → Cₖ₋₁
958  /// 2. **Compute Boundaries**: Find image of boundary operator ∂ₖ₊₁: Cₖ₊₁ → Cₖ
959  /// 3. **Quotient Space**: Compute basis for quotient space Zₖ/Bₖ
960  /// 4. **Return Homology**: Package result with Betti number and generators
961  ///
962  /// # Special Cases
963  ///
964  /// - **k = 0**: H₀ measures connected components. Z₀ = C₀ (all 0-chains are cycles)
965  /// - **Empty Complex**: All homology groups are trivial (Hₖ = 0)
966  /// - **No k-elements**: Returns trivial homology for that dimension
967  ///
968  /// # Field Dependency
969  ///
970  /// The choice of coefficient field F affects the result:
971  /// - **ℤ/2ℤ (Boolean)**: Ignores orientation, counts mod 2
972  /// - **ℚ (Rationals)**: Full torsion-free homology
973  /// - **ℤ/pℤ (Prime fields)**: Reveals p-torsion in homology
974  ///
975  /// # Performance
976  ///
977  /// - **Time**: O(n³) where n is the number of k-dimensional elements
978  /// - **Space**: O(n²) for storing boundary matrices
979  /// - **Bottleneck**: Matrix kernel and image computations
980  ///
981  /// # Return Value
982  ///
983  /// Returns a [`Homology`] object containing:
984  /// - `dimension`: The dimension k being computed
985  /// - `betti_number`: The rank of Hₖ (number of independent k-dimensional holes)
986  /// - `homology_generators`: Basis vectors representing the homology classes
987  ///
988  /// # Examples
989  ///
990  /// ## Circle (1-dimensional hole)
991  ///
992  /// ```rust
993  /// use cova_algebra::algebras::boolean::Boolean;
994  /// use cova_space::complexes::{Complex, Simplex};
995  ///
996  /// let mut complex = Complex::new();
997  ///
998  /// // Create a triangle boundary (3 edges forming a cycle)
999  /// let edge1 = Simplex::new(1, vec![0, 1]);
1000  /// let edge2 = Simplex::new(1, vec![1, 2]);
1001  /// let edge3 = Simplex::new(1, vec![2, 0]);
1002  /// complex.join_element(edge1);
1003  /// complex.join_element(edge2);
1004  /// complex.join_element(edge3);
1005  ///
1006  /// let h0 = complex.homology::<Boolean>(0);
1007  /// let h1 = complex.homology::<Boolean>(1);
1008  ///
1009  /// assert_eq!(h0.betti_number, 1); // One connected component
1010  /// assert_eq!(h1.betti_number, 1); // One 1-dimensional hole
1011  /// ```
1012  ///
1013  /// ## Filled Triangle (no holes)
1014  ///
1015  /// ```rust
1016  /// # use cova_algebra::algebras::boolean::Boolean;
1017  /// # use cova_space::complexes::{Complex, Simplex};
1018  /// # let mut complex = Complex::new();
1019  ///
1020  /// let triangle = Simplex::new(2, vec![0, 1, 2]);
1021  /// complex.join_element(triangle);
1022  ///
1023  /// let h0 = complex.homology::<Boolean>(0);
1024  /// let h1 = complex.homology::<Boolean>(1);
1025  ///
1026  /// assert_eq!(h0.betti_number, 1); // One connected component
1027  /// assert_eq!(h1.betti_number, 0); // No 1D holes (filled)
1028  /// ```
1029  ///
1030  /// ## Sphere Surface (2-dimensional hole)
1031  ///
1032  /// ```rust
1033  /// # use cova_algebra::algebras::boolean::Boolean;
1034  /// # use cova_space::complexes::{Complex, Simplex};
1035  /// # let mut complex = Complex::new();
1036  ///
1037  /// // Tetrahedron boundary (4 triangular faces)
1038  /// let face1 = Simplex::new(2, vec![0, 1, 2]);
1039  /// let face2 = Simplex::new(2, vec![0, 1, 3]);
1040  /// let face3 = Simplex::new(2, vec![0, 2, 3]);
1041  /// let face4 = Simplex::new(2, vec![1, 2, 3]);
1042  /// complex.join_element(face1);
1043  /// complex.join_element(face2);
1044  /// complex.join_element(face3);
1045  /// complex.join_element(face4);
1046  ///
1047  /// let h2 = complex.homology::<Boolean>(2);
1048  /// assert_eq!(h2.betti_number, 1); // One 2-dimensional hole (sphere interior)
1049  /// ```
1050  pub fn homology<F: Field + Copy>(&self, k: usize) -> Homology<F> {
1051    let k_elements = {
1052      let mut elements = self.elements_of_dimension(k);
1053      elements.sort_unstable();
1054      elements
1055    };
1056
1057    if k_elements.is_empty() {
1058      return Homology::trivial(k);
1059    }
1060
1061    let cycles = if k == 0 {
1062      // Z₀ = C₀ (kernel of ∂₀: C₀ -> C₋₁ is C₀ itself).
1063      let num_0_elements = k_elements.len();
1064      let mut basis: Vec<DVector<F>> = Vec::with_capacity(num_0_elements);
1065      for i in 0..num_0_elements {
1066        let mut v_data = vec![F::zero(); num_0_elements];
1067        v_data[i] = F::one();
1068        basis.push(DVector::from_row_slice(&v_data));
1069      }
1070      basis
1071    } else {
1072      let boundary_k = self.get_boundary_matrix::<F>(k);
1073      kernel(&boundary_k)
1074    };
1075
1076    let boundary_k_plus_1 = self.get_boundary_matrix::<F>(k + 1);
1077    let boundaries = image(&boundary_k_plus_1);
1078
1079    let quotient_basis_vectors = compute_quotient_basis(&boundaries, &cycles);
1080
1081    Homology {
1082      dimension:           k,
1083      betti_number:        quotient_basis_vectors.len(),
1084      homology_generators: quotient_basis_vectors,
1085    }
1086  }
1087
1088  /// Constructs the boundary matrix ∂ₖ: Cₖ → Cₖ₋₁ for the k-th boundary operator.
1089  ///
1090  /// The boundary matrix is the matrix representation of the linear map that takes
1091  /// k-dimensional chains to their (k-1)-dimensional boundaries. This is the
1092  /// fundamental building block for homology computations.
1093  ///
1094  /// # Mathematical Definition
1095  ///
1096  /// For a k-dimensional element σ, its boundary ∂σ is a formal sum of (k-1)-dimensional
1097  /// faces with appropriate orientation coefficients:
1098  ///
1099  /// ```text
1100  /// ∂ₖ(σ) = Σᵢ aᵢ τᵢ
1101  /// ```
1102  ///
1103  /// where τᵢ are the faces of σ and aᵢ ∈ F are the orientation coefficients.
1104  ///
1105  /// # Matrix Structure
1106  ///
1107  /// The resulting matrix has:
1108  /// - **Rows**: Indexed by (k-1)-dimensional elements (codomain basis)
1109  /// - **Columns**: Indexed by k-dimensional elements (domain basis)
1110  /// - **Entry (i,j)**: Coefficient of codomain element i in ∂(domain element j)
1111  ///
1112  /// # Element Ordering
1113  ///
1114  /// Both domain and codomain elements are sorted using their natural ordering
1115  /// (from [`Ord`] implementation). This ensures:
1116  /// - Deterministic matrix construction
1117  /// - Consistent results across runs
1118  /// - Predictable basis element correspondence
1119  ///
1120  /// # Boundary Operator Properties
1121  ///
1122  /// The matrix satisfies the fundamental property **∂² = 0**, meaning that
1123  /// `boundary_matrix(k+1) * boundary_matrix(k) = 0`. This is essential for
1124  /// the chain complex structure and homology computations.
1125  ///
1126  /// # Special Cases
1127  ///
1128  /// - **k = 0**: Returns empty matrix (0-dimensional elements have no boundary)
1129  /// - **No k-elements**: Returns matrix with correct row count but no columns
1130  /// - **No (k-1)-elements**: Returns matrix with correct column count but no rows
1131  ///
1132  /// # Implementation Details
1133  ///
1134  /// This method uses the [`ComplexElement::boundary_with_orientations`] method
1135  /// to get the oriented boundary of each element, then constructs the matrix
1136  /// by mapping faces to their positions in the codomain basis.
1137  ///
1138  /// # Performance
1139  ///
1140  /// - **Time**: O(nf) where n is the number of k-elements and f is the average number of faces
1141  /// - **Space**: O(nm) where m is the number of (k-1)-elements
1142  /// - **Optimized**: Only computes non-zero entries
1143  ///
1144  /// # Examples
1145  ///
1146  /// ## Triangle Boundary Matrix
1147  ///
1148  /// ```rust
1149  /// use cova_algebra::algebras::boolean::Boolean;
1150  /// use cova_space::complexes::{Complex, Simplex};
1151  ///
1152  /// let mut complex = Complex::new();
1153  /// let triangle = Simplex::new(2, vec![0, 1, 2]);
1154  /// complex.join_element(triangle);
1155  ///
1156  /// // Get boundary matrix ∂₂: C₂ → C₁ (triangles → edges)
1157  /// let boundary_2 = complex.get_boundary_matrix::<Boolean>(2);
1158  ///
1159  /// // Should be 3×1 matrix (3 edges, 1 triangle)
1160  /// assert_eq!(boundary_2.nrows(), 3); // 3 edges
1161  /// assert_eq!(boundary_2.ncols(), 1); // 1 triangle
1162  /// ```
1163  ///
1164  /// ## Edge Boundary Matrix  
1165  ///
1166  /// ```rust
1167  /// # use cova_algebra::algebras::boolean::Boolean;
1168  /// # use cova_space::complexes::{Complex, Simplex};
1169  /// # let mut complex = Complex::new();
1170  /// let edge = Simplex::new(1, vec![0, 1]);
1171  /// complex.join_element(edge);
1172  ///
1173  /// // Get boundary matrix ∂₁: C₁ → C₀ (edges → vertices)
1174  /// let boundary_1 = complex.get_boundary_matrix::<Boolean>(1);
1175  ///
1176  /// // Should be 2×1 matrix (2 vertices, 1 edge)
1177  /// assert_eq!(boundary_1.nrows(), 2); // 2 vertices
1178  /// assert_eq!(boundary_1.ncols(), 1); // 1 edge
1179  /// ```
1180  pub fn get_boundary_matrix<F: Field + Copy>(&self, k: usize) -> DMatrix<F>
1181  where T: ComplexElement {
1182    let domain_basis = self.elements_of_dimension(k);
1183    let codomain_basis = self.elements_of_dimension(k.saturating_sub(1));
1184
1185    if domain_basis.is_empty() || codomain_basis.is_empty() {
1186      // Return appropriate empty matrix
1187      return DMatrix::<F>::zeros(codomain_basis.len(), domain_basis.len());
1188    }
1189
1190    let mut matrix = DMatrix::<F>::zeros(codomain_basis.len(), domain_basis.len());
1191
1192    // Create a map from elements to their position in the codomain basis
1193    let basis_map_for_codomain: HashMap<&T, usize> =
1194      codomain_basis.iter().enumerate().map(|(i, s)| (s, i)).collect();
1195    let num_codomain_elements = codomain_basis.len();
1196
1197    for (col_idx, element_from_domain) in domain_basis.iter().enumerate() {
1198      // Compute boundary using the Topology trait implementation
1199      let boundary_chain: Chain<Self, F> = self.boundary(element_from_domain);
1200
1201      // Convert the chain to a coefficient vector representing this column.
1202      let col_vector =
1203        boundary_chain.to_coeff_vector(&basis_map_for_codomain, num_codomain_elements);
1204
1205      // Fill the `col_idx`-th column of the boundary matrix.
1206      matrix.set_column(col_idx, &col_vector);
1207    }
1208
1209    matrix
1210  }
1211}
1212
1213impl<T: ComplexElement> Default for Complex<T> {
1214  fn default() -> Self { Self::new() }
1215}
1216
1217// =============================================================================
1218// TRAIT IMPLEMENTATIONS
1219// =============================================================================
1220//
1221// The following implementations make Complex<T> integrate seamlessly with the
1222// broader algebraic and topological framework:
1223//
1224// - Collection: Basic containment and emptiness queries
1225// - Poset: Face relation partial order operations (≤, upset, downset, etc.)
1226// - Topology: Neighborhood and boundary operations for topological computations
1227//
1228// These implementations enable Complex<T> to work with:
1229// - Generic algorithms that operate on posets or topological spaces
1230// - Homology computations via the Topology trait boundary operator
1231// - Sheaf computations that require poset structure
1232// - General lattice-theoretic operations
1233
1234/// Implementation of [`Collection`] for complexes.
1235///
1236/// Provides basic set-theoretic operations for checking element membership
1237/// and complex emptiness. Note that containment is based on ID equality,
1238/// so elements must have been added to the complex to be considered contained.
1239impl<T: ComplexElement> Collection for Complex<T> {
1240  type Item = T;
1241
1242  fn contains(&self, point: &Self::Item) -> bool {
1243    if let Some(id) = point.id() { self.elements.contains_key(&id) } else { false }
1244  }
1245
1246  fn is_empty(&self) -> bool { self.elements.is_empty() }
1247}
1248
1249/// Implementation of [`Poset`] for complexes.
1250///
1251/// Defines the face relation as the partial order: σ ≤ τ means "σ is a face of τ".
1252/// This implementation delegates to the attachment lattice for efficiency while
1253/// providing the full element objects in the interface.
1254///
1255/// The face relation satisfies all poset axioms:
1256/// - **Reflexivity**: σ ≤ σ (every element is a face of itself)
1257/// - **Antisymmetry**: σ ≤ τ and τ ≤ σ implies σ = τ
1258/// - **Transitivity**: σ ≤ τ and τ ≤ ρ implies σ ≤ ρ
1259impl<T: ComplexElement> Poset for Complex<T> {
1260  fn leq(&self, a: &Self::Item, b: &Self::Item) -> Option<bool> {
1261    match (a.id(), b.id()) {
1262      (Some(id_a), Some(id_b)) => self.attachment_lattice.leq(&id_a, &id_b),
1263      _ => None,
1264    }
1265  }
1266
1267  fn upset(&self, a: Self::Item) -> std::collections::HashSet<Self::Item> {
1268    if let Some(id) = a.id() {
1269      self
1270        .attachment_lattice
1271        .upset(id)
1272        .into_iter()
1273        .filter_map(|upset_id| self.get_element(upset_id).cloned())
1274        .collect()
1275    } else {
1276      std::collections::HashSet::new()
1277    }
1278  }
1279
1280  fn downset(&self, a: Self::Item) -> std::collections::HashSet<Self::Item> {
1281    if let Some(id) = a.id() {
1282      self
1283        .attachment_lattice
1284        .downset(id)
1285        .into_iter()
1286        .filter_map(|downset_id| self.get_element(downset_id).cloned())
1287        .collect()
1288    } else {
1289      std::collections::HashSet::new()
1290    }
1291  }
1292
1293  fn minimal_elements(&self) -> std::collections::HashSet<Self::Item> {
1294    self
1295      .attachment_lattice
1296      .minimal_elements()
1297      .into_iter()
1298      .filter_map(|id| self.get_element(id).cloned())
1299      .collect()
1300  }
1301
1302  fn maximal_elements(&self) -> std::collections::HashSet<Self::Item> {
1303    self
1304      .attachment_lattice
1305      .maximal_elements()
1306      .into_iter()
1307      .filter_map(|id| self.get_element(id).cloned())
1308      .collect()
1309  }
1310
1311  fn join(&self, a: Self::Item, b: Self::Item) -> Option<Self::Item> {
1312    match (a.id(), b.id()) {
1313      (Some(id_a), Some(id_b)) => self
1314        .attachment_lattice
1315        .join(id_a, id_b)
1316        .and_then(|join_id| self.get_element(join_id).cloned()),
1317      _ => None,
1318    }
1319  }
1320
1321  fn meet(&self, a: Self::Item, b: Self::Item) -> Option<Self::Item> {
1322    match (a.id(), b.id()) {
1323      (Some(id_a), Some(id_b)) => self
1324        .attachment_lattice
1325        .meet(id_a, id_b)
1326        .and_then(|meet_id| self.get_element(meet_id).cloned()),
1327      _ => None,
1328    }
1329  }
1330
1331  fn successors(&self, a: Self::Item) -> std::collections::HashSet<Self::Item> {
1332    if let Some(id) = a.id() {
1333      self
1334        .attachment_lattice
1335        .successors(id)
1336        .into_iter()
1337        .filter_map(|succ_id| self.get_element(succ_id).cloned())
1338        .collect()
1339    } else {
1340      std::collections::HashSet::new()
1341    }
1342  }
1343
1344  fn predecessors(&self, a: Self::Item) -> std::collections::HashSet<Self::Item> {
1345    if let Some(id) = a.id() {
1346      self
1347        .attachment_lattice
1348        .predecessors(id)
1349        .into_iter()
1350        .filter_map(|pred_id| self.get_element(pred_id).cloned())
1351        .collect()
1352    } else {
1353      std::collections::HashSet::new()
1354    }
1355  }
1356}
1357
1358/// Implementation of [`Topology`] for complexes.
1359///
1360/// Provides topological operations that integrate with the broader framework:
1361/// - **Neighborhood**: Returns cofaces (elements containing the given element)
1362/// - **Boundary**: Computes oriented boundary using element-specific operators
1363///
1364/// The boundary implementation is crucial for homology computations as it provides
1365/// the chain complex structure with proper orientation handling.
1366impl<T: ComplexElement> Topology for Complex<T> {
1367  fn neighborhood(&self, item: &Self::Item) -> Vec<Self::Item> {
1368    // Return direct cofaces (elements that have this item as a face)
1369    self.cofaces(item)
1370  }
1371
1372  fn boundary<R: Ring + Copy>(&'_ self, item: &Self::Item) -> Chain<'_, Self, R> {
1373    if item.dimension() == 0 {
1374      return Chain::new(self);
1375    }
1376
1377    let mut boundary_chain_items = Vec::new();
1378    let mut boundary_chain_coeffs = Vec::new();
1379
1380    // Use the element-specific boundary computation with orientations
1381    let faces_with_orientations = item.boundary_with_orientations();
1382
1383    for (face, orientation) in faces_with_orientations {
1384      // Find the corresponding element in the complex that matches this face's content
1385      if let Some(complex_face) = self.find_equivalent_element(&face) {
1386        // Use the orientation coefficient from the element-specific boundary operator
1387        let coeff = if orientation > 0 {
1388          R::one()
1389        } else if orientation < 0 {
1390          -R::one()
1391        } else {
1392          continue; // Skip faces with zero coefficient
1393        };
1394        boundary_chain_items.push(complex_face);
1395        boundary_chain_coeffs.push(coeff);
1396      }
1397    }
1398
1399    Chain::from_items_and_coeffs(self, boundary_chain_items, boundary_chain_coeffs)
1400  }
1401}
1402
1403#[cfg(test)]
1404mod tests {
1405  use cova_algebra::algebras::boolean::Boolean;
1406
1407  use super::*;
1408
1409  // =============================================================================
1410  // COMPREHENSIVE TEST SUITE
1411  // =============================================================================
1412  //
1413  // This test suite demonstrates and validates the key features of the complexes
1414  // module across multiple dimensions:
1415  //
1416  // 1. **Generic Complex Operations**: Tests that work with both simplicial and cubical complexes,
1417  //    showing the power of the generic Complex<T> design
1418  //
1419  // 2. **Closure Property**: Verifies that adding elements automatically includes all faces,
1420  //    maintaining the fundamental complex property
1421  //
1422  // 3. **ID Management**: Tests automatic ID assignment, deduplication, and proper handling of
1423  //    elements with/without IDs
1424  //
1425  // 4. **Poset Structure**: Validates that face relations form a proper partial order with correct
1426  //    upset/downset computations
1427  //
1428  // 5. **Topology Integration**: Tests neighborhood operations and boundary computations that
1429  //    integrate with the topology framework
1430  //
1431  // 6. **Homology Computations**: Comprehensive tests of homology computation for standard
1432  //    topological spaces (circles, spheres, etc.) over different coefficient fields
1433  //
1434  // 7. **Cross-Complex Compatibility**: Demonstrates that the same algorithms work on both
1435  //    simplicial and cubical complexes, producing mathematically consistent results
1436  //
1437  // These tests serve both as validation and as examples of proper usage patterns.
1438
1439  #[test]
1440  fn test_generic_complex_with_simplex() {
1441    let mut complex: Complex<Simplex> = Complex::new();
1442
1443    // Create a triangle (2-simplex)
1444    let triangle = Simplex::new(2, vec![0, 1, 2]);
1445    let added_triangle = complex.join_element(triangle);
1446
1447    // Should have 1 triangle, 3 edges, 3 vertices
1448    assert_eq!(complex.elements_of_dimension(2).len(), 1);
1449    assert_eq!(complex.elements_of_dimension(1).len(), 3);
1450    assert_eq!(complex.elements_of_dimension(0).len(), 3);
1451
1452    // Check that the triangle is in the complex (use the returned element with ID)
1453    assert!(complex.contains(&added_triangle));
1454
1455    // Check lattice relationships
1456    let faces = complex.faces(&added_triangle);
1457    assert_eq!(faces.len(), 3); // Triangle should have 3 edges as direct faces
1458  }
1459
1460  #[test]
1461  fn test_generic_complex_with_cube() {
1462    let mut complex: Complex<Cube> = Complex::new();
1463
1464    // Create a square (2-cube)
1465    let square = Cube::square([0, 1, 2, 3]);
1466    let added_square = complex.join_element(square);
1467
1468    // Should have 1 square, 4 edges, 4 vertices
1469    assert_eq!(complex.elements_of_dimension(2).len(), 1);
1470    assert_eq!(complex.elements_of_dimension(1).len(), 4);
1471    assert_eq!(complex.elements_of_dimension(0).len(), 4);
1472
1473    // Check that the square is in the complex (use the returned element with ID)
1474    assert!(complex.contains(&added_square));
1475
1476    // Check lattice relationships
1477    let faces = complex.faces(&added_square);
1478    assert_eq!(faces.len(), 4); // Square should have 4 edges as direct faces
1479  }
1480
1481  #[test]
1482  fn test_automatic_id_assignment() {
1483    let mut complex = SimplicialComplex::new();
1484
1485    // Create elements without IDs
1486    let vertex1 = Simplex::new(0, vec![0]);
1487    let vertex2 = Simplex::new(0, vec![1]);
1488    let edge = Simplex::new(1, vec![0, 1]);
1489
1490    // Add them to the complex - should get automatic ID assignment
1491    let added_vertex1 = complex.join_element(vertex1);
1492    let added_vertex2 = complex.join_element(vertex2);
1493    let added_edge = complex.join_element(edge);
1494
1495    // All should be contained
1496    assert!(complex.contains(&added_vertex1));
1497    assert!(complex.contains(&added_vertex2));
1498    assert!(complex.contains(&added_edge));
1499
1500    // Should have assigned IDs
1501    assert!(added_vertex1.id().is_some());
1502    assert!(added_vertex2.id().is_some());
1503    assert!(added_edge.id().is_some());
1504
1505    // Should have proper lattice relationships
1506    assert!(complex.leq(&added_vertex1, &added_edge).unwrap());
1507    assert!(complex.leq(&added_vertex2, &added_edge).unwrap());
1508  }
1509
1510  #[test]
1511  fn test_element_deduplication() {
1512    let mut complex = SimplicialComplex::new();
1513
1514    // Create two mathematically identical simplices
1515    let simplex1 = Simplex::new(1, vec![0, 1]);
1516    let simplex2 = Simplex::new(1, vec![0, 1]); // Same content
1517
1518    let added1 = complex.join_element(simplex1);
1519    let added2 = complex.join_element(simplex2);
1520
1521    // Should return the same element (by ID)
1522    assert_eq!(added1.id(), added2.id());
1523    assert_eq!(complex.elements_of_dimension(1).len(), 1); // Only one edge stored
1524  }
1525
1526  // =====================================================
1527  // GENERIC POSET OPERATIONS TESTS
1528  // =====================================================
1529
1530  #[test]
1531  fn test_complex_poset_operations() {
1532    let mut complex = SimplicialComplex::new();
1533
1534    let triangle = Simplex::new(2, vec![0, 1, 2]);
1535    let added_triangle = complex.join_element(triangle);
1536
1537    // Get the actual elements with IDs from the complex
1538    let vertices = complex.elements_of_dimension(0);
1539    let edges = complex.elements_of_dimension(1);
1540
1541    // Find specific vertex and edge by content
1542    let vertex = vertices.iter().find(|v| v.vertices() == [0]).unwrap().clone();
1543    let edge = edges.iter().find(|e| e.vertices() == [0, 1]).unwrap().clone();
1544
1545    // Test leq relationships
1546    assert_eq!(complex.leq(&vertex, &edge), Some(true));
1547    assert_eq!(complex.leq(&edge, &added_triangle), Some(true));
1548    assert_eq!(complex.leq(&vertex, &added_triangle), Some(true));
1549    assert_eq!(complex.leq(&added_triangle, &vertex), Some(false));
1550
1551    // Test upset/downset
1552    let vertex_upset = complex.upset(vertex.clone());
1553    assert!(vertex_upset.contains(&vertex));
1554    assert!(vertex_upset.contains(&edge));
1555    assert!(vertex_upset.contains(&added_triangle));
1556
1557    let triangle_downset = complex.downset(added_triangle.clone());
1558    assert!(triangle_downset.contains(&vertex));
1559    assert!(triangle_downset.contains(&edge));
1560    assert!(triangle_downset.contains(&added_triangle));
1561  }
1562
1563  #[test]
1564  fn test_complex_topology_operations() {
1565    let mut complex = SimplicialComplex::new();
1566
1567    let edge = Simplex::new(1, vec![0, 1]);
1568    let added_edge = complex.join_element(edge);
1569
1570    // Get the actual vertex with ID from the complex
1571    let vertices = complex.elements_of_dimension(0);
1572    let vertex = vertices.iter().find(|v| v.vertices() == [0]).unwrap().clone();
1573
1574    // Test neighborhood (cofaces)
1575    let vertex_neighborhood = complex.neighborhood(&vertex);
1576    assert_eq!(vertex_neighborhood.len(), 1);
1577    assert!(vertex_neighborhood.contains(&added_edge));
1578
1579    let edge_neighborhood = complex.neighborhood(&added_edge);
1580    assert_eq!(edge_neighborhood.len(), 0); // No 2-simplices attached
1581  }
1582
1583  #[test]
1584  fn test_mixed_complex_operations() {
1585    // Test that we can use the same generic operations on both simplicial and cubical complexes
1586
1587    // Simplicial complex
1588    let mut simplicial_complex = SimplicialComplex::new();
1589    let triangle = Simplex::from_vertices(vec![0, 1, 2]);
1590    let added_triangle = simplicial_complex.join_element(triangle);
1591
1592    // Should automatically add all faces
1593    assert_eq!(simplicial_complex.elements_of_dimension(2).len(), 1);
1594    assert_eq!(simplicial_complex.elements_of_dimension(1).len(), 3);
1595    assert_eq!(simplicial_complex.elements_of_dimension(0).len(), 3);
1596
1597    // Cubical complex
1598    let mut cubical_complex = CubicalComplex::new();
1599    let square = Cube::square([0, 1, 2, 3]);
1600    let added_square = cubical_complex.join_element(square);
1601
1602    // Should automatically add all faces
1603    assert_eq!(cubical_complex.elements_of_dimension(2).len(), 1);
1604    assert_eq!(cubical_complex.elements_of_dimension(1).len(), 4);
1605    assert_eq!(cubical_complex.elements_of_dimension(0).len(), 4);
1606
1607    // Both should support the same interface
1608    assert!(simplicial_complex.contains(&added_triangle));
1609    assert!(cubical_complex.contains(&added_square));
1610
1611    // Both should have proper lattice structure
1612    let triangle_faces = simplicial_complex.faces(&added_triangle);
1613    let square_faces = cubical_complex.faces(&added_square);
1614    assert_eq!(triangle_faces.len(), 3); // triangle has 3 edges
1615    assert_eq!(square_faces.len(), 4); // square has 4 edges
1616  }
1617
1618  #[test]
1619  fn test_generic_homology_computation() {
1620    // Test simplicial complex - triangle boundary (should have H_1 = 1)
1621    let mut simplicial_complex = SimplicialComplex::new();
1622
1623    // Create a triangle boundary (3 edges forming a cycle)
1624    let edge1 = Simplex::new(1, vec![0, 1]);
1625    let edge2 = Simplex::new(1, vec![1, 2]);
1626    let edge3 = Simplex::new(1, vec![2, 0]);
1627
1628    simplicial_complex.join_element(edge1);
1629    simplicial_complex.join_element(edge2);
1630    simplicial_complex.join_element(edge3);
1631
1632    // Compute homology over Z/2Z
1633    let h0 = simplicial_complex.homology::<Boolean>(0);
1634    let h1 = simplicial_complex.homology::<Boolean>(1);
1635
1636    assert_eq!(h0.betti_number, 1); // One connected component
1637    assert_eq!(h1.betti_number, 1); // One 1-dimensional hole
1638
1639    // Test cubical complex - square boundary (should also have H_1 = 1)
1640    let mut cubical_complex = CubicalComplex::new();
1641
1642    // Create a square boundary (4 edges forming a cycle)
1643    let edge1 = Cube::edge(0, 1);
1644    let edge2 = Cube::edge(1, 2);
1645    let edge3 = Cube::edge(2, 3);
1646    let edge4 = Cube::edge(3, 0);
1647
1648    cubical_complex.join_element(edge1);
1649    cubical_complex.join_element(edge2);
1650    cubical_complex.join_element(edge3);
1651    cubical_complex.join_element(edge4);
1652
1653    // Compute homology over Z/2Z
1654    let h0_cube = cubical_complex.homology::<Boolean>(0);
1655    let h1_cube = cubical_complex.homology::<Boolean>(1);
1656
1657    assert_eq!(h0_cube.betti_number, 1); // One connected component
1658    assert_eq!(h1_cube.betti_number, 1); // One 1-dimensional hole
1659
1660    // Both simplicial and cubical complexes should give same topological result
1661    assert_eq!(h0.betti_number, h0_cube.betti_number);
1662    assert_eq!(h1.betti_number, h1_cube.betti_number);
1663  }
1664
1665  #[test]
1666  fn test_generic_homology_filled_shapes() {
1667    // Test filled triangle (should have H_1 = 0)
1668    let mut simplicial_complex = SimplicialComplex::new();
1669    let triangle = Simplex::new(2, vec![0, 1, 2]);
1670    simplicial_complex.join_element(triangle);
1671
1672    let h0 = simplicial_complex.homology::<Boolean>(0);
1673    let h1 = simplicial_complex.homology::<Boolean>(1);
1674
1675    assert_eq!(h0.betti_number, 1); // One connected component
1676    assert_eq!(h1.betti_number, 0); // No 1D holes (filled)
1677
1678    // Test filled square (should also have H_1 = 0)
1679    let mut cubical_complex = CubicalComplex::new();
1680    let square = Cube::square([0, 1, 2, 3]);
1681    cubical_complex.join_element(square);
1682
1683    let h0_cube = cubical_complex.homology::<Boolean>(0);
1684    let h1_cube = cubical_complex.homology::<Boolean>(1);
1685
1686    assert_eq!(h0_cube.betti_number, 1); // One connected component
1687    assert_eq!(h1_cube.betti_number, 0); // No 1D holes (filled)
1688  }
1689}