diff --git a/.gitignore b/.gitignore index 06cfbf3d2f8..0622ef9f60b 100644 --- a/.gitignore +++ b/.gitignore @@ -1172,3 +1172,5 @@ patches_after_merge.ply CMakeUserPresets.json /.cache compile_commands.json +/HDVF/test/HDVF/tmp +/HDVF/examples/HDVF/tmp diff --git a/Algebraic_foundations/doc/Algebraic_foundations/CGAL/number_utils.h b/Algebraic_foundations/doc/Algebraic_foundations/CGAL/number_utils.h index 74d7c452e99..cf0b3c55bec 100644 --- a/Algebraic_foundations/doc/Algebraic_foundations/CGAL/number_utils.h +++ b/Algebraic_foundations/doc/Algebraic_foundations/CGAL/number_utils.h @@ -22,7 +22,7 @@ namespace CGAL { \ingroup PkgAlgebraicFoundationsRef The template function `compare()` compares the first argument with respect to -the second, i.e.\ it returns `CGAL::LARGER` if \f$ x\f$ is larger than \f$ y\f$. +the second. In case the argument types `NT1` and `NT2` differ, `compare` is performed with the semantic of the type determined via diff --git a/Documentation/doc/Documentation/packages.txt b/Documentation/doc/Documentation/packages.txt index da8d09c1f0d..351cceda8b7 100644 --- a/Documentation/doc/Documentation/packages.txt +++ b/Documentation/doc/Documentation/packages.txt @@ -131,7 +131,11 @@ \package_listing{Stream_lines_2} \package_listing{Classification} \package_listing{Heat_method_3} + +\cgalPackageSection{PartTopology,Topology} + \package_listing{Surface_mesh_topology} +\package_listing{HDVF} \cgalPackageSection{PartSearchStructures,Spatial Searching and Sorting} diff --git a/HDVF/doc/HDVF/Concepts/AbstractChainComplex.h b/HDVF/doc/HDVF/Concepts/AbstractChainComplex.h new file mode 100644 index 00000000000..b30064af4cf --- /dev/null +++ b/HDVF/doc/HDVF/Concepts/AbstractChainComplex.h @@ -0,0 +1,136 @@ +/*! +\ingroup PkgHDVFConcepts +\cgalConcept + +The concept `AbstractChainComplex` describes the requirements for (topological) chain complexes associated to abstract complexes used in the concept `HDVF`. + +It provides methods to: + + - get the dimension of the complex, the number of cells in each dimension + - get the boundary and co-boundary of cell(s) + - get the vertices of a given cell + - output the complex in text format + + Cells are indexed along each dimension and thus identified by their index together with their dimension. + + +\cgalHasModelsBegin +\cgalHasModelsBare{`CGAL::Homological_discrete_vector_field::Abstract_simplicial_chain_complex`} +\cgalHasModelsBare{`CGAL::Homological_discrete_vector_field::Sub_chain_complex_mask`} +\cgalHasModelsEnd + +*/ + +class AbstractChainComplex +{ +public: +/// \name Types +/// @{ + +/*! +Type of coefficients ring used to compute homology, model of `IntegralDomainWithoutDivision` + */ +typedef CoefficientRing Coefficient_ring; + +/*! + Type of column-major chains (returned by the boundary operator) + */ +typedef CGAL::OSM::Sparse_chain Column_chain; + +/*! + Type of row-major chains (returned by the co-boundary operator) + */ +typedef CGAL::OSM::Sparse_chain Row_chain ; + +/*! + Type of column-major sparse matrices (used to store the boundary operator) + */ +typedef CGAL::OSM::Sparse_matrix Column_matrix; + +/// @} + +/// \name Operators +/// @{ + +/*! +Assignment operator. + +The operator creates a copy of `complex`. + */ + AbstractChainComplex& operator= (const AbstractChainComplex& complex); + +/// @} + +/// \name Access functions +/// @{ + +/*! +Returns the dimension of the complex, that is, the largest dimension of cells. + */ +int dimension(); + +/*! +Returns the number of cells of dimension `q`. +If `q` is negative of larger than the dimension of the complex, returns 0. + */ +size_t number_of_cells(int q); + +/*! +\brief Returns all boundary matrices. + +The function returns constant reference to a vector of column-major sparse matrices. The `q`-th element of this vector is the matrix of \f$\partial_q\f$, which gives the boundary of cells of dimension `q`(as a linear combination of `q`-1 cells). + + */ +const vector & boundary_matrices() const; + +/*! + \brief Returns the boundary matrix of dimension `q` (ie.\ the matrix of \f$\partial_q\f$). + +The function returns a column-major sparse matrices. + */ +const Column_matrix & boundary_matrix(int q) const; + +/*! + \brief Returns the boundary of the cell of index `id_cell` in dimension `q`. + +This boundary is a finite linear combination of cells of dimension `q`-1. It is encoded as a column-major chain (which maps each cell with a non-zero coefficient to this coefficient). This boundary is thus the `id_cell`-th column of the boundary matrix in dimension `q`. + +*/ +Column_chain d(size_t id_cell, int q); + + +/*! + \brief Returns the co-boundary of the cell of index `id_cell` in dimension `q`. + +This boundary is a finite linear combination of cells of dimension `q`+1. It is encoded as a row-major chain (which maps each cell with a non-zero coefficient to this coefficient). This co-boundary is thus the `id_cell`-th row of the boundary matrix in dimension `q`+1. +*/ +Row_chain cod(size_t id_cell, int q); + +/*! + \brief Returns the vertices of a given cell (that is, the indices of its faces of dimension 0). + +*/ +std::vector bottom_faces(size_t id_cell, int q) const; + +/*! + * \brief Returns the cofaces of a given chain in dimension `q`. + * + * The resulting chain, whatever the storage format of the input, is column-major, lies in dimension `q`+1 and is null if this dimension exceeds the dimension of the complex. + * + * \tparam CoefficientR `CoefficientRing` of the chain. + * \tparam StorageF `StorageFormat` of the chain. +*/ +template +Column_chain cofaces_chain (SparseChain chain, int q) const; + +/// @} + +}; + + +/*! +\relates AbstractChainComplex +Inserts the chain complex in text mode in the stream. +*/ +std::ostream& operator<<(std::ostream& out, const AbstractChainComplex& complex) const; + diff --git a/HDVF/doc/HDVF/Concepts/Filtration.h b/HDVF/doc/HDVF/Concepts/Filtration.h new file mode 100644 index 00000000000..953c36a7f02 --- /dev/null +++ b/HDVF/doc/HDVF/Concepts/Filtration.h @@ -0,0 +1,181 @@ +/*! +\ingroup PkgHDVFConcepts +\cgalConcept + +The concept `Filtration` describes the requirements for persistent filtrations associated to persistent homology computation. + +A filtration is associated to an `AbstractChainComplex`. Each cell is equiped with a scalar value (called its degree) and the filtration is an enumeration of cells in any dimension by increasing degrees. + +A filtration class provides: + + - an iterator to visit all cells by increasing degrees. + - getters to get the degree of a cell, and the cell of a given index along the filtration. + - an overload of `operator<<()` to output filtrations. + +Cells are indexed along each dimension and thus identified by their index together with their dimension. + +\cgalHasModelsBegin +\cgalHasModelsBare{`CGAL::Homological_discrete_vector_field::Filtration_core`} +\cgalHasModelsBare{`CGAL::Homological_discrete_vector_field::Filtration_lower_star`} +\cgalHasModelsEnd + +*/ + +class Filtration +{ +public: + /*! \brief Type of the underlying complex. */ + typedef unspecified_type Complex; + + /*! \brief (Scalar) type of degrees (model of `RealEmbeddable`). + */ + typedef unspecified_type Degree ; + + /*! \brief Type of coefficients used to compute homology. */ + typedef Complex::Coefficient_ring Coefficient_ring; + + /*! \brief Type for indexing uniquely a cell. + * + * As stated in `AbstractChainComplex`, cells are identified by their dimension, together with their index along this dimension. The type `Cell` stores this pair: + * + * - First element of the pair: index of the cell. + * - Second element of the pair: dimension of the cell. + */ + typedef std::pair Cell ; + + /*! \brief Value type of the filtration iterator. + * Contains a cell (identified by its index and dimension in a `Cell`) and its associated degree. + */ + typedef struct { + Cell cell_dim ; + Degree degree ; + } Filtration_iter_value ; + +protected: + /*! + Type of column-major sparse matrices. + */ + typedef SparseMatrix Column_matrix ; + + /*! + Type of row-major sparse matrices. + */ + typedef SparseMatrix Row_matrix ; + + /*! + Type of column-major chains. + */ + typedef SparseChain Column_chain ; + + /*! + Type of row-major chains. + */ + typedef SparseChain Row_chain ; + +public: + /** + * \brief The iterator over filtration. + * + * Iterate the filtration by increasing degrees. + */ + struct iterator + { + // Iterator tags + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = Filtration_iter_value; + + /*! Iterator constructor + */ + iterator(const Filtration& f, size_t i=0); + + /*! Dereference operator. + */ + value_type operator*() const; + + /*! Gets the index (time) associated to the iterator. + */ + size_t time () const ; + + /*! Gets the `Cell` (cell and its dimension) associated to the iterator. + */ + Cell cell_dim () const ; + + /*! Gets the degree associated to the iterator. + */ + Degree degree () const ; + + /*! + * \brief Prefix incrementation. + * Move to next index in the filtration. + */ + iterator& operator++(); + + /*! \brief Postfix incrementation. + */ + iterator operator++(int); + + /*! + * \brief Equality check. + * \returns True if the indices are equal. + */ + friend bool operator== (const iterator& a, const iterator& b); + + /*! + * \brief Inequality check. + * \returns True if the indices are different. + */ + friend bool operator!= (const iterator& a, const iterator& b); + }; + + /*! + * \brief Iterator to the beginning of the filtration. + */ + iterator begin(); + + /*! + * \brief Returns a past-the-end iterator. + */ + iterator end(); + + // getters + /*! \brief Gets the filtration size. + */ + size_t size () const; + + /*! \brief Gets the cell (that is cell index and dimension) at the index `i` of the filtration. + */ + Cell cell_index_dimension (size_t i) const; + + /*! \brief Gets the degree of the `i`th element of the filtration. + */ + Degree degree (size_t i) const; + + // Filtration verification + /*! \brief Checks that a filtration is valid. + * Checks that cells are ordered in increasing degrees and all cells have indices larger than their faces. + */ + bool is_valid() const; + + // Input/output filtration + + + /** + * \brief Exports the filtration time indices. + * + * The method exports the time index of every cell in each dimension. + */ + vector > export_filtration () const +}; + + /*! + \relates Filtration + \brief extracts a filtration from a stream. + */ + istream & operator>>(istream & in, Filtration &f); + + /*! + \relates Filtration + \brief Inserts a filtration into a stream. + */ + ostream & operator<<(ostream & out, const Filtration &f); \ No newline at end of file diff --git a/HDVF/doc/HDVF/Concepts/GeometricChainComplex.h b/HDVF/doc/HDVF/Concepts/GeometricChainComplex.h new file mode 100644 index 00000000000..4e8d7ee80f6 --- /dev/null +++ b/HDVF/doc/HDVF/Concepts/GeometricChainComplex.h @@ -0,0 +1,45 @@ +/*! +\ingroup PkgHDVFConcepts +\cgalConcept + +The concept `GeometricChainComplex` refines the concept `AbstractChainComplex` and describes the requirements for (topological) chain complexes associated to geometric complexes used in the concept `HDVF`. + It adds to `AbstractChainComplex` methods to get vertex coordinates. + + \cgalRefines{AbstractChainComplex} + +\cgalHasModelsBegin +\cgalHasModelsBare{`CGAL::Homological_discrete_vector_field::Simplicial_chain_complex`} +\cgalHasModelsBare{`CGAL::Homological_discrete_vector_field::Cubical_chain_complex`} +\cgalHasModelsEnd + +*/ + +class GeometricChainComplex +{ +public: +/// \name Types +/// @{ + +/*! + Type of coordinates (the vector size is free, hence coordinates can be any dimension). + */ +typedef unspecified_type Point ; +/// @} + + +/// \name Access functions +/// @{ + +/*! +Returns the vector of vertex coordinates. + */ +const std::vector& points() const; + +/*! + Returns the coordinates of the cell of index `i` and dimension 0. + */ +Point point(size_t i) const; + +/// @} + +}; diff --git a/HDVF/doc/HDVF/Concepts/HDVF.h b/HDVF/doc/HDVF/Concepts/HDVF.h new file mode 100644 index 00000000000..d3f4292a703 --- /dev/null +++ b/HDVF/doc/HDVF/Concepts/HDVF.h @@ -0,0 +1,414 @@ +/*! +\ingroup PkgHDVFConcepts +\cgalConcept + +The concept `HDVF` describes the requirements for Homological Discrete Vector Fields (%HDVF for short) , a theory of computational homology unifying discrete Morse theory and effective homology. HDVFs were introduced by Aldo Gonzalez-Lorenzo in his PhD (see [AGL,2017], [AGL,2016]). + +A HDVF is a combinatorial tool computing the homology of a chain complex (described by the `AbstractChainComplex` concept) including "geometric" information such as homology and cohomology generators. + + As described in the user manual description, an HDVF is: + - a partition of the cells of the underlying complex into three classes: primary (P), secondary (S) and critical (C), together with partial invertibility conditions for the boundary operator, + - an associated reduction \f$(f,g,h)\f$ from the chain complex onto a chain complex generated by critical cells and a reduced boundary. + A HDVF is perfect when the reduced boundary is null; in this case, the maps \f$f\f$ and \f$g\f$ (and their duals) provide homology and cohomology information (generators, annotation...). + + More precisely, a HDVF over a chain complex C, derived from a complex K, is a partition of the cells: \f$K = P\coprod S\coprod C\f$ with \f$P\f$ primary cells, \f$S\f$ secondary cells and \f$C\f$ critical cells, such that the sub-matrix: + \f\[\partial (S)|_P\text{ is invertible.} + \f\] + In what follows, given \f$q\in \mathbb N\f$ and \f$G\f$ a sub-group of the qth chain group \f$C_q\f$, we denote by: + + - \f$i_G\f$ the inclusion \f$G\to C_q\f$ + - \f$j_G\f$ the projection \f$C_q\to G\f$ + +That is, for all \f$q\in \mathbb N\f$, if + + \f\[\mathrm{Mat}(\partial_q) = \begin{array}{lc} + & P_q \ \ \ \ S_q \ \ \ \ C_q \\ + \begin{array}{l} + P_{q-1} \\ + S_{q-1} \\ + C_{q-1} \\ + \end{array} & \left(\begin{array}{ccc} + \star & D_{PS} & \star \\ +\star & \star & \star \\ +\star & \star & \star \\ + \end{array}\right)\\ + \end{array} + \f\] + then \f$D_{PS}\f$, the matrix of \f$j_P\circ \partial\circ i_S\f$, also written \f$\partial (S)|_P\f$ is invertible. + + In order to build a perfect HDVF, a pairing operation called `A()` associates (under conditions) two critical cells (becoming PRIMARY and SECONDARY) and updates the reduction in time \f$\mathscr O(n^2)\f$. Intuitively, this operation bears some similitudes (see users guide) with discrete Morse theory's DGVF arrows (and an HDVF can always be represented by such a vector field - possibly with cycles). + Given a HDVF \f$X = (P,S,C)\f$, A operation between two cells \f$(\gamma_1, \gamma_2)\f$ of respective dimension \f$q/q+1\f$ is valid if \f$(P',S',C') = (P\cup\{\gamma_1\}, P\cup\{\gamma_2\}, C\backslash\{\gamma_1,\gamma_2\})\f$ is a HDVF (ie.\ if \f$j_{P'}\circ \partial\circ i_{S'}\f$ is still invertible). This is actually the case if and only if \f$\langle d(\gamma_2), \gamma_1 \rangle\f$ (with \f$d\f$ the reduced boundary operator) is invertible. This condition is called the *validity condition for A*; all the `find_pair_A()` and `find_pairs_A()` methods search for such valid pairs. + + + Starting from this operation, `HDVF` provides two methods to generate perfect HDVFs (and hence compute homology): + - `compute_perfect_hdvf()` which computes a perfect HDVF by chosing at each step the first A-pairings available (depending on cells ordering in the chain complex) + - `compute_rand_perfect_hdvf()` which computes a perfect random HDVF by chosing at each step a rand A-pairing among all possible ones (this option is thus slower) + + For efficiency, the HDVF reduction can be computed partially: + - compute only the reduced boundary (option `OPT_BND`): to get Betti numbers + - compute \f$\partial'\f$ together with \f$h\f$ and \f$g\f$ (option `OPT_G`): to get homology generators + - compute \f$\partial'\f$ together with \f$h\f$ and \f$f\f$ (option `OPT_F`): to get co-homology generators + - compute the full reduction (option `OPT_FULL`): to get the full homological / cohomological information + + Let us consider a simple cubical complex example (see \ref secHDVFcubical_chain_complex for an introduction). In what follows, PRIMARY cells are coloured green, SECONDARY are coloured red and critical blue. The picture below shows an initial empty HDVF where all cells are critical: + + + + The boundary matrix of \f$\partial_2\f$ (which equals the reduced boundary matrix as the HDVF is empty) for \f$\mathbb Z\f$-homology is (cells are identified by their Khalimsky coordinates): + \f\[\mathrm{Mat}(\partial_2) = \begin{array}{lc} + & (3,1)\\ + \begin{array}{l} + (3,0) \\ + (2,1) \\ + (4,1) \\ + (3,2) \\ + \end{array} & \left(\begin{array}{c} + 1 \\ + -1 \\ + 1 \\ + -1 \\ + \end{array}\right)\\ + \end{array} + \f\] + Let us consider \f$\gamma_2 = (3,1)\f$ and \f$\gamma_1 = (3,2)\f$, the condition for A is valid (-1 is invertible). The following images illustrates this pairing in discrete Morse theory "style" (pink arrow in left complex) and the resulting HDVF (right complex). + + + + + The reduced boundary matrix in dimension 2 becomes empty and the matrix of \f$d_1\f$ is similar to the boundary matrix \f$\partial_1\f$ with the row/colums associated to previous cells removed. + + Thus, if we now consider \f$\gamma_2 = (4,1)\f$ and \f$\gamma_1 = (4,0)\f$, the condition for A is valid; cells can be paired and we obtain the following HDVF: + + + + + The process can be continued step by step: + + + + + Until we eventually get a perfect HDVF: + + + + Each hole is identified by a critical cell and the \f$g\f$ map of the reduction provides associated homology generators (highlighted in blue): + + + + + + A corresponding cohomology generators (highlighted in pink): + + + + + + +\cgalHasModelsBegin +\cgalHasModelsBare{`CGAL::Homological_discrete_vector_field::Hdvf_core`} +\cgalHasModelsBare{`CGAL::Homological_discrete_vector_field::Hdvf`} +\cgalHasModelsBare{`CGAL::Homological_discrete_vector_field::Hdvf_persistence`} +\cgalHasModelsBare{`CGAL::Homological_discrete_vector_field::Hdvf_duality`} +\cgalHasModelsEnd + + \sa `AbstractChainComplex` + \sa `IntegralDomainWithoutDivision` + \sa `SparseChain` + \sa `SparseMatrix` + + *How to describe constants declared in the namespace Homological_discrete_vector_field and used everywhere? PSC_flag, options, exporttype* + + [AGL, 2017] Aldo Gonzalez-Lorenzo, Alexandra Bac, Jean-Luc Mari, Pedro Real. Allowing cycles in discrete Morse theory, Topology and its Applications, Volume 228, 2017, Pages 1-35. + + [AGL, 2016] Aldo Gonzalez-Lorenzo. Computational Homology Applied to Discrete Objects. Discrete Mathematics [cs.DM]. Aix-Marseille Université; Universidad de Sevilla, 2016. + +*/ + +class HDVF +{ +public: +/// \name Types +/// @{ + +/*! + * \brief Type of underlying chain complex (a model of `AbstractChainComplex`). + */ +typedef unspecified_type Complex; + +/*! \brief Type of coefficients used to compute homology. */ +typedef Complex::Coefficient_ring Coefficient_ring; + +/*! + * \brief Type of sparse chains (a model of `SparseChain`). + */ + typedef unspecified_type Sparse_chain; + +/*! + * \brief Type of sparse matrices (a model of `SparseMatrix`). + */ + typedef unspecified_type Sparse_matrix; + +/*! \brief Structure to represent data for HDVF operations (pairs of cells). + * + * Cells are always sorted so that the dimension of `sigma` is lesser than the dimension of `tau`. + */ +struct Cell_pair { + /// Index of the first cell + size_t sigma; + /// Index of the second cell + size_t tau; + /// Dimension of cells: `dim`/`dim`+1 for `A()` and `R()`, `dim`/`dim` for other operations + int dim; +}; + +/*! + Type of column-major chains + */ +typedef Sparse_chain Column_chain; + +/*! + Type of row-major chains + */ +typedef Sparse_chain Row_chain ; + +/*! + Type of column-major sparse matrices + */ +typedef Sparse_matrix Column_matrix; + +/*! + Type of row-major sparse matrices + */ +typedef Sparse_matrix Row_matrix; + +/// @} + +/// \name Functions to find valid pairs for `A()` operations +/// @{ + +/*! + * \brief Finds a valid Cell_pair of dimension q / q+1 for A. + * + * The function searches a pair of critical cells \f$(\gamma_1, \gamma2)\f$ of dimension q / q+1, valid for A (ie.\ such that \f$\langle \partial_{q+1}(\gamma_2), \gamma_1 \rangle\f$ invertible). It returns the first valid pair found by iterators. + * + * \param q Lower dimension of the pair. + * \param found Reference to a %Boolean variable. The method sets `found` to `true` if a valid pair is found, `false` otherwise. + */ +virtual Cell_pair find_pair_A(int q, bool &found) const; + +/*! + * \brief Finds a valid Cell_pair for A containing `gamma` (a cell of dimension `q`) + * + * The function searches a cell \f$\gamma'\f$ such that one of the following conditions holds: + * - \f$\gamma'\f$ has dimension q+1 and \f$(\gamma, \gamma')\f$ is valid for A (ie.\ such that \f$\langle \partial_{q+1}(\gamma'), \gamma \rangle\f$ invertible), + * - \f$\gamma'\f$ has dimension q-1 and \f$(\gamma', \gamma)\f$ is valid for A (ie.\ such that \f$\langle \partial_{q}(\gamma), \gamma' \rangle\f$ invertible). + * + * \param q Dimension of the cell `gamma`. + * \param found Reference to a %Boolean variable. The method sets `found` to `true` if a valid pair is found, `false` otherwise. + * \param gamma Index of a cell to pair. + */ +virtual Cell_pair find_pair_A(int q, bool &found, size_t gamma) const; + +/** + * \brief Finds *all* valid Cell_pair of dimension q / q+1 for A. + * + * The function searches all pairs of critical cells \f$(\gamma_1, \gamma2)\f$ of dimension q / q+1, valid for A (ie.\ such that \f$\langle \partial_{q+1}(\gamma_2), \gamma_1 \rangle\f$ invertible). + * It returns a vector of such pairs. + * + * \param q Lower dimension of the pair. + * \param found Reference to a %Boolean variable. The method sets `found` to `true` if a valid pair is found, `false` otherwise. + */ +virtual std::vector find_pairs_A(int q, bool &found) const; + +/** + * \brief Finds *all* valid Cell_pair for A containing `gamma` (a cell of dimension `q`) + * + * The function searches all critical cells \f$\gamma'\f$ such that one of the following conditions holds: + * - \f$\gamma'\f$ has dimension q+1 and \f$(\gamma, \gamma')\f$ is valid for A (ie.\ such that \f$\langle \partial_{q+1}(\gamma'), \gamma \rangle\f$ invertible), + * - \f$\gamma'\f$ has dimension q-1 and \f$(\gamma', \gamma)\f$ is valid for A (ie.\ such that \f$\langle \partial_{q}(\gamma), \gamma' \rangle\f$ invertible). + * It returns a vector of such pairs. + * + * \param q Dimension of the cell `gamma`. + * \param found Reference to a %Boolean variable. The method sets `found` to `true` if a valid pair is found, `false` otherwise. + * \param gamma Index of a cell to pair. + */ +virtual std::vector find_pairs_A(int q, bool &found, size_t gamma) const; + + +/// @} + + +/// \name HDVF functions and operations +/// @{ + +/*! + * \brief A operation: pairs two critical cells. + * + * A pair of critical cells \f$(\gamma_1, \gamma_2)\f$ of respective dimension q and q+1 is valid for A if \f$\langle \partial_{q+1}(\gamma_2), \gamma_1 \rangle\f$ is invertible. After the `A()` operation, \f$\gamma_1\f$ becomes PRIMARY, \f$\gamma_2\f$ becomes SECONDARY. The A functions updates the reduction accordingly (in time \f$\mathscr O(n^2)\f$). + */ +void A(size_t gamma1, size_t gamma2, int q); + +/*! + * \brief Computes a perfect HDVF. + * + * As long as valid pairs for A exist, the function selects the first available pair (returned by `find_pair_A()`) and applies the corresponding `A()` operation. + * If the `IntegralDomainWithoutDivision` of coefficients is a field, this operation always produces a perfect HDVF (ie.\ the reduced boundary is null and the reduction provides homology and cohomology information). + * + * If the HDVF is initially not trivial (some cells have already been paired), the function completes it into a perfect HDVF. + * + * \param verbose If `true`, all intermediate reductions are printed out. + * + */ +std::vector compute_perfect_hdvf(bool verbose = false); + +/*! + * \brief Computes a random perfect HDVF. + * + *As long as valid pairs for A exist, the function selects a random pair (among pairs returned by `find_pairs_A()`) and applies the corresponding `A()` operation. + * If the `IntegralDomainWithoutDivision` of coefficients is a field, this operation always produces a perfect HDVF (ie.\ the reduced boundary is null and the reduction provides homology and cohomology information). + * + * If the HDVF is initially not trivial (some cells have already been paired), the function randomly completes it into a perfect HDVF. + * + * \param verbose If `true`, all intermediate reductions are printed out. + */ +std::vector compute_rand_perfect_hdvf(bool verbose = false); +/// @} + +/// \name Getters +/// @{ + +/*! + * \brief Gets cells with a given `PSC_flag` in any dimension. + * + * The function returns in each dimension the vector of cells with a given `PSC_flag`. + */ +std::vector > flag (PSC_flag flag) const; + +/*! + * \brief Gets cells with a given `PSC_flag` in dimension `q`. + * + * The function returns the vector of cells of dimension `q` with a given `PSC_flag`. + */ +std::vector psc_flags (PSC_flag flag, int q) const; + +/*! + * \brief Gets the `PSC_flag` of the cell `tau` in dimension `q`. + */ +PSC_flag psc_flag (int q, size_t tau) const; + +/*! + * \brief Gets HDVF option. + */ +int hdvf_opts (); + +/*! + * \brief Gets the row-major matrix of \f$f\f$ (from the reduction associated to the HDVF). + */ +const Row_matrix& matrix_f (int q) const; + +/*! + * \brief Gets the column-major matrix of \f$g\f$ (from the reduction associated to the HDVF). + */ +const Column_matrix& matrix_g (int q) const; + +/*! + * \brief Gets the column-major matrix of \f$h\f$ (from the reduction associated to the HDVF). + */ +const Column_matrix& matrix_h (int q) const; + +/*! + * \brief Gets the column-major matrix of \f$\partial'\f$, reduced boundary operator (from the reduction associated to the HDVF). + */ +const Column_matrix& matrix_dd (int q) const; + +/*! + * \brief Tests if a HDVF is perfect. + * + * The function returns `true` if the reduced boundary matrix is null and `false` otherwise. + */ + bool is_perfect_hdvf(); + +/// @} + +/// \name Output functions +/// @{ + +/*! + * \brief Writes the matrices of the reduction. + * + * Writes the matrices of the reduction (that is \f$f\f$, \f$g\f$, \f$h\f$, \f$\partial'\f$ the reduced boundary). + * + * By default, writes the complex to `std::cout`. +*/ +std::ostream& write_matrices(std::ostream &out = std::cout) const; + +/*! \brief Writes the homology and cohomology reduction information. + * + * Writes the homology and cohomology reduction information (that is \f$f^*\f$, \f$g\f$ \f$\partial'\f$ the reduced boundary over each critical cell). + * + * By default, writes the complex to `std::cout`. +*/ +std::ostream& write_reduction(std::ostream &out = std::cout) const; + +/*! + *\brief Writes a HDVF and its reduction to a stream. + * + * Write a HDVF together with the associated reduction (f, g, h, d matrices) to the stream `out` in `.hdvf` file format. + */ + +std::ostream& write_hdvf_reduction(std::ostream& out) ; + +/*! + * \brief Reads a HDVF and its reduction from a stream. + * + * Reads a HDVF and its reduction from the stream `in` in `.hdvf` file format, a simple text file format (see for a specification). + */ +std::istream& read_hdvf_reduction(std::istream& in) ; + +/*! + * \brief Exports primary/secondary/critical labels (in particular for vtk export). + * + * The method exports the labels of every cell in each dimension. + * + * \return A vector containing, for each dimension, the vector of labels by cell index. + */ +virtual std::vector > psc_labels () const; + +/*! + * \brief Returns a chain containing the homology generator associated to `cell_index` (critical cell) of dimension `q` (in particular for vtk export). + * + * The method exports the chain \f$g(\sigma)\f$ for \f$\sigma\f$ the cell of index `cell_index` and dimension `q`. + * + * \return A column-major chain. + */ +virtual Column_chain homology_chain (size_t cell_index, int q) const; + +/*! + * \brief Returns a chain containing the cohomology generators associated to `cell_index` (critical cell) of dimension `q` (in particular for vtk export). + * + * The method exports: + * - the chain \f$f^\star(\sigma)\f$ for \f$\sigma\f$ the cell of index `cell_index` and dimension `q`, + * - or the co-faces of this chain if `co_faces` is true (sometimes easier to view cohomology generators) + * + * Below, a sample mesh with, (left) homology generators, (right) two examples of cohomology generators (corresponding generators/co-generators bear similar colours): + * + * + * + * + * + * The same generators displayed through their co-faces: + * + * + * + * + * All homology / cohomology generators: + * + * + * + * \return A column-major chain. + */ +virtual Column_chain cohomology_chain (size_t cell_index, int dim, bool co_faces = false) const; + +/// @} + +}; diff --git a/HDVF/doc/HDVF/Concepts/HDVFTraits.h b/HDVF/doc/HDVF/Concepts/HDVFTraits.h new file mode 100644 index 00000000000..1fc7dc97aa1 --- /dev/null +++ b/HDVF/doc/HDVF/Concepts/HDVFTraits.h @@ -0,0 +1,29 @@ +/*! +\ingroup PkgHDVFConcepts +\cgalConcept + +The concept `HDVFTraits` describes the requirements for geometric traits classes used in this package. + +\cgalHasModelsBegin +\cgalHasModelsBare{`CGAL::Homological_discrete_vector_field::Hdvf_traits_2`} +\cgalHasModelsBare{`CGAL::Homological_discrete_vector_field::Hdvf_traits_3`} +\cgalHasModelsBare{`CGAL::Homological_discrete_vector_field::Hdvf_traits_d`} +\cgalHasModelsEnd + +@todo add requirements on Point, Vector, FT, Bbox +*/ +class HDVFTraits +{ +public: + /* \brief Type tag encoding the dimension of the data. Must be a `CGAL::Dimension_tag<>` */ + typedef unspecified_type Dimension ; + + /* \brief Geometric kernel type. Must be a model of the `Kernel` concept. */ + typedef unspecified_type Kernel ; + typedef unspecified_type Point ; + typedef unspecified_type Vector ; + typedef typename Kernel::FT FT ; + + /* \brief Type of bounding boxes in the data dimension. */ + typedef unspecified_type Bbox ; +}; \ No newline at end of file diff --git a/HDVF/doc/HDVF/Concepts/SparseChain.h b/HDVF/doc/HDVF/Concepts/SparseChain.h new file mode 100644 index 00000000000..06c4bfc37c8 --- /dev/null +++ b/HDVF/doc/HDVF/Concepts/SparseChain.h @@ -0,0 +1,311 @@ +/*! +\ingroup PkgHDVFConcepts +\cgalConcept + +The concept `SparseChain` describes the requirements for sparse vectors (called *sparse chains* in homology) optimized for topological computations. +More precisely, `SparseChain` provides all the operations on chains required by the `SparseMatrix` concept. + + `SparseChain` encodes non zero coefficients of a sparse vector. + + `SparseChain` can be either row or column vectors. The following constants, called `StorageFormat`, encode the direction of sparse chains (and sparse matrices). + - `CGAL::OSM::COLUMN` for column-major chains and matrices (which is the default), + - `CGAL::OSM::ROW` for row-major chains and matrices. + + +\cgalHasModelsBegin +\cgalHasModelsBare{`CGAL::OSM::Sparse_chain`} +\cgalHasModelsEnd + + \sa `IntegralDomainWithoutDivision` + \sa `SparseMatrix` +*/ + +class SparseChain { + +public: + /// \name Types + /// @{ + + /*! + * \brief Type of coefficients stored in the matrix (a model of `IntegralDomainWithoutDivision`). + */ + typedef unspecified_type Coefficient_ring; + + /*! + * \brief Matrix and chain storage format (either CGAL::OSM::ROW or CGAL::OSM::COLUMN). + */ + typedef int Storage_format; + + /*! + * \brief Sparse chain iterator type. + */ + typedef unspecified_type iterator; + + /*! + * \brief Sparse chain constant iterator type. + */ + typedef unspecified_type const_iterator; + + /// @} + + /// \name Creation, filling + /// @{ + + /*! + * \brief Creates new empty sparse chain. + * + * Creates a sparse chain encoding an empty linear combination of cells. + */ + SparseChain(); + + /*! + * \brief Creates new empty sparse chain (ie.\ zero-chain) of given size. + * + * Constructor with size, initializes an empty sparse chain encoding a linear combination of cells with all coefficients null. + */ + SparseChain(size_t chainSize); + + /*! + * \brief Creates new sparse chain by copy. + * + * Copy constructor, initialize a sparse chain from an existing sparse chain of same storage format. + */ + SparseChain(const SparseChain &other); + + /*! + * \brief Assigns to other chain. + * + * Assign to other chain coefficient-wise, equivalent to copying it. + * + * SparseChain must have the same `Coefficient_ring`. + */ + SparseChain& operator=(const SparseChain &other); + + /// @} + + /// \name Matrix informations and iterators + /// @{ + + /*! + * \brief Returns the dimension of the basis (that is, size of the chain). + */ + size_t dimension() const; + + /*! + * \brief Iterator to the beginning of the chain. + * + * The function returns an iterator to the first non zero index. + */ + iterator begin() noexcept; + + /*! + * \brief Constant iterator to the beginning of the chain. + * + * The function returns a constant iterator to the first non zero index. + */ + const_iterator begin() const noexcept; + + /*! + * \brief Constant iterator to the beginning of the chain. + * + * The function returns a constant iterator to the first non zero index. + */ + const_iterator cbegin() const noexcept; + + /*! + * \brief Past-the-end iterator of the chain. + * + * The function returns an iterator past the end of the chain. + */ + iterator end() noexcept; + + /*! + * \brief Constant past-the-end iterator of the chain. + * + * The function returns a constant iterator past the end of the chain. + */ + const_iterator end() const noexcept; + + /*! + * \brief Constant past-the-end iterator of the chain. + * + * The function returns a constant iterator past the endi of the chain. + */ + const_iterator cend() const noexcept; + + /// @} + + /// \name Output + /// @{ + + /*! + * \brief Inserts `chain` in the output stream. + */ + friend std::ostream& operator<<(std::ostream &stream, const SparseChain& chain); + + /// @} + + /// \name Linear algebra operators + /// @{ + + /*! + * \brief Adds two chains together. + * + * Add two chains and return the result in a new matrix. + * Chains must have the same `Coefficient_ring` and the same storage format. + */ + friend SparseChain operator+(const SparseChain &first, const SparseChain &second); + + /*! + * \brief Subtracts a chain from another chain. + * + * Subtract two chains and return the result in a new matrix. + * Chains must have the same `Coefficient_ring` and the same storage format. + */ + friend SparseChain operator-(const SparseChain &first, const SparseChain &second); + + /*! + * \brief Applies multiplication on each coefficient. + */ + friend SparseChain operator*(const Coefficient_ring& lambda, const SparseChain &chain); + + /*! + * \brief Applies multiplication on each coefficient. + */ + friend SparseChain operator*(const SparseChain &_chain, const Coefficient_ring& lambda); + + /*! + * \brief Performs matrix multiplication between a column chain and a row chain and returns a column-based matrix. + * + * Generate a column-based matrix from the matrix multiplication and return it. + * \pre chains must have the same `Coefficient_ring`. + * \pre `column.is_column()` and `row.is_row()` must be `true` + */ + friend SparseMatrix operator*(const SparseChain &column, const SparseChain &row); + + /*! + * \brief Performs matrix multiplication between a column chain and a row chain and returns a row-based matrix. + * + * Generate a row-based matrix from the matrix multiplication and return it. + * \pre chains must have the same `Coefficient_ring`. + * \pre `column.is_column()` and `row.is_row()` must be `true` + */ + friend SparseMatrix operator%(const Sparse_chain &column, const Sparse_chain &row); + + /*! + * \brief Performs dot product between a row chain and a column chain. + * + * \pre chains must have the same `Coefficient_ring`. + * \pre `row.is_row()` and `column.is_column()` must be `true` + */ + friend Coefficient_ring operator*(const Sparse_chain &row, const Sparse_chain &column); + + /*! + * \brief Adds a chain to `this`. + * + * Add a chain to `this`. + * Chains must have the same `Coefficient_ring` and the same storage format. + */ + SparseChain& operator+=(const SparseChain &_other); + + /*! + * \brief Subtracts a chain from `this`. + * + * Subtract a chain from `this`. + * Chains must have the same `Coefficient_ring` and the same storage format. + */ + SparseChain& operator-=(const SparseChain &_other); + + /*! + * \brief Applies multiplication on each coefficient of `this`. + */ + SparseChain& operator*=(const Coefficient_ring& lambda); + + /*! + * \brief Transposes a sparse chain. + * + * The result is a chain with storage format switched between column and row. + */ + SparseChain transpose(); + + /// @} + + /// \name Access and modifications + /// @{ + + /*! + * \brief Compares two chains. + */ + bool operator==(const SparseChain& other_chain); + + /*! + * \brief Gets the value of a coefficient of the chain. + */ + Coefficient_ring operator[](size_t index); + + /*! + * \brief Gets the value of a coefficient of the chain. + */ + Coefficient_ring get_coefficient(size_t index) const ; + + /** + * \brief Sets a given coefficient of the chain. + */ + void set_coefficient(size_t index, Coefficient_ring d); + + /*! + * \brief Checks if a coefficient of the chain is null. + */ + bool is_null(size_t index) const; + + /*! + * \brief Checks if the chain is null. + */ + bool is_null() const; + + /*! + * \brief Gets a sub-chain from the chain. + * + * Return a new chain where all coefficients of indices provided in the vector are removed. + */ + friend SparseChain operator/(const SparseChain &chain, const std::vector &indices); + + /*! + * \brief Gets a sub-chain from the chain. + * + * Return a new chain where the coefficients at a given index is removed. + */ + friend SparseChain operator/(const SparseChain &chain, size_t indices); + + /*! + * \brief Restricts the chain to a sub-chain by removing indices. + * + * Removes all indices provided in the vector from the chain. Return a reference to the modified chain. + */ + SparseChain& operator/=(const std::vector &indices); + + /** + * \brief Restricts the chain to a sub-chain by removing a given index. + * + * Removes the index provided from the chain. Return a reference to the modified chain. + */ + SparseChain& operator/=(size_t index); + + /** + * \brief Removes all coefficients from the chain. + * + * The function comes to set all coefficients to zero. + */ + void nullify(); + + /*! + * \brief Checks if chain is a column. + */ + bool is_column() const; + + /*! + * \brief Checks if chain is a row. + */ + bool is_row() const; + /// @} +}; diff --git a/HDVF/doc/HDVF/Concepts/SparseMatrix.h b/HDVF/doc/HDVF/Concepts/SparseMatrix.h new file mode 100644 index 00000000000..281a61eeb66 --- /dev/null +++ b/HDVF/doc/HDVF/Concepts/SparseMatrix.h @@ -0,0 +1,416 @@ +/*! +\ingroup PkgHDVFConcepts +\cgalConcept + +The concept `SparseMatrix` describes the requirements for sparse matrices optimized for topological computations. +Traditionally, sparse matrices data structures encode non zero coefficients of (sparse) matrices in order to optimize either matrices memory footprint, or linear algebra operations (which usually comes to optimize iterators over non zero coefficients and access to coefficients). However, topological operations require slightly different features: + - fast access to row or columns of matrices (which are actually the images under the application encoded by the matrix) + - fast block operations (especially along row or columns) + + The `SparseMatrix` concept describes requirements for such sparse matrix. It relies on the model of `SparseChain` which encodes sparse row or column vectors. Matrices are either column major or row major (hence they either store column sparse chains or row sparse chains). + + The following constants, called `StorageFormat`, encode the major direction of both sparse chains and sparse matrices. + - `CGAL::OSM::COLUMN` for column-major chains and matrices (which is the default), + - `CGAL::OSM::ROW` for row-major chains and matrices. + + For instance, given the \f$5\times 4\f$ matrix: + \f[ + A = \left(\begin{array}{cccc} + 1 & \cdot & \cdot & \cdot \\ + -1 & \cdot & 2 & \cdot\\ + \cdot & \cdot & 1 & \cdot \\ + \cdot & \cdot & \cdot & \cdot \\ + \cdot & \cdot & \cdot & \cdot \\ + \end{array}\right) + \f] + where \f$\cdot\f$ means \f$0\f$. + - A column-major representation of \f$A\f$ is: \f$[0\mapsto c_0, 2\mapsto c_2]\f$ with the column-chains: \f$c_0 = [0\mapsto 1, 1\mapsto -1]\f$ and \f$c_2 = [1\mapsto 2, 2\mapsto 1]\f$. + - A row-major representation of \f$A\f$ is: \f$[0\mapsto c_0, 1\mapsto c_1, 2\mapsto c_2]\f$ with the row-chains: \f$c_0 = [0\mapsto 1]\f$ and \f$c_1 = [0\mapsto -1, 2\mapsto 2]\f$ and \f$c_2 = [2\mapsto 1]\f$. + +\cgalHasModelsBegin +\cgalHasModelsBare{`CGAL::OSM::Sparse_matrix`} +\cgalHasModelsEnd + + \sa `IntegralDomainWithoutDivision` + \sa `SparseChain` +*/ + +class SparseMatrix { + +public: + /// \name Types + /// @{ + + /*! + * \brief Type of coefficients stored in the matrix (a model of `IntegralDomainWithoutDivision`). + */ + typedef unspecified_type Coefficient_ring; + + /*! + * \brief Matrix and chain storage format (either `ROW` or `COLUMN`). + */ + typedef int Storage_format; + + /*! + * \brief A data structure storing indices of non empty chains. + */ + typedef unspecified_type Non_zero_chain_indices; + + /*! + * \brief Type of the chains stored in the matrix. + */ + typedef unspecified_type Matrix_chain; + + /// @} + + + /// \name Creation, Filling + /// @{ + + /*! + * \brief Creates an empty new sparse matrix object. + * + * Default constructor, initialize an empty matrix of type `StorageFormat` with coefficients of type `Coefficient_ring`. + * The default matrix size is 0x0. + */ + SparseMatrix() ; + + /*! + * \brief Creates a new sparse matrix object with given rows/columns sizes. + * + * Constructor with sizes, initialize an empty matrix of type `StorageFormat` with coefficients of type `Coefficient_ring` and a given size along rows/columns. + */ + SparseMatrix(const size_t rowCount, const size_t columnCount) ; + + /** + * \brief Creates a new sparse matrix from another sparse matrix object (with possibly a different `StorageFormat`). + * + * Copy constructor, initialize a sparse matrix of same sizes, containing the same coefficients (but not necessarly of the same `StorageFormat`). + * If types are different, the constructor performs conversion. + */ + SparseMatrix(const SparseMatrix& otherToCopy); + + /*! + * \brief Assigns to other matrix. + * + * Assign to other matrix coefficient-wise, equivalent to copying it. + * + * Matrices must have the same type. + */ + SparseMatrix& operator=(const SparseMatrix& _otherToCopy); + + /** + * \brief Cleans a sparse matrix (set all coefficients to zero). + * + * Empty all structures of the sparse matrix. + * + */ + void nullify(); + + /// @} + + /// \name Matrix informations and iterators + /// @{ + + /** + * \brief Tests if a sparse matrix is null. + * + * The function returns `true` if the sparse matrix is null (that is, empty) and `false` otherwise. + */ + bool is_null(); + + /** + * \brief Gets the matrix size. + * + * The matrix size as a row/column pair. + */ + std::pair dimensions() const; + + /** + * \brief Iterator to the beginning of the chain indices (visited by increasing indices). + * + * The function returns an iterator to the beginning of the (non zero) chain indices. + */ + inline Non_zero_chain_indices::iterator begin() const noexcept; + + /** + * \brief Past-the-end iterator of the chain indices (visited by increasing indices). + * + * The function returns an iterator past the end of the chain indices. + */ + inline Non_zero_chain_indices::iterator end() const noexcept; + + /** + * \brief Reverse iterator to the beginning of the chain indices (visited by decreasing indices). + * + * The function returns a reverse iterator to the beginning of the (non zero) chain indices. + */ + inline Non_zero_chain_indices::iterator reverse_begin() const noexcept; + + /** + * \brief Past-the-end reverse iterator of the chain indices (visited by decreasing indices). + * + * The function returns a past-the-end reverse iterator of the chain indices. + */ + inline Non_zero_chain_indices::iterator reverse_end() const noexcept; + + /// @} + + /// \name Output + /// @{ + + /** + * \brief Inserts `matrix` in the output stream. + */ + friend std::ostream& operator<<(std::ostream &_stream, const SparseMatrix &matrix); + + /** + * \brief Inserts `matrix` in an output stream. + */ + friend std::ostream& write_matrix (const SparseMatrix& matrix, std::ostream& out); + + /** + * \brief Extracts a sparse matrix from an input stream. + */ + + template + friend std::istream& read_matrix (SparseMatrix& matrix, std::istream& in); + /// @} + + /// \name Linear algebra operators + /// @{ + + /** + * \brief Adds a matrix and assign. + * + * Adds each coefficient of the matrix together and stores the result in `this`. + * Matrices must have the same `Coefficient_ring` but can have different `StorageFormat`. + */ + friend SparseMatrix& operator+=(SparseMatrix &matrix, const SparseMatrix &other); + + /** + * \brief Adds two matrices together. + * + * Adds each coefficient of the matrices together and returns a new matrix (of the same type as `first`) representing the result (when possible, prefer `+=` for efficiency). + * Matrices must have the same `Coefficient_ring` but can have different `StorageFormat`. + */ + friend SparseMatrix operator+(const SparseMatrix &first, const SparseMatrix &second); + + + /** + * \brief Subtracts a matrix and assign. + * + * Subtracts each coefficient of the matrix together and stores the result in `matrix`. + * Matrices must have the same `Coefficient_ring` but can have different `StorageFormat`. + */ + SparseMatrix& operator-=(SparseMatrix &matrix, const SparseMatrix &other); + + /** + * \brief Subtracts two matrices together. + * + * Subtracts each coefficient of the matrix together and returns a new matrix (of the same type as `first`) representing the result (when possible, prefer `-=` for efficiency). + * Matrices must have the same `Coefficient_ring` but can have different `StorageFormat`. + */ + friend SparseMatrix operator-(const SparseMatrix &first, const SparseMatrix &second); + + /** + * \brief Computes the negative of a matrix (unary operator). + * + * \return The resulting matrix. + */ + friend SparseMatrix operator-(const SparseMatrix& matrix); + + /** + * \brief Applies factor on each coefficients into a new matrix. + * + * This method creates a new matrix obtained by multiplying the matrix by a scalar factor `lambda`. If `lambda` is zero, the function comes to nullify the matrix (when possible, prefer `*=` for efficiency). + */ + friend SparseMatrix operator*(const Coefficient_ring& lambda, const SparseMatrix &matrix); + + /** + * \brief Applies factor on each coefficients into a new matrix. + * + * This method creates a new matrix obtained by multiplying the matrix by a scalar factor `lambda`. If `lambda` is zero, the function comes to nullify the matrix (when possible, prefer `*=` for efficiency). + */ + friend SparseMatrix operator*(const SparseMatrix &matrix, const Coefficient_ring& lambda); + + /** + * \brief Applies factor on each coefficient and assign. + * + * This method multiplies the matrix by a scalar factor `lambda`. + */ + SparseMatrix& operator*=(const Coefficient_ring& lambda); + + /** + * \brief Multiplies a matrix and assign. + * + * Multiply each coefficient of the matrix together and stores the result in `matrix`. + * Matrices must have the same `Coefficient_ring` but can have different `StorageFormat`. + */ + friend SparseMatrix& operator*=(SparseMatrix &matrix, const SparseMatrix &other); + + /** + * \brief Performs multiplication between matrices and returns a new column-major matrix. + * + * Perform standard linear algebra product between matrices and returns a new column-major matrix (when possible, prefer `*=` for efficiency). Matrices must have the same `Coefficient_ring` but can have different `StorageFormat`. Efficiency of the product depends of `StorageFormat` (when possible, prefer row-major by column-major products). + */ + friend SparseMatrix operator*(const SparseMatrix &first, const SparseMatrix &second); + + /** + * \brief Performs multiplication between matrices and returns a new row-major matrix. + * + * Perform standard linear algebra product between matrices and returns a new row-major matrix (when possible, prefer `*=` for efficiency). Matrices must have the same `Coefficient_ring` but can have different `StorageFormat`. Efficiency of the product depends of `StorageFormat`. + */ + friend SparseMatrix operator%(const SparseMatrix &first, const SparseMatrix &second); + + /** + * \brief Performs multiplication between a matrix and a column-based chain. + * + * Generates a column-based chain from the matrix multiplication and returns it. + * + * \pre the matrix and the chain must have the same `Coefficient_ring`. + * \pre `column.is_column()` must be `true` + */ + friend SparseChain operator*(const SparseMatrix& matrix, const SparseChain& column); + + /** + * \brief Performs multiplication between a row-based chain and a matrix. + * + * Generates a row-based chain from the matrix multiplication and returns it. + * + * \pre the matrix and the chain must have the same `Coefficient_ring`. + * \pre `row.is_row()` must be `true` + */ + friend SparseChain operator*(const SparseChain& row, const SparseMatrix& matrix); + + /** + * \brief Transposes a matrix. + * + * The function returns a new matrix where the `StorageFormat` is changed. + */ + SparseMatrix transpose(); + + + /// @} + + /// \name Access and blocks operations + /// @{ + + /** + * \brief Gets a chain from a const matrix. + * + * If the matrix is column-major, returns the `i`th column, and if the matrix is row-major, returns the `i`th row. + */ + Matrix_chain operator[](size_t index) const; + + /** + * \brief Sets a given coefficient. + * + * Assign the scalar `d` to the coefficient on row `i` and column `j`. + */ + friend void set_coefficient(SparseMatrix& matrix, size_t i, size_t j, const Coefficient_ring d); + + /** + * \brief Gets a given coefficient. + * + * Returns the coefficient on row `i` and column `j` of the matrix. + */ + friend Coefficient_ring get_coefficient(const SparseMatrix& matrix, size_t i, size_t j); + + /** + * \brief Gets the value of the column at a given `index` from the matrix (whatever the `StorageFormat` of the matrix). + * + * For column-matrices, it is equivalent to `operator[]`, for row-matrices a traversal of the matrix is required (in \f$\mathcal O(n)\f$). + * + *\returns a column chain. + */ + friend SparseChain get_column(const SparseMatrix &matrix, size_t index); + + /** + * \brief Gets the value of the row at a given `index` from the matrix (whatever the `StorageFormat` of the matrix). + * + * For row-matrices, it is equivalent to `operator[]`, for column-matrices a traversal of the matrix is required (in \f$\mathcal O(n)\f$). + * + *\returns a row chain. + */ + friend SparseChain get_row(const SparseMatrix &matrix, size_t index); + + /** + * \brief Gets a constant reference over the column of index`i` from a column matrix. + * \pre `matrix.is_column()` must be `true`. + * + * \returns a constant reference to a column chain. + */ + const SparseChain& cget_column(const SparseMatrix& matrix, size_t i); + + /** + * \brief Gets a constant reference over the row of index`i` from a row matrix. + * \pre `matrix.is_row()` must be `true`. + * + * \returns a constant reference to a row chain. + */ + const SparseChain& cget_row(const SparseMatrix& matrix, size_t i); + + /** + * \brief Sets a column in the matrix (whatever the `StorageFormat` of the matrix). + * + * Set the `i`th column of `matrix` to `column`. + * For column-matrices, it should be equivalent to an assignment, however, for row-matrices, a traversal of the matrix is required (in \f$\mathcal O(n)\f$). + * \pre `column.is_column()` must be `true` + */ + void set_column(SparseMatrix &matrix, size_t i, const SparseChain &column); + + /** + * \brief Sets a row in `matrix` (whatever the `StorageFormat` of the matrix). + * + * Set the `i`th row of `matrix` to `chain`. + * For row-matrices, it should be equivalent to an assignment, however, for column-matrices, a traversal of the matrix is required (in \f$\mathcal O(n)\f$). + * \pre `row.is_row()` must be `true` + */ + void set_row(SparseMatrix &matrix, size_t i, const Sparse_chain &row); + + /** + * \brief Gets a submatrix from the matrix. + * + * Nullifies the chain of index `i` along the major direction of a copy of the matrix amd returns it. + */ + friend SparseMatrix operator/(const SparseMatrix &matrix, size_t i); + + /** + * \brief Gets a submatrix from the matrix and assign. + * + * Removes (along the major dimension) all indices provided in the vector `indices` from the matrix and returns it. + */ + SparseMatrix& operator/=(const std::vector &indices); + + /** + * \brief Gets a submatrix from the matrix and assign. + * + * Removes (along the major dimension) the chain of index `i` from the matrix and returns it. + */ + SparseMatrix& operator/=(size_t i); + + /** + * \brief Nullifies a column from the matrix. + * + * Removes column of index `i` whatever the `StorageFormat` of the matrix. For column matrices, it just comes to the `\=` operator and for row matrices, it entails a traversal of the matrix. + */ + friend SparseMatrix& remove_column(SparseMatrix& matrix, size_t index); + + /** + * \brief Nullifies a row from the matrix. + * + * Removes row of index `i` whatever the `StorageFormat` of the matrix. For row matrices, it just comes to the `\=` operator and for column matrices, it entails a traversal of the matrix. + */ + friend SparseMatrix& remove_row(SparseMatrix& matrix, size_t index); + + /** + * \brief Nullifies a coefficient of the matrix. + * + * Removes coefficient on row `i` / column `j` of the matrix. + */ + friend SparseMatrix& remove_coefficient(SparseMatrix& matrix, size_t i, size_t j); + + /// @} + +}; diff --git a/HDVF/doc/HDVF/Doxyfile.in b/HDVF/doc/HDVF/Doxyfile.in new file mode 100644 index 00000000000..2a35a9e9098 --- /dev/null +++ b/HDVF/doc/HDVF/Doxyfile.in @@ -0,0 +1,3 @@ +@INCLUDE = ${CGAL_DOC_PACKAGE_DEFAULTS} + +PROJECT_NAME = "CGAL ${CGAL_DOC_VERSION} - Homological Discrete Vector Fields" diff --git a/HDVF/doc/HDVF/HDVF.txt b/HDVF/doc/HDVF/HDVF.txt new file mode 100644 index 00000000000..ee0fdc25ffd --- /dev/null +++ b/HDVF/doc/HDVF/HDVF.txt @@ -0,0 +1,790 @@ +namespace CGAL { +/*! + +\mainpage User Manual +\anchor Chapter_HDVF + +\cgalAutoToc +\author Alexandra Bac + +Algebraic topology, introduced more than a century ago, studies topological spaces using algebraic tools derived from them (e.g. groups). Since the 70's and 80's, pioneering works (MacLane, Hilton, Munkres...) paved the way to computational homology, which studies the ``holes'' of an object. +In particular, Munkres introduced an algorithm computing Betti numbers based on the pseudo-diagonalization of boundary matrices (Smith Normal Form). + +In the 90's, persistent homology emerged independently through the work of H. Edelsbrunner et al. (North Carolina), P. Frosini et al. (Bologna), and V. Robins (Colorado), extending the study of homology to sequences of complexes (filtrations). +This later theory proved to have a wide range of applications in topological data analysis and hence quickly developed and spread in the scientific community. +In parallel, in the late 80's, R. Forman defined a discrete version of Morse theory for CW-complexes, while F. Sergeraert introduced effective homology theory. + +In [AGL, 2017], A. Gonzalez-Lorenzo et al. defined the **homological discrete vector field** (which we refer to as HDVF in the present package), a combinatorial object that generalizes previous approaches. +Precisely, HDVFs combine discrete Morse and effective homology theory and fully encode homology information (homology groups decomposition, Betti numbers, homology generators) for cellular complexes. +HDVFs encompass discrete Morse theory, effective homology and Smith Normal form, and: persistent homology can be computed with HDVFs (see [AGL, 2016]), relative homology and Alexander duality (see [AGL, 2025]). + + \cgalFigureBegin{HDVFtwirl,HDVF_twirl.png} + HDVF on a twirl model. **Left:** a perfect HDVF over the model; **Middle:** Homology generators associated to this HDVF; **Right:** Corresponding cohomology generators. + \cgalFigureEnd + +Unlike other main stream state of art approaches and librairies (largely based on persistent homology), HDVFs provide a framework for "geometric" homology and cohomology computation, where holes are not only enumerated, but also "localized" (through homology, cohomology generators, annotations...). + + + +\section secHDVFdefinitions Overview + +The HDVF package is dedicated to homology and cohomology computation for discrete objects and provides tools to compute: +- basic homological information (such as Betti numbers, ie.\ number of holes per dimension), +- but also geometric, higher level information (homology and cohomology generators, homology/cohomology annotation, cycles and co-cycles comparison...), +- moreover HDVFs compute persistent homology (and provide "persistent generators" information) as well as Alexander duality and relative homology. + +\section secHDVFHomology Homology/cohomology and related reminders + +Homology and cohomology are well know topological invariants which accounts for the holes of an object using commutative groups built from the object. In Mathematics, homology and cohomology can be defined for general Hausdorff topological spaces; we speak then about "singular homology and cohomology". However, such a general definition is not computable and thus, homology/cohomology computation requires a discretization of spaces into cells of various dimensions. Such a discretization is called a **complex**: +- **simplicial complexes** are intuitively meshes built from vertices, edges, triangles, tetrahedra... +- **cubical complexes** are built from vertices, edges, squares, cubes... +- **cellular complexes** are built from vertices, edges, polygons (without holes), polyhedra (without holes)... + +Actually, a set of cells is called a complex \f$K\f$ when: +- it is closed under the "face" operation (ie.\ all the faces of a cell of the complex also belong to the complex) +- intersections of cells are either empty of they are a proper cell of the complex +The dimension of \f$K\f$ is the largest dimension of its cells. + + \cgalFigureBegin{HDVFcomplexes,HDVF_cub_simpl_complex.png} + Illustration of complexes. **Left:** a simplicial complex of dimension 3; **Right:** A cubical complex of dimension 3. + \cgalFigureEnd + + In order to compute homology and cohomology, algebraic objects are built from such "geometric" complexes, namely chain complexes. + +\subsection secHDVFChainComplexes Chain complexes + +In order to define this algebraic structure, one first need to chose a **ring of coefficients** \f$A\f$ (\f$A\f$ is often taken to be \f$Z\f$ or a \f$\mathbb Z/p\mathbb Z\f$ field). + +Given a complex \f$K\f$ of dimension \f$d\f$, a **chain complex** \f$C\f$ is: + +\f\[C = \cdots C_{q+1} \xrightarrow{\partial_{q+1}} C_q \xrightarrow{\partial_{q}} C_{q-1} \xrightarrow{\partial_{q-1}} \cdots \to C_1 \xrightarrow{\partial_1} C_0 \xrightarrow{\partial_0 = 0} 0\f\] + + + +- a sequence of commutative groups, called **chain groups** \f$C_q\f$ for any \f$0\leqslant q \leqslant d\f$ defined as the formal linear combinations of cells with coefficients in \f$A\f$ (or \f$A\f$-module).
+Intuitively, elements of \f$C_q\f$, called **chains** are sums \f$\gamma = \sum_{i=1}^k\lambda_i\cdot \sigma_i\f$ with \f$\lambda_i\in A\f$ and \f$\sigma_i\f$ cells of dimension \f$q\f$, which can be imagined as a set of paths of oriented cells with multiplicity (see \cgalFigureRef{HDVFchain_complexes} where two 1-chains are represented). +- a sequence of morphisms between chain groups, called **boundary morphisms** \f$\partial_q\,:\, C_q\to C_{q-1}\f$ for any \f$0\leqslant q \leqslant d\f$, "mimicking" algebraically the geometric boundary and satisfying + +\f\[\partial_q\partial_{q+1} = 0\f\] + +Coming back to the example of \cgalFigureRef{HDVFchain_complexes}, as we will see in section \ref secHDVFsimplicial_chain_complex, the boundary of the edge \f$e_{45}\f$ is the 0-chain \f$v_5-v_4\f$ (ordering is given by vertices indices). Hence, on easily gets that \f$\partial_1(\gamma_1)=0\f$ and \f$\partial_1(\gamma_2)=v_8-v_4\f$, which is a very intuitive result. + + \cgalFigureBegin{HDVFchain_complexes,simplicial_complex_chains.png} + Examples of chains representation in a simplicial complex (with \f$A = \mathbb Z\f$). **Pink:** representation the 1-chain (of edges) \f$\gamma_1 = e_{24}+e_{46}-e_{26}\f$; **Blue:** representation of the 1-chain \f$\gamma_2 = e_{45}+e_{56}+e_{67}+e_{78}\f$. + \cgalFigureEnd + +The definition of the boundary operator depends on the underlying complex, it is detailed in next sections for simplicial and cubical complexes respectively. + + Notation + +Given a chain \f$\gamma\in C_q\f$ and \f$\sigma\f$ a \f$q\f$-cell, we denote by \f$\langle\gamma,\sigma\rangle\f$ the coefficient of \f$\sigma\f$ in \f$\gamma\in C_q\f$. + +\subsubsection secHDVFsimplicial_chain_complex Simplicial chain complexes + +An **abstract simplicial complex** of dimension \f$d\f$ is built from: +- a set of vertices identified by an index of type `size_t` (cells of dimension 0 or **0-simplices**) +- for any \f$q\f$ with \f$0< q\leqslant d\f$, cells of dimension \f$q\f$ (or **q-simplices**) are defined by \f$q+1\f$ vertices (denoted \f$\langle v_0,\ldots,v_{q}\rangle\f$ with vertices sorted by *increasing indices*). + +A **geometric simplicial complex** equips vertices with coordinates in \f$\mathbb R^q\f$ so that \f$q\f$-simplices are convex hulls of \f$q+1\f$ points. + +Given a simplicial complex (abstract or geometric) \f$K\f$, the associated chain complex \f$(C,\partial)\f$, called a **simplicial chain complex** is defined as follows: + +- The \f$q\f$-th chain group \f$C_q\f$ is the free \f$A\f$-module generated by \f$q\f$-cells described in the section \ref secHDVFChainComplexes. + +- The \f$q\f$-th boundary morphism is defined over the \f$q\f$-simplices (ie.\ the basis) by: + +\f\[\partial_q : \begin{array}{rcl} +C_q & \to & C_{q-1} \\ +\langle v_0,\ldots,v_{q}\rangle & \mapsto & \sum_{i=0}^q (-1)^i\cdot \langle v_0,\ldots,\hat{v_i}, \ldots, v_{q}\rangle \\ +\end{array} +\f\] +where \f$\hat{v_i}\f$ means that the \f$i\f$-th vertex has been discarded. + +For instance, in our previous example: +- For the edge \f$\langle 4,5\rangle\f$, we get \f$\partial_1(\langle 4,5\rangle) = \langle 5\rangle - \langle 4\rangle\f$ +- For the triangle \f$\langle 2,4,6\rangle\f$, we get \f$\partial_2(\langle 2,4,6\rangle) = \langle 4,6\rangle - \langle 2,6\rangle + \langle 2,4\rangle\f$ + +\subsubsection secHDVFcubical_chain_complex Cubical chain complexes + +A **cubical complex** of dimension \f$d\f$ is a set of cells defined as cartesian products: +\f\[\sigma = [n_1,n_1+\delta_1]\times\cdots \times [n_d,n_d+\delta_d]\f\] +with \f$n_i\in \mathbb N\f$ and \f$\delta_i\in\{0,1\}\f$ for \f$i=1\ldots d\f$. +The dimension of such a cell is \f$\sum_{i=1}^d \delta_i\f$. +As stated in previous section, a complex is closed under the "face" operation (if a cell belongs to the complex, so do its faces). + +**Khalimsky coordinates** associate to each cell, whatever its dimension, a unique coordinate in \f$\mathbb N^d\f$ defined as: +\f\[\mathrm{khal}(\sigma) = (2 n_1+\delta_1,\ldots, 2 n_d+\delta_d)\f\] +The dimension of a cell in this coordinate system is thus the number of odd coordinates. + + \cgalFigureBegin{HDVFcubical_complex_fig,cubical_complex.png} + A cubical complex of dimension 3. + \cgalFigureEnd + + For instance, in the example above: + - \f$v = [2,2] \times [1,1] \times [0,0]\f$ and \f$\mathrm{khal}(\sigma) = (4,2,0)\f$ (cell of dimension 0) + - \f$e = [0,1] \times [1,1] \times [0,0]\f$ and \f$\mathrm{khal}(\sigma) = (1,2,0)\f$ (cell of dimension 1) + - \f$f = [2,3] \times [0,1] \times [1,1]\f$ and \f$\mathrm{khal}(\sigma) = (5,1,2)\f$ (cell of dimension 2) + +Given a cubical complex \f$K\f$, the associated chain complex \f$(C,\partial)\f$, called a **cubical chain complex** is defined as follows: +- The \f$q\f$-th chain group \f$C_q\f$ is the free \f$A\f$-module generated by \f$q\f$-cells described in the section \ref secHDVFChainComplexes. +- The \f$q\f$-th boundary morphism is defined over Khalimsky coordinates of cells (ie.\ the basis) as follows: +\f\[\partial_q(x_1,\ldots,x_d) = \sum_{\substack{i=1\\i\text{ odd}}}^d (-1)^{i+1}\Big((x_1,\ldots, x_i+1,\ldots, x_d) - (x_1,\ldots, x_i-1,\ldots, x_d)\Big)\f] + +For instance, in the example above: +- \f$\partial(e) = (2,2,0)-(0,2,0)\f$ +- \f$\partial(f) = \big((6,1,2)-(4,1,2)\big) - \big((5,2,2)-(5,0,2)\big)\f$ + +\subsubsection secHDVFcochain Co-chain complex (for cohomology) + +In the "mathematical world", singular cohomology is defined on dual groups \f$C^q = C_q^* = \mathrm{Hom}(C_q,A)\f$ with coboundary morphisms: \f$\partial^q\,:\, C^q \to C^{q+1} = \partial_q^*\f$. + +However in our discrete setting of finite complexes, a space and its dual can be identified. Hence cohomology can be defined in a more intuitive way: + +\f\[C^* = \cdots C^{q+1} \xleftarrow{\partial^{q}} C^q \xleftarrow{\partial^{q-1}} C^{q-1} \leftarrow \cdots \leftarrow C^2 \xleftarrow{\partial^1} C^1 \xleftarrow{\partial^0} C^0\f\] + +- **cochain groups** \f$C^q\f$ for any \f$0\leqslant q \leqslant d\f$ are identified with \f$C_q\f$. Hence chains and cochains are similar. +- a sequence of morphisms between chain groups, called **coboundary morphisms** \f$\partial^q\,:\,C^q\to C^{q+1}\f$ for any \f$0\leqslant q \leqslant d\f$ with \f$\partial^q = \partial_q^t\f$ (thus, the matrix of the coboundary operator is the transpose of the boundary operator matrix). The coboundary operator "mimicks" algebraically the geometric coboundary (ie.\ cofaces) and satisfies: +\f\[\partial^q\partial^{q-1} = 0\f\] + +The following figure illustrates the coboundary morphism + + \cgalFigureBegin{HDVFcoboundary,simplicial_complex_coboundary.png} + Illustration of the coboundary morphism (with \f$A = \mathbb Z\f$) **Pink:** coboundary of vertex \f$v_6\f$ (\f$\partial^0(v_6) = e_{26} + e_{46}+e_{56} - e_{67} - e_{68}\f$); **Blue:** coboundary of edge \f$e_{24}\f$ (\f$\partial^1(e_{24} ) = f_{246} - f_{234}\f$). + \cgalFigureEnd + +\subsection secHDVFhomology Homology of discrete chain complexes + +Homology is a topological invariant characterizing the holes of an object of various dimension. What is a hole? +- holes of dimension 0 are connected components +- holes of dimension 1 are tunnels or handles of the object +- holes of dimension 2 are cavities + + \cgalFigureBegin{HDVFholes_example,holes_example.png} + Illustration of the holes of a double torus. **Black:** 1 connected component (dimension 0); **Red:** 4 holes of dimension 1; **Blue** 1 cavity of dimension 2. + \cgalFigureEnd + +Let us now come back to our simplicial complex example: + \cgalFigureBegin{HDVFsimplicial_cycles_example,simplicial_complex_homology.png} + Cycles on a simplicial complex. **Blue:** this cycle is the boundary of \f$f_{234}+f_{246}\f$; **Pink:** this cycle is not a boundary. + \cgalFigureEnd + +This example suggests that holes are delineated by *cycles which are not a boundaries*: +- **Cycles** of dimension \f$q\f$ are chains in the kernel of \f$\partial_q\f$: +\f\[Z_q(C) = \ker(\partial_q) \f\] +- **Boundaries** of dimension \f$q\f$ are chains in the image of \f$\partial_{q+1}\f$ +\f\[B_q(C) = \mathrm{Im}(\partial_{q+1}) \f\] +- As \f$\partial_{q}\partial_{q+1} = 0\f$, \f$B_q(C)\subseteq Z_q(C)\f$. +- Hence we can define the quotient \f$Z_q(C)/B_q(C)\f$ which contains exactly "cycles which are not boundaries", ie.\ the \f$q\f$th homology group: +\f\[H_q(C) = Z_q(C)/B_q(C) \f\] + +For finite complexes, these groups are finitely generated and we have the following decomposition (theorem of decomposition of finitely generated groups): +\f\[H_q(C) = A^{\beta_q}\oplus A/\lambda_1A\oplus \cdots \oplus A/\lambda_k A \f\] +where: +- \f$\beta_q\in\mathbb N\f$ is called the **\f$q\f$th Betti number** (giving the number of holes of dimension \f$q\f$); +- coefficients \f$\lambda_i\f$ are called the **torsion coefficients in dimension \f$q\f$** and satisfy \f$\lambda_i |Ā \lambda_{i+1}\f$. + +Let us assume homology groups have no torsion; homology can be computed "at different levels": +- **level 1:** Computing Betti numbers. +- **level 2:** Computing \f$\beta_q\f$ cycles generating \f$H_q(C)\f$ called **generators** (hence cycles "delineating" the holes). + +Most libraries focus on persistent homology computation for topological data analysis (see section \ref secHDVFpersistent_homology), which is a "level 1" homology computation. +The **HDVF package is dedicated to a "level 2" homology computation**, which is computationally more complex but provides "geometric" informations about holes (generators, annotations...). + +\subsection secHDVFcohomology Cohomology of discrete chain complexes + +Starting from a cochain complex: + +\f\[C^* = \cdots C^{q+1} \xleftarrow{\partial^{q}} C^q \xleftarrow{\partial^{q-1}} C^{q-1} \leftarrow \cdots \leftarrow C^2 \xleftarrow{\partial^1} C^1 \xleftarrow{\partial^0} C^0\f\] + +one can define: +- **Cocycles** of dimension \f$q\f$ are (co)chains in the kernel of \f$\partial^q\f$: +\f\[Z^q(C^*) = \ker(\partial^q) \f\] +- **Coboundaries** of dimension \f$q\f$ are chains in the image of \f$\partial^{q-1}\f$ +\f\[B^q(C^*) = \mathrm{Im}(\partial^{q-1}) \f\] +- As \f$\partial^{q}\partial^{q-1} = 0\f$, \f$B^q(C^*)\subseteq Z^q(C^*)\f$. +- Hence we can define the quotient \f$Z^q(C^*)/B^q(C^*)\f$, ie.\ the \f$q\f$th cohomology group: +\f\[H^q(C^*) = Z^q(C^*)/B^q(C^*) \f\] + + \cgalFigureBegin{HDVFex_hom_cohom,HDVF_ex_hom_cohom.png} + Illustration of homology and cohomology generators for a simple 2D cubical complex \f$C\f$ (computed with HDVFs). **Left:** both generators of \f$H_1(C)\f$; **Middle, right:** both cohomology generators \f$H^1(C)\f$ (for convenience, the dual of cohomology generators has been displayed with bulleted pink lines). + \cgalFigureEnd + + \cgalFigureBegin{HDVFex_hom_cohom_cofaces,HDVF_ex_hom_cohom_cofaces.png} + Cofaces representation of cohomology generators. + \cgalFigureEnd + +Figure \cgalFigureRef{HDVFex_hom_cohom} and \cgalFigureRef{HDVFex_hom_cohom_cofaces} illustrate homology and cohomology generators in a simple cubical complex. Actually: + - homology generators, intuitively delineate holes; + - cohomology generators provide cocycles to open or break the holes (therefore, it is sometimes more convenient to display the cofaces of cohomology generators as in figure \cgalFigureRef{HDVFex_hom_cohom_cofaces}). + +Therefore homology and cohomology provide isomorphic (and dual) results. Actually, the following proposition holds in finite dimension: +\f\[H^q(C^*) \sim H_q(C)\f\] + + +\subsection secHDVFcompHomology Computing homology and cohomology + +First results on the computation of discrete homology and cohomology date back to the 80's (see for instance [Munkres]). They are based on a diagonalisation of boundary matrices (called Smith Normal form). +Nowadays, computational (co)homology approaches can be classified into three branches illustrated in figure \cgalFigureRef{HDVFoverview_comput_hom}. + + \cgalFigureBegin{HDVFoverview_comput_hom,HDVFoverview_comput_hom.png} + Overview of approaches in computational homology. HDVFs are in-between discrete Morse theory and effective homology (and they encompass persistent homology, relative homology and tri-partitions). + \cgalFigureEnd + +\subsubsection secHDVFreduction Effective homology and reductions + +Effective homology considers homology computations from a categorical perspective ; its main results are theorems building functors providing a computation of homology for infinite spaces (locally effective spaces) starting from finite ones (effective spaces). + +Among the tools of effective homology, **reductions** characterise how to map a (large) chain complex onto a (small) one with isomorphic homology groups. Formally, a reduction from a chain complex \f$C\f$ to a chain complex \f$C'\f$ is a triple of graded maps \f$(f,g,h)\f$: +\f\[\begin{array}{cccccc} +\cdots & C_{q+1} & \substack{\xrightarrow{\partial_{q+1}}\\ \xleftarrow[h_{q}]{}} & C_{q} & \substack{\xrightarrow{\partial_{q}}\\ \xleftarrow[h_{q-1}]{}} & \cdots \\ +& f_{q+1}\Big\downarrow \Big\uparrow g_{q+1} & & f_{q}\Big\downarrow \Big\uparrow g_{q} & & \\ +\cdots & C'_{q+1} & \xrightarrow{\partial'_{q+1}} & C'_{q} & \xrightarrow{\partial'_{q}} & \cdots \\ +\end{array} +\f\] +such that: + +- \f$f\f$ and \f$g\f$ are chain maps (ie.\ commute with \f$\partial\f$ and \f$\partial'\f$) +- \f$f_q g_q = \mathrm{Id}_{C'_q}\f$ for any \f$q\f$ +- \f$g_q f_q = \mathrm{Id}_{C_q} - \partial_{q+1} h_q - h_q \partial_{q-1}\f$ for any \f$q\f$ +- \f$h_{q+1} h_q = 0\f$, \f$f_{q+1} h_{q} = 0\f$ and \f$h_{q} g_q = 0\f$ for any \f$q\f$ + + +\f$C'\f$ is called the **reduced complex** and \f$\partial'\f$ the **reduced boundary**. + +Under these assumptions, the homology and cohomology groups of \f$C\f$ and \f$C'\f$ are isomorphic. + +A reduction is **perfect** when the reduced boundary is null; then for any \f$q\f$: +\f\[H_q(C) \sim g(C'_q)\f\] +in other words, homology groups are isomorphic to the image of the reduced complex by \f$g\f$, +\f\[H^q(C) \sim f^*(C'_q)\f\] +and cohomology groups are isomorphic to the image of the reduced complex by \f$f^*\f$. + +\subsubsection secHDVFdiscrete_Morse A few words about discrete Morse theory + +Discrete Morse theory was introduced by Forman in the 90's as a combinatorial approach for homology computation directly inspired by Morse theory (on compact manifolds). +He showed that in the discrete context, Morse functions are better defined through their gradient: a vector field defined over the complex connecting cells and one of their co-faces. In this context, gradients of Morse functions can be easily characterized by acyclicity conditions. + +More precisely, given a complex \f$K\f$, a **discrete gradient vector field** (called DGVF here after) can be built as follows: +- consider the Hasse diagram of the complex (connecting cells to their boundary - figure (left)) + +\subsection secHDVFpersist_hom Persistent homology + +\subsection secHDVFrelative_hom Relative homology + +\subsection secHDVFAlexander Alexander duality + +\section secHDVF The HDVF framework + +This section presents a brief introduction to HDVFs, see [AGL, 2016] for more details. + +\subsection secHDVFHDVF_definitions Definition of HDVFs + +HDVFs are an "in-between" theory: +- in-between Discrete Morse theory (combinatorial approach) and effective homology (categorical approach) +- in-between combinatorics and algebra +but they cannot be reduced to any of these frameworks. As we will see, they provide a bridge in-between them. + +Let \f$K\f$ be a complex (simplicial, cubical...) and let \f$(\mathscr C, \partial)\f$ be its associated chain complex. + +**Notation** Starting from now on, we will use \f$\mathscr C\f$ for chain complexes and \f$C\f$ for critical cells. + +Formally a **HDVF** \f$X=(P,S,C)\f$ is a partition of the cells of \f$K\f$: + \f\[K = P\cup S\cup C\f\] + with \f$P\f$ primary cells, \f$S\f$ secondary cells and \f$C\f$ critical cells, such that the sub-matrix: + \f\[\partial (S)|_P\text{ is invertible.} + \f\] +where \f$\partial (S)|_P\f$ means that the matrix of \f$\partial\f$ is restricted to columns of secondary cells and rows of primary cells. + +As stated earlier, this definition is both combinatorial (partition of cells) and algebraic (invertibility of a sub-matrix) but it cannot be simplified or reduced to either of these approaches. + +Given such an HDVF \f$X=(P,S,C)\f$, we can define the following associated reduction \f$(f,g,h)\f$: + +\f\[\begin{array}{cccccc} +\cdots & \mathscr C_{q+1} & \substack{\xrightarrow{\partial_{q+1}}\\ \xleftarrow[h_{q}]{}} & \mathscr C_{q} & \substack{\xrightarrow{\partial_{q}}\\ \xleftarrow[h_{q-1}]{}} & \cdots \\ +& f_{q+1}\Big\downarrow \Big\uparrow g_{q+1} & & f_{q}\Big\downarrow \Big\uparrow g_{q} & & \\ +\cdots & C_{q+1} & \xrightarrow{\mathrm d_{q+1}} & C_{q} & \xrightarrow{\mathrm d_{q}} & \cdots \\ +\end{array} +\f\] +\f$\mathrm d\f$ is the **reduced boundary** (over critical cells) and the reduction is defined as follows: + +- the matrix of \f$h_q\f$ is: +\f\[\begin{array}{c} +\ \ \ \ \ \ \ \ \ \ P_q \ \ \ \ S_q \ \ \ \ C_q \\ +\begin{array}{c} +P_{q+1} \\ +S_{q+1} \\ +C_{q+1} \\ +\end{array} +\begin{array}{|c|c|c|} +\hline +0 & 0 & 0 \\ +\hline +H_q & 0 & 0 \\ +\hline +0 & 0 & 0 \\ +\hline +\end{array} +\end{array} +\f\] +with \f\[H_q = (\partial (S_{q+1})|_{P_q})^{-1}\f\]. + +- the matrix of \f$f_q\f$ is: +\f\[\begin{array}{c} +\ \ \ \ \ \ P_q \ \ \ \ S_q \ \ \ \ C_q \\ +\begin{array}{c} +C_q \\ +\end{array} +\begin{array}{|c|c|c|} +\hline +F_q & 0 & \mathrm{Id} \\ +\hline +\end{array} +\end{array} +\f\] +with +\f\[ F_q = - \partial(S_{q+1})|_{C_q}\cdot H_q\f\] + +- the matrix of \f$g_q\f$ is: +\f\[\begin{array}{c} +\ \ \ \ \ \ C_q \\ +\begin{array}{c} +P_q \\ +S_q \\ +C_q \\ +\end{array} +\begin{array}{|c|} +\hline +0 \\ +\hline +G_q \\ +\hline +\mathrm{Id} \\ +\hline +\end{array} +\end{array} +\f\] +with +\f\[ G_q = - H_{q-1} \cdot \partial(C_{q})|_{P_{q-1}}\f\] + +- last, the matrix of the reduced boundary \f$\mathrm d_q\f$ is: +\f\[\begin{array}{c} +\ \ \ \ \ \ C_q \\ +\begin{array}{c} +C_{q-1} \\ +\end{array} +\begin{array}{|c|} +\hline +DD_q \\ +\hline +\end{array} +\end{array} +\f\] +with +\f\[ D_q = \partial(C_{q})|_{C_{q-1}} + F_{q} \cdot \partial(C_{q})|_{P_{q-1}} = \partial(C_{q})|_{C_{q-1}} + \partial(S_{q})|_{C_{q-1}} \cdot G_q \f\] + +Therefore the HDVF "reduces" the complex to a smaller complex over critical cells. + +A HDVF is called **perfect** when the reduced boundary is null, then for any \f$q \in \{0\ldots d\}\f$: +- \f$\beta_q = | C_q |\f$ +- Homology generators of dimension \f$q\f$ are \f$\{g(\sigma) \,;\, \sigma\in C_q \}\f$ +- Cohomology generators of dimension \f$q\f$ are \f$\{f^*(\sigma) \,;\, \sigma\in C_q \}\f$ + +Hence a perfect HDVF computes homology and provides a rich homological information. +- when \f$A\f$ is a field, perfect HDVFs always exist and any HDVF can be completed into a perfect HDVF (see \ref secHDVFcomput) +- when \f$A\f$ is a ring: + - if the torsion part is null, we hypothesise that a perfect HDVF always exist + - with torsion, we hypothesise that a torsion-perfect HDVF always exist (where the reduced boundary is limited to torsion) +Both questions are still an open and we have no counter-examples. + +Moreover, the reduction provides various informations on cycles and co-cycles: +- An **annotation** associates to a cycle its decomposition in the basis of homology generators, up to boundaries:
+Given \f$\gamma\in Z_q(\mathscr C)\f$, \f$f(\gamma)\f$ is an annotation of \f$\gamma\f$: +\f\[\text{if }f(\gamma) = \sum_i \alpha_i\cdot \sigma_i,\ \ \ \sigma_i\in C_q\f\] +\f\[\text{then }\ \ \ \gamma = \sum_i \alpha_i\cdot g(\sigma_i) + \beta,\ \ \ \beta \in B_q(\mathscr C)\f\] +- A **co-annotation** associates to a cycle its decomposition in the basis of cohomology generators, up to coboundaries:
+Given \f$\gamma\in Z^q(\mathscr C)\f$, \f$g^*(\gamma)\f$ is a co-annotation of \f$\gamma\f$: +\f\[\text{if }g^*(\gamma) = \sum_i \alpha_i\cdot \sigma_i,\ \ \ \sigma_i\in C^q\f\] +\f\[\text{then }\ \ \ \gamma = \sum_i \alpha_i\cdot f^*(\sigma_i) + \beta,\ \ \ \beta \in B^q(\mathscr C)\f\] + +Annotations and co-annotations provide convenient tests to decide if to cycles (resp. co-cycles) belong to the same homology (resp. cohomology) class: +- Two cycles \f$\gamma\f$ and \f$\gamma'\f$ belong to the same homology class if they have the same annotation (ie.\ \f$f(\gamma) = f(\gamma')\f$) +- Two co-cycles \f$\gamma\f$ and \f$\gamma'\f$ belong to the same cohomology class if they have the same co-annotation (ie.\ \f$g^*(\gamma) = g^*(\gamma')\f$) + +Figure \cgalFigureRef{HDVFex_hom_cohom} shows an example of HDVF together with corresponding homology and cohomology generators. In all the package, we use the following colors convention: +- PRIMARY cells are depicted in green +- SECONDARY cells are depicted in red +- CRITICAL cells are depicted in blue + +Let us point out that HDVFs can be represented in a "discrete Morse theory" style, with arrows (ie.\ a vector field) pairing a primary and a secondary cell among its co-faces (the vector field associated to a HDVF is not unique). But DGVF are a strict subset of HDVFs: +- each DGVF is a HDVF (labelling sources of arrows as PRIMARY, targets of arrows as SECONDARY, unpaired cells as CRITICAL always produces an HDVF) +- but HDVF may produce vector fields with cycles + +The following figures show an HDVF and an associated vector field:
+ + + + And the following figure exhibits the vector field representation of a HDVF with a cycle (on the right, the cycle is highlighted in purple):
+ +
+ Hence this vector field is a valid HDVF (\f$\partial(S)|_P\f$ is invertible) but it is not a DGVF. + +\subsection secHDVFcomput Computing and deforming a HDVF + +HDVFs can be computed using previous matrix expression; however, Aldo Gonzalez-Lorenzo has shown that several operations on HDVF can be introduced, not only to built, but also transform HDVFs. + +- **A operation** (pairing of two critical cells)
+Given a HDVF \f$X=(P,S,C)\f$, and two CRITICAL cells \f$\gamma_1\in C_q\f$, \f$\gamma_2\in C_{q+1}\f$ +\f\[ A(X) = \Big(P\cup\{\gamma_1\}, S\cup\{\gamma_2\}, C\backslash \{\gamma_1, \gamma_2\}\Big)\f\] +and the operation is valid (ie.\ produces an HDVF) if and only if: +\f\[ \langle \mathrm d(\gamma_2),\gamma_1\rangle \text{ is invertible}\f\] + + - Any perfect HDVF can be built by iterating the A operation (with complexity at most \f$\mathscr O(n^3))\f$) + - This operation can also complete a HDVF into a perfect HDVF + + Figures below illustrate this operation on a cubical chain complex: (left) an HDVF such that \f$(\gamma_1,\gamma_2)\f$ meet the A condition, (right) the HDVF obtained after \f$A(\gamma_1,\gamma_2)\f$. + + + + +- **R operation** (unpairing of two critical cells)
+Given a HDVF \f$X=(P,S,C)\f$, a PRIMARY cell \f$\pi\in P_q\f$ and a SECONDARY cells \f$\sigma\in S_{q+1}\f$ +\f\[ R(X) = \Big(P\backslash\{\pi\}, S\backslash\{\sigma\}, C\cup \{\pi, \sigma\}\Big)\f\] +and the operation is valid (ie.\ produces an HDVF) if and only if: +\f\[ \langle h(\pi),\sigma\rangle \text{ is invertible}\f\] + + Operations A and R are inverse one another. + +- **M operation** (exchange a PRIMARY and a CRITICAL cell)
+Given a HDVF \f$X=(P,S,C)\f$, a PRIMARY cell \f$\pi\in P_q\f$ and a CRITICAL cell \f$\gamma_1\in C_{q}\f$ +\f\[ M(X) = \Big(P\backslash \{\pi\} \cup\{\gamma\}, S, C\backslash \{\gamma\}\cup \{\pi\}\Big)\f\] +and the operation is valid (ie.\ produces an HDVF) if and only if: +\f\[ \langle \mathrm f(\pi),\gamma\rangle \text{ is invertible}\f\] + + The M operation modifies the homology generator associated to \f$\gamma\f$ (while preserving is associated cohomology generator). + The following figure illustrates the M operation. + + \cgalFigureBegin{HDVF_M,HDVF_M.png} + M operation on a cubical complex \f$K\f$. **Left:** perfect HDVF over \f$K\f$ with cells \f$(\pi, \gamma)\f$ meeting the M operation condition (blue: homology generator associated with \f$\gamma\f$, pink: cohomology generator associated with \f$\gamma\f$); **Right:** perfect HDVF obtained with \f$M(\pi,\gamma)\f$ and updated generators (associated to \f$\pi\f$ which is now critical). + \cgalFigureEnd + + - **W operation** (exchange a SECONDARY and a CRITICAL cell)
+Given a HDVF \f$X=(P,S,C)\f$, a SECONDARY cell \f$\sigma\in S_q\f$ and a CRITICAL cell \f$\gamma_1\in C_{q}\f$ +\f\[ W(X) = \Big(P, S\backslash\{\sigma\}\cup \{\gamma\}, C\backslash \{\gamma\}\cup \{\sigma\}\Big)\f\] +and the operation is valid (ie.\ produces an HDVF) if and only if: +\f\[ \langle \mathrm g(\gamma),\sigma\rangle \text{ is invertible}\f\] + + The W operation modifies the cohomology generator associated to \f$\gamma\f$ (while preserving is associated homology generator). + The following figure illustrates the W operation. + + \cgalFigureBegin{HDVF_W,HDVF_W.png} + W operation on a cubical complex \f$K\f$. **Left:** perfect HDVF over \f$K\f$ with cells \f$(\sigma, \gamma)\f$ meeting the W operation condition (blue: homology generator associated with \f$\gamma\f$, pink: cohomology generator associated with \f$\gamma\f$); **Right:** perfect HDVF obtained with \f$W(\pi,\gamma)\f$ and updated generators (associated to \f$\sigma\f$ which is now critical). + \cgalFigureEnd + +- **MW operation** (exchange a PRIMARY and a SECONDARY cell)
+ Given a HDVF \f$X=(P,S,C)\f$, a PRIMARY cell \f$\pi\in P_q\f$ and a SECONDARY cell \f$\sigma\in S_{q}\f$ + \f\[ MW(X) = \Big(P\backslash\{\pi\}\cup \{\sigma\}, S\backslash\{\sigma\}\cup \{\pi\}, C\Big)\f\] + and the operation is valid (ie.\ produces an HDVF) if and only if: + \f\[ \langle \mathrm dh(\pi),\sigma\rangle \text{ and } \langle h\mathrm d(\sigma),\pi\rangle\text{ are invertible}\f\] + + The MW operation modifies both homology and cohomology generators. + The following figure illustrates the W operation. + + \cgalFigureBegin{HDVF_MW,HDVF_MW.png} + MW operation on a cubical complex \f$K\f$. **Top:** perfect HDVF over \f$K\f$ with cells \f$(\pi_1, \sigma_1)\f$ and \f$(\pi_2, \sigma_2)\f$ meeting the MW operation condition (blue: homology generators, pink: cohomology generators); **Down, left:** perfect HDVF obtained with \f$MW(\pi_1,\sigma_1)\f$ and updated generators (a cohomology generator is modified); **Down, right:** perfect HDVF obtained with \f$MW(\pi_2,\sigma_2)\f$ and updated generators (a homology generator is modified). + \cgalFigureEnd + +Recent works by Yann-Situ Gazull [YSG1], [YSG2] proved that (when \f$A\f$ is a field): +- the space of perfect HDVFs is connected under M, W and MW operations (see [YSG2]) +- homology bases computed by HDVFs and other computational approaches (Smith Normal Form, persistent homology, tripartitions...) can be precisely characterised as **explicit basis** (see [YSG1]) +- any perfect HDVF can be adapted in time \f$\mathscr O(kn^2)\f$ to compute a free set of generators of size \f$k\f$ (e.g. a given basis) + +These results will soon be implemented in the HDVF package. + +\subsection secHDVFpersistent_homology Persistent homology + +\subsection secHDVFreduced_Alexander Relative homology and Alexander duality + +\section secHDVFHDVFpackage The HDVF package + +This section describes the HDVF package implementing the theory described in previous sections. + +In order to compute the homology/cohomology of a discrete object with an HDVF, the process is as follows: +-# Define the ring of coefficients type +-# Build a chain complex from one of the models of `AbstractChainComplex` (`Geometric_simplicial_chain_complex`, `Abstract_simplicial_chain_complex` or `Cubical_chain_complex`) +-# Build a HDVF from this chain complex +-# Compute a perfect HDVF (fast mode or random mode) + + \cgalFigureBegin{HDVFoverview_HDVF,HDVFoverview_HDVF.png} + Overview of homology/cohomology computation with the HDVF package. + \cgalFigureEnd + +\subsection secHDVFCoefficientType Defining the ring of coefficients + +The ring of coefficients must be a model of the `IntegralDomainWithoutDivision` concept: +- the ring \f$\mathbb Z\f$ +\code + typedef int CoefficientType ; +\endcode +- a field \f$\mathbb Z/p\mathbb Z\f$ +\code + #include + typedef Zp

CoefficientType ; +\endcode +with \f$p\f$ a given prime number. +- the field \f$\mathbb Z/2\mathbb Z\f$ (the class`Zp` can also be used, but `Z2`is optimized for \f$p=2\f$) +\code + #include + typedef Z2 CoefficientType ; +\endcode + +\subsection secHDVFChainComplex Building a chain complex + +The HDVF package provides several chain complexes: `Abstract_simplicial_chain_complex`, `Geometric_simplicial_chain_complex` and `Cubical_chain_complex`. + +In order to build such a chain complex, discrete objects are first imported into a temporary combinatorial structure storing input cells. The chain complex is then built from this intermediate structure by adding all the faces of cells and computing the corresponding boundary matrix. + +Let us point out that whatever the type of chain complex, cells are indexed along each dimension; hence a cell is identified by an index and a dimension. + +- `Abstract_simplicial_chain_complex` (see section \ref secHDVFsimplicial_chain_complex) are built using the `Mesh_object_io` intermediate class from abstract simplices (vertices have no coordinates). Therefore, such complexes are usually computed from `.simp` files (a simple file format enumerating on each line vertices of simplices, see section secHDVFsimpFile): + \code + using Complex = Abstract_simplicial_chain_complex ; + Mesh_object_io mesh ; + mesh.read_simp(filename); + Complex complex(mesh); + \endcode +- `Simplicial_chain_complex` are also built using the `Mesh_object_io` intermediate structure to import mesh files (`.off` format): + \code + using Complex = Simplicial_chain_complex ; + Mesh_object_io mesh ; + mesh.read_off(filename); + Complex complex(mesh); + \endcode +- `Cubical_chain_complex` are built using the `Cub_object_io` intermediate structure by importing either simple `.cub` files (enumerating Khalimsky coordinates of cells, see section \ref secHDVFcubFile) or `.pgm` files. + - For `.cub` files enumerating Khalimsky coordinates, cells are imported and their faces are added to produce a proper complex. + \code + using Complex = Cubical_chain_complex ; + Cub_object_io mesh ; + mesh.read_cub(filename, true); + Complex complex(mesh); + \endcode + - For `.pgm` files (ie.\ for binary images or volumes), two constructions are available for cubical complexes: + - PRIMAL, encoding \f$3^d-1\f$-adjacency (where cells are imported as such, see figure \cgalFigureRef{HDVF_primal_dual}, middle) + \code + using Complex = Cubical_chain_complex ; + Cub_object_io mesh ; + // Read pgm file (store Khalimsky coordinates) + mesh.read_pgm(filename, true); + // Build the PRIMAL complex associated + Complex complex(mesh, CGAL::Homological_discrete_vector_field::Complex::PRIMAL); + \endcode + - DUAL, encoding \f$2d\f$-adjacency (where cubes produce vertices... see figure \cgalFigureRef{HDVF_primal_dual} right) + \code + using Complex = Cubical_chain_complex ; + Cub_object_io mesh ; + // Read pgm file (store voxel coordinates) + mesh.read_pgm(filename, false); + // Build the DUAL complex associated + Complex complex(mesh, CGAL::Homological_discrete_vector_field::Complex::DUAL); + \endcode + +\cgalFigureBegin{HDVF_primal_dual,HDVF_primal_dual.png} +Primal and dual constructions of cubical chain complexes from a binary volume. **Left:** a 3D binary volume; **Middle:** PRIMAL cubical (chain) complex associated to the volume; **Right:** DUAL cubical (chain) complex associated to the volume).
+ Figure taken from [AGL, 2016]. +\cgalFigureEnd + +\subsubsection secHDVFsimpFile The `.simp` file format + +The `simp` file format is a very simple text format listing, encoding simplicial complexes. It enumerates simplices line by line (described by the indices of their vertices). +The file doesn't need to provide an exhaustive enumeration of simplices (with all their faces), indeed, the simplicial chain complex constructors fill them with faces. Figure \cgalFigureRef{HDVF_simp_file} illustrate the format on a simple simplicial complex. + +\cgalFigureBegin{HDVF_simp_file,HDVF_simp_file.png} +Example of `simp` file. **Left:** a simplicial complex; **Right:** associated `simp` files describing maximal simplices. +\cgalFigureEnd + +\subsubsection secHDVFcubFile The `.cub` file format + +The `cub` file format is a very simple text format encoding binary volumes: +- first line: dimension +- second line: size of Khalimsky coordinates of the bounding box along each axis +- following lines: list of cells in Khalimsky coordinates + +The file doesn't need to provide an exhaustive enumeration of cells (with all their faces), indeed, the cubical chain complex constructors fill them with faces. Figure \cgalFigureRef{HDVF_cub_file} illustrate the format on a simple cubical complex. + +\cgalFigureBegin{HDVF_cub_file,HDVF_cub_file.png} +Example of `cub` file. **Left:** a cubical complex; **Right:** associated `cub` files describing maximal cells. +\cgalFigureEnd + +\subsection secHDVFbuild Building and computing a HDVF + +An empty HDVF is built from a chain complex: +\code + using HDVF_type = Hdvf ; + HDVF_type hdvf(complex, OPT_FULL) ; +\endcode + +Several HDVF options are available: +- `OPT_D`: fastest option - compute only the reduced boundary \f$\mathrm d\f$ (under this option, HDVFs provide only low level homological information: Betti numbers and critical cells +- `OPT_F`, `OPT_G`: compute the reduction partially (only \f$f\f$ for cohomology generators or \f$g\f$ for homology generators) +- `OPT_FULL`: compute the full reduction + +\subsubsection secHDVFcomputePerfect Computing a perfect HDVF automatically + +The HDVF obtained is empty (all cells are critical). In order to compute homology/cohomology that is a perfect HDVF, the package provides two methods: +- `compute_perfect_hdvf` (fastest method: cells are paired using the A operation by decreasing dimension using the first valid pair) +- `compute_rand_perfect_hdvf` (slowest: compute all valid pairs for the A operation are select one randomly) + +\code + hdvf.compute_perfect_hdvf(); +\endcode +or +\code + hdvf.compute_rand_perfect_hdvf(); +\endcode + +When the ring of coefficients is a field, these methods always produce a perfect HDVF. However for \f$\mathbb Z\f$-(co)homology (without torsion), the existence of complexes admitting no perfect HDVF is still an open question (and the answer is negative for "reasonable" objects, indeed we still have no counter example). +The method `is_perfect_hdvf` checks if a HDVF is perfect. + +\subsubsection secHDVFcomputePerfectA Computing a perfect HDVF with A operations + +For specific applications, HDVFs can be built by iterating the A operation: +\code + hdvf.A(id1, id2, q) ; +\endcode +performs a pairing between the cells \f$\gamma_1\f$ (index `id1` in dimension `q`) and \f$\gamma_2\f$ (index `id2` in dimension `q+1`). + +The package provide a family of methods to search for valid pairings: +\code + Cell_pair pair; + std::vector pairs; + bool found; + + // Search for the first valid pair in dimension `q` + pair = find_pair_A(q, found); + // Search for the first cell (of dimension `q-1` or `q+1`) forming a valid pair with tau (index `id`, dimension `q`) + pair = find_pair_A(q, found, id); + // Search for all valid pairs in dimension `q` + pairs = find_pairs_A(q, found); + // Search for all cells (of dimension `q-1` or `q+1`) forming a valid pair with tau (index `id`, dimension `q`) + pairs = find_pairs_A(q, found, id); +\endcode + +\subsection secHDVFoutput Extracting and saving homological/cohomological information + +\warning All `vtk` exports are only available for geometric chain complexes (ie.\ `Simplicial_chain_complex` and `Cubical_chain_complex`). + +How to extract (co)homological information then? Various solutions and output are available: +- **HDVF extraction / visualization**: + The method `psc_labels` extracts cells flags in any dimensions. The complex annotated with labels can then be exported to `vtk` for visualization. The `vtk` output includes a `Label` property encoding the HDVF PSC_flag (-1 for PRIMARY, 1 for SECONDARY and 0 for CRITICAL cells), and a `CellId` property storing cells indices: + \code + // Labels extraction + vector > labels = hdvf.psc_labels() ; + // VTK export + Complex::chain_complex_to_vtk(complex, filename, &labels) ; + \endcode +- **Betti numbers**: + For a perfect HDVF, the Betti number in dimension \f$q\f$ equals \f$|C_q|\f$, the number of critical cells. + Critical cells can be obtained: + - either in all dimensions: + \code + std::vector > criticals = hdvf.psc_flags(CGAL::Homological_discrete_vector_field::CRITICAL); + \endcode + - or in a given dimension `q`: + \code + std::vector criticals_q = hdvf.psc_flags(CGAL::Homological_discrete_vector_field::CRITICAL, q) ; + \endcode +- **Homology/cohomology generators** + Each critical cell \f$\gamma\f$ bears an associated homology generator: \f$g(\gamma)\f$ and cohomology generator: \f$f^*(\gamma)\f$. + - Generators associated to \f$\gamma\f$ (index `id`, dimension `q`) can be extracted as column chains (ie.\ column `Sparse_chain`): + \code + HDVF_type::Column_chain hom_generator(hdvf.homology_chain(id, q)) ; + HDVF_type::Column_chain cohom_generator(hdvf.cohomology_chain(id, q)) ; + \endcode + These chains can be visited as described in section \ref secHDVFmatrixChain. + - The HDVF package also provides a vtk exporter for such chains (cells with a non zero coefficient are exported). For instance: + \code + Complex::chain_to_vtk(complex, filename, hom_generator, q, id) ; + \endcode + If `id` is provided, a `Label` integer property is added to the vtk file (0 for the cell `id`, 2 for other cells). +- **Reduction maps** + Matrices of the reduction (\f$F\f$, \f$G\f$, \f$H\f$ and \f$D\f$) can be also obtained using `matrix_f`, `matrix_g`, `matrix_h` and `matrix_dd`. +- **Automated vtk export** + In order to ease `vtk` export, an "automated" method `CGAL::IO::write_VTK` is also provided, exporting in distinct files, the HDVF flags and all the generators computed (homology / cohomology depending of the HDVF option): + \code + CGAL::IO::write_VTK(hdvf, complex, outfile_root, co_faces) ; + \endcode + where `outfile_root` is the root of the `.vtk` file names and `co_faces` is a %Boolean (if `true`, cohomology generators are represented through their cofaces). +- HDVFs and reductions can also be saved / loaded to `.hdvf` files (a simple text file format described in section \ref secHDVFHDVF_format) with `insert_hdvf_reduction()` and `extract_hdvf_reduction()`. + +\subsubsection secHDVFHDVF_format The `.hdvf` file format + +The `.hdvf` file format is a simple text format used to save HDVFs. It relies on the `.osm` format described in section \ref secHDVFOSM_format. + +\warning The file format does not store the underlying complex, therefore the dimension and the number of cells in each dimension are used to check the coherence of the `.hdvf` file with the underlying complex provided by the user. + +- HDVF type (0 for HDVF and reduction, 1 for HDVF only) +- Dimension of the complex +- List of the number of cells in each dimension (one dimension by row) +- HDVF option (OPT_BND | OPT_F | OPT_G | OPT_FULL) +- List of the flags of cells in each dimension (one dimension by row: -1 for PRIMARY, 1 for SECONDARY, 0 for CRITICAL) +- According to HDVF option: output F matrices in each dimension (`.osm` format) +- According to HDVF option: output G matrices in each dimension (`.osm` format) +- Output H matrices in each dimension (`.osm` format) +- Output DD matrices in each dimension (`.osm` format) + +\subsection secHDVFexHDVF HDVF examples + +The following examples exhibit the computation of a perfect HDVF over a simplicial (resp. cubical) chain complex built from a `.off` file (resp. a `.pgm` file). Generators are then exported to `.vtk`. +\cgalExample{HDVF/hdvf_simplicial.cpp} +\cgalExample{HDVF/hdvf_cubical.cpp} + +\subsection secHDVFmatrixChain A few words about sparse matrices and chains + +All maps (boundary operator, \f$f\f$, \f$g\f$, \f$h\f$ and \f$\mathrm d\f$) are stored in sparse matrices and chains are stored in sparse chains. +The `OSM` library provides an implementation of the `SparseMatrix` and `SparseChain` concepts, that is sparse structures optimized for topological operations (block operations). + +The structure is based on a mapped matrix implementation, hence, matrices and chains are either column or row major (`CGAL::OSM::COLUMN` or `CGAL::OSM::ROW` type). + +In order to speed up both block operations and iterators (over non empty columns or row according to the type), a **bitboard** structure is used (inherited from chess programming community). Hence, in order to iterate over the major dimension of a matrix, one should actually iterate a bitboard (providing indices of non empty columns / rows). See `Sparse_matrix` documentation for more details. + +Iterating over sparse chains them comes to iterate over a map. + +The following example builds a column major matrix, iterate over its non empty columns (ie.\ chains) and then iterate over each chain. + +\cgalExample{HDVF/matrix_chain.cpp} + +All standard linear algebra operators are provided (and optimized according to the matrix/chain types). + +\subsubsection secHDVFOSM_format The `.osm` file format + +The `.osm` file format is a simple text format to store OSM sparse matrices: +- Matrix type (0 for COLUMN, 1 for ROW) +- Number of rows and number of columns (separated by a space) +- Number of coefficients +- List of coefficients (one by row):
+ `id_row id_col value` + +\subsection secHDVFoperations HDVF operations + +\subsection secHDVFannotations HDVFs and annotations + +\subsection secHDVFpersistentHom_package HDVFs and persistent homology + +\subsection secHDVFAlexander_package HDVFs, Alexander duality and relative homology + + + + [AGL, 2017] Aldo Gonzalez-Lorenzo, Alexandra Bac, Jean-Luc Mari, Pedro Real. Allowing cycles in discrete Morse theory, Topology and its Applications, Volume 228, 2017, Pages 1-35. + + [AGL, 2016] Aldo Gonzalez-Lorenzo. Computational Homology Applied to Discrete Objects. Discrete Mathematics [PhD]. Aix-Marseille Université; Universidad de Sevilla, 2016. + + [AGL, 2025] Aldo Gonzalez-Lorenzo, Alexandra Bac and Yann-Situ Gazull. A constructive approach of Alexander duality. J Appl. and Comput. Topology 9, 2 (2025). + + [YSG1, 2025] Yann-Situ Gazull, Aldo Gonzalez-Lorenzo and Alexandra Bac. Characterization of the computed homology and cohomology bases. DGMM 2025 (to appear). + [YSG1, 2025] Yann-Situ Gazull, Aldo Gonzalez-Lorenzo and Alexandra Bac. Characterization of the computed homology and cohomology bases. DGMM 2025 (to appear). + + [YSG2, 2025] Yann-Situ Gazull, Aldo-Gonzalez-Lorenzo and Alexandra Bac. Space of homological computations: connectivity and paths (submitted to Journal of Applied and Computational Topology). + + [Munkres] J. Munkres. Elements of Algebraic Topology, Addison Wesley Publishing Company, (1984) + +*/ +} /* namespace CGAL */ diff --git a/HDVF/doc/HDVF/PackageDescription.txt b/HDVF/doc/HDVF/PackageDescription.txt new file mode 100644 index 00000000000..d4699c87035 --- /dev/null +++ b/HDVF/doc/HDVF/PackageDescription.txt @@ -0,0 +1,47 @@ +// PRETTY HDVF should equal the project title in Doxyfile.in + +/// \defgroup PkgHDVFRef Homological Discrete Vector Fields Reference +/// \defgroup PkgHDVFConcepts Concepts +/// \ingroup PkgHDVFRef + +/// \defgroup PkgHDVFAlgorithmClasses Sparse Matrices and Vectors +/// \ingroup PkgHDVFRef + +/// \defgroup PkgHDVFTraitsClasses Traits Classes +/// \ingroup PkgHDVFRef + +/// \defgroup PkgHDVFIOClasses IO Classes +/// \ingroup PkgHDVFRef + + +/*! +\addtogroup PkgHDVFRef +\cgalPkgDescriptionBegin{Homological Discrete Vector Fields, PkgHDVF} +\cgalPkgPicture{HDVF-small.png} + +\cgalPkgSummaryBegin +\cgalPkgAuthors{Alexandra Bac} +\cgalPkgDesc{ +The package provides an implementation of Homological Discrete %Vector Fields, a framework computing homology and cohomology information (Betti numbers, generators, annotation functions...) for discrete objects (including persistent homology, Alexander duality isomorphism and reduced homology).} +\cgalPkgManuals{Chapter_HDVF,PkgHDVFRef} +\cgalPkgSummaryEnd + +\cgalPkgShortInfoBegin +\cgalPkgSince{6.2} +\cgalPkgBib{cgal:b-hdvf} +\cgalPkgLicense{\ref licensesGPL "GPL"} + +\cgalPkgShortInfoEnd + +\cgalPkgDescriptionEnd + +\cgalClassifedRefPages + +\cgalCRPSection{Concepts} +- `AbstractChainComplex` + +\cgalCRPSection{Classes} +- `CGAL::Homological_discrete_vector_field::Abstract_simplicial_chain_complex` + + +*/ diff --git a/HDVF/doc/HDVF/dependencies b/HDVF/doc/HDVF/dependencies new file mode 100644 index 00000000000..6c33abe8990 --- /dev/null +++ b/HDVF/doc/HDVF/dependencies @@ -0,0 +1,9 @@ +Manual +Kernel_23 +Kernel_d +STL_Extension +Algebraic_foundations +Number_types +Circulator +Stream_support +Surface_mesh diff --git a/HDVF/doc/HDVF/examples.txt b/HDVF/doc/HDVF/examples.txt new file mode 100644 index 00000000000..c559a068b8c --- /dev/null +++ b/HDVF/doc/HDVF/examples.txt @@ -0,0 +1,9 @@ +/*! +\example HDVF/main_dual_hdvf.cpp +\example HDVF/main_hdvf.cpp +\example HDVF/main_per_hdvf.cpp +\example HDVF/hdvf_simplicial.cpp +\example HDVF/hdvf_surface_mesh_simplicial.cpp +\example HDVF/hdvf_cubical.cpp +\example HDVF/matrix_chain.cpp +*/ \ No newline at end of file diff --git a/HDVF/doc/HDVF/fig/HDVF-small.png b/HDVF/doc/HDVF/fig/HDVF-small.png new file mode 100644 index 00000000000..096b04a15da Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF-small.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_M.png b/HDVF/doc/HDVF/fig/HDVF_M.png new file mode 100644 index 00000000000..612a66959c2 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_M.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_MW.png b/HDVF/doc/HDVF/fig/HDVF_MW.png new file mode 100644 index 00000000000..34ba63cd901 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_MW.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_W.png b/HDVF/doc/HDVF/fig/HDVF_W.png new file mode 100644 index 00000000000..66ca07bb796 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_W.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_annot_1cycle_mod.png b/HDVF/doc/HDVF/fig/HDVF_annot_1cycle_mod.png new file mode 100644 index 00000000000..238c52ac092 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_annot_1cycle_mod.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_annot_3cycles.png b/HDVF/doc/HDVF/fig/HDVF_annot_3cycles.png new file mode 100644 index 00000000000..1ee83e094a0 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_annot_3cycles.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_annot_hom1.png b/HDVF/doc/HDVF/fig/HDVF_annot_hom1.png new file mode 100644 index 00000000000..ff41d7ec20d Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_annot_hom1.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_annotations.png b/HDVF/doc/HDVF/fig/HDVF_annotations.png new file mode 100644 index 00000000000..f368d28c907 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_annotations.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_cub_file.png b/HDVF/doc/HDVF/fig/HDVF_cub_file.png new file mode 100644 index 00000000000..7512e7beb6d Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_cub_file.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_cub_simpl_complex.png b/HDVF/doc/HDVF/fig/HDVF_cub_simpl_complex.png new file mode 100644 index 00000000000..4496f12b9c8 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_cub_simpl_complex.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_cycle.png b/HDVF/doc/HDVF/fig/HDVF_cycle.png new file mode 100644 index 00000000000..083915cf12b Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_cycle.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_cycle_highlighted.png b/HDVF/doc/HDVF/fig/HDVF_cycle_highlighted.png new file mode 100644 index 00000000000..07d26175425 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_cycle_highlighted.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_dtorus_all.png b/HDVF/doc/HDVF/fig/HDVF_dtorus_all.png new file mode 100644 index 00000000000..22d5a9119f2 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_dtorus_all.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_dtorus_cohom1.png b/HDVF/doc/HDVF/fig/HDVF_dtorus_cohom1.png new file mode 100644 index 00000000000..7c5560bc23e Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_dtorus_cohom1.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_dtorus_cohom1_co.png b/HDVF/doc/HDVF/fig/HDVF_dtorus_cohom1_co.png new file mode 100644 index 00000000000..0af00cd7e44 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_dtorus_cohom1_co.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_dtorus_cohom2.png b/HDVF/doc/HDVF/fig/HDVF_dtorus_cohom2.png new file mode 100644 index 00000000000..94cd4b0e0bf Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_dtorus_cohom2.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_dtorus_cohom2_co.png b/HDVF/doc/HDVF/fig/HDVF_dtorus_cohom2_co.png new file mode 100644 index 00000000000..c4985303371 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_dtorus_cohom2_co.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_dtorus_homs.png b/HDVF/doc/HDVF/fig/HDVF_dtorus_homs.png new file mode 100644 index 00000000000..3d33bb19004 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_dtorus_homs.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_eight_view1.png b/HDVF/doc/HDVF/fig/HDVF_eight_view1.png new file mode 100644 index 00000000000..a4bee8a122e Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_eight_view1.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_eight_view2.png b/HDVF/doc/HDVF/fig/HDVF_eight_view2.png new file mode 100644 index 00000000000..18224e91458 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_eight_view2.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_ex1.png b/HDVF/doc/HDVF/fig/HDVF_ex1.png new file mode 100644 index 00000000000..d0d8ae4e263 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_ex1.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_ex1_A1.png b/HDVF/doc/HDVF/fig/HDVF_ex1_A1.png new file mode 100644 index 00000000000..dbf88490a9f Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_ex1_A1.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_ex1_A1_arrow.png b/HDVF/doc/HDVF/fig/HDVF_ex1_A1_arrow.png new file mode 100644 index 00000000000..33d6261aa20 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_ex1_A1_arrow.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_ex1_A2.png b/HDVF/doc/HDVF/fig/HDVF_ex1_A2.png new file mode 100644 index 00000000000..29936d296b8 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_ex1_A2.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_ex1_A2_A.png b/HDVF/doc/HDVF/fig/HDVF_ex1_A2_A.png new file mode 100644 index 00000000000..6703235b689 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_ex1_A2_A.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_ex1_A2_arrow.png b/HDVF/doc/HDVF/fig/HDVF_ex1_A2_arrow.png new file mode 100644 index 00000000000..84cd4fcf1d1 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_ex1_A2_arrow.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_ex1_A3.png b/HDVF/doc/HDVF/fig/HDVF_ex1_A3.png new file mode 100644 index 00000000000..a83599ebc32 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_ex1_A3.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_ex1_A3_DGVF.png b/HDVF/doc/HDVF/fig/HDVF_ex1_A3_DGVF.png new file mode 100644 index 00000000000..5145af54f27 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_ex1_A3_DGVF.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_ex1_A3_arrow.png b/HDVF/doc/HDVF/fig/HDVF_ex1_A3_arrow.png new file mode 100644 index 00000000000..bd09c40c620 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_ex1_A3_arrow.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_ex1_perfect.png b/HDVF/doc/HDVF/fig/HDVF_ex1_perfect.png new file mode 100644 index 00000000000..f26356c134a Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_ex1_perfect.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_ex1_perfect_cohom1.png b/HDVF/doc/HDVF/fig/HDVF_ex1_perfect_cohom1.png new file mode 100644 index 00000000000..ffc6a4d2e78 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_ex1_perfect_cohom1.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_ex1_perfect_cohom2.png b/HDVF/doc/HDVF/fig/HDVF_ex1_perfect_cohom2.png new file mode 100644 index 00000000000..ef0b65f1acf Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_ex1_perfect_cohom2.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_ex1_perfect_cohom3.png b/HDVF/doc/HDVF/fig/HDVF_ex1_perfect_cohom3.png new file mode 100644 index 00000000000..ec30b3f1fde Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_ex1_perfect_cohom3.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_ex1_perfect_hom1.png b/HDVF/doc/HDVF/fig/HDVF_ex1_perfect_hom1.png new file mode 100644 index 00000000000..a006fd9733c Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_ex1_perfect_hom1.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_ex1_perfect_hom2.png b/HDVF/doc/HDVF/fig/HDVF_ex1_perfect_hom2.png new file mode 100644 index 00000000000..a92010d6c52 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_ex1_perfect_hom2.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_ex1_perfect_hom3.png b/HDVF/doc/HDVF/fig/HDVF_ex1_perfect_hom3.png new file mode 100644 index 00000000000..ce6d3e72d5c Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_ex1_perfect_hom3.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_ex_hom_cohom.png b/HDVF/doc/HDVF/fig/HDVF_ex_hom_cohom.png new file mode 100644 index 00000000000..5a2b7444589 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_ex_hom_cohom.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_ex_hom_cohom_cofaces.png b/HDVF/doc/HDVF/fig/HDVF_ex_hom_cohom_cofaces.png new file mode 100644 index 00000000000..d2509a58068 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_ex_hom_cohom_cofaces.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_op1.png b/HDVF/doc/HDVF/fig/HDVF_op1.png new file mode 100644 index 00000000000..93a378d719c Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_op1.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_op1_g1.png b/HDVF/doc/HDVF/fig/HDVF_op1_g1.png new file mode 100644 index 00000000000..aee8104ceba Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_op1_g1.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_op1_g2.png b/HDVF/doc/HDVF/fig/HDVF_op1_g2.png new file mode 100644 index 00000000000..0ebebdcaa68 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_op1_g2.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_op1_g3.png b/HDVF/doc/HDVF/fig/HDVF_op1_g3.png new file mode 100644 index 00000000000..3808e1de135 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_op1_g3.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_op2_W7_11.png b/HDVF/doc/HDVF/fig/HDVF_op2_W7_11.png new file mode 100644 index 00000000000..93af4f12afa Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_op2_W7_11.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_op2_g1.png b/HDVF/doc/HDVF/fig/HDVF_op2_g1.png new file mode 100644 index 00000000000..4302c9628c4 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_op2_g1.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_op2_g2.png b/HDVF/doc/HDVF/fig/HDVF_op2_g2.png new file mode 100644 index 00000000000..bec7e0d13b4 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_op2_g2.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_op2_g3.png b/HDVF/doc/HDVF/fig/HDVF_op2_g3.png new file mode 100644 index 00000000000..78ce05b4a87 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_op2_g3.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_op3_MW5_7.png b/HDVF/doc/HDVF/fig/HDVF_op3_MW5_7.png new file mode 100644 index 00000000000..54e5b5d17ab Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_op3_MW5_7.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_op3_g1.png b/HDVF/doc/HDVF/fig/HDVF_op3_g1.png new file mode 100644 index 00000000000..b6bfcd2b7ee Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_op3_g1.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_op3_g2.png b/HDVF/doc/HDVF/fig/HDVF_op3_g2.png new file mode 100644 index 00000000000..cdf151c9339 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_op3_g2.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_op3_g3.png b/HDVF/doc/HDVF/fig/HDVF_op3_g3.png new file mode 100644 index 00000000000..d5c5d8ef7e9 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_op3_g3.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_primal_dual.png b/HDVF/doc/HDVF/fig/HDVF_primal_dual.png new file mode 100644 index 00000000000..e5072fd82ff Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_primal_dual.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_primal_dual_ex.png b/HDVF/doc/HDVF/fig/HDVF_primal_dual_ex.png new file mode 100644 index 00000000000..b45b9ce40b4 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_primal_dual_ex.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_simp_file.png b/HDVF/doc/HDVF/fig/HDVF_simp_file.png new file mode 100644 index 00000000000..32715bdad11 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_simp_file.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_twirl.png b/HDVF/doc/HDVF/fig/HDVF_twirl.png new file mode 100644 index 00000000000..852519a0b9d Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_twirl.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_twirl_quartet.png b/HDVF/doc/HDVF/fig/HDVF_twirl_quartet.png new file mode 100644 index 00000000000..ba49ac23cf2 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_twirl_quartet.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_twirl_view1.png b/HDVF/doc/HDVF/fig/HDVF_twirl_view1.png new file mode 100644 index 00000000000..a9ce6b90aac Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_twirl_view1.png differ diff --git a/HDVF/doc/HDVF/fig/HDVF_twirl_view2.png b/HDVF/doc/HDVF/fig/HDVF_twirl_view2.png new file mode 100644 index 00000000000..e936efe1059 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVF_twirl_view2.png differ diff --git a/HDVF/doc/HDVF/fig/HDVFoverview_HDVF.png b/HDVF/doc/HDVF/fig/HDVFoverview_HDVF.png new file mode 100644 index 00000000000..1fc1da43db5 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVFoverview_HDVF.png differ diff --git a/HDVF/doc/HDVF/fig/HDVFoverview_comput_hom.png b/HDVF/doc/HDVF/fig/HDVFoverview_comput_hom.png new file mode 100644 index 00000000000..f78ccc508a0 Binary files /dev/null and b/HDVF/doc/HDVF/fig/HDVFoverview_comput_hom.png differ diff --git a/HDVF/doc/HDVF/fig/Sparse_matrix_example_col.pdf b/HDVF/doc/HDVF/fig/Sparse_matrix_example_col.pdf new file mode 100644 index 00000000000..ff6befb8d97 Binary files /dev/null and b/HDVF/doc/HDVF/fig/Sparse_matrix_example_col.pdf differ diff --git a/HDVF/doc/HDVF/fig/Sparse_matrix_example_row.pdf b/HDVF/doc/HDVF/fig/Sparse_matrix_example_row.pdf new file mode 100644 index 00000000000..bc69bc24d4b Binary files /dev/null and b/HDVF/doc/HDVF/fig/Sparse_matrix_example_row.pdf differ diff --git a/HDVF/doc/HDVF/fig/cubical_complex.png b/HDVF/doc/HDVF/fig/cubical_complex.png new file mode 100644 index 00000000000..4992f3ad4bb Binary files /dev/null and b/HDVF/doc/HDVF/fig/cubical_complex.png differ diff --git a/HDVF/doc/HDVF/fig/cubical_implementation.png b/HDVF/doc/HDVF/fig/cubical_implementation.png new file mode 100644 index 00000000000..b3066e386ff Binary files /dev/null and b/HDVF/doc/HDVF/fig/cubical_implementation.png differ diff --git a/HDVF/doc/HDVF/fig/holes_example.png b/HDVF/doc/HDVF/fig/holes_example.png new file mode 100644 index 00000000000..3e17d413df7 Binary files /dev/null and b/HDVF/doc/HDVF/fig/holes_example.png differ diff --git a/HDVF/doc/HDVF/fig/lower_star_filtration_y.png b/HDVF/doc/HDVF/fig/lower_star_filtration_y.png new file mode 100644 index 00000000000..8a7ddb91850 Binary files /dev/null and b/HDVF/doc/HDVF/fig/lower_star_filtration_y.png differ diff --git a/HDVF/doc/HDVF/fig/lower_star_filtration_z.png b/HDVF/doc/HDVF/fig/lower_star_filtration_z.png new file mode 100644 index 00000000000..e971e109083 Binary files /dev/null and b/HDVF/doc/HDVF/fig/lower_star_filtration_z.png differ diff --git a/HDVF/doc/HDVF/fig/persistence_2generator1.png b/HDVF/doc/HDVF/fig/persistence_2generator1.png new file mode 100644 index 00000000000..c7325b75f0d Binary files /dev/null and b/HDVF/doc/HDVF/fig/persistence_2generator1.png differ diff --git a/HDVF/doc/HDVF/fig/persistence_after1.png b/HDVF/doc/HDVF/fig/persistence_after1.png new file mode 100644 index 00000000000..3ca88a0ed90 Binary files /dev/null and b/HDVF/doc/HDVF/fig/persistence_after1.png differ diff --git a/HDVF/doc/HDVF/fig/persistence_cycles1.png b/HDVF/doc/HDVF/fig/persistence_cycles1.png new file mode 100644 index 00000000000..a5b0a7b5b5c Binary files /dev/null and b/HDVF/doc/HDVF/fig/persistence_cycles1.png differ diff --git a/HDVF/doc/HDVF/fig/persistence_hole2.png b/HDVF/doc/HDVF/fig/persistence_hole2.png new file mode 100644 index 00000000000..117f2345f97 Binary files /dev/null and b/HDVF/doc/HDVF/fig/persistence_hole2.png differ diff --git a/HDVF/doc/HDVF/fig/persistence_hole3.png b/HDVF/doc/HDVF/fig/persistence_hole3.png new file mode 100644 index 00000000000..12048909cea Binary files /dev/null and b/HDVF/doc/HDVF/fig/persistence_hole3.png differ diff --git a/HDVF/doc/HDVF/fig/persistence_hole4.png b/HDVF/doc/HDVF/fig/persistence_hole4.png new file mode 100644 index 00000000000..82b5612c7b8 Binary files /dev/null and b/HDVF/doc/HDVF/fig/persistence_hole4.png differ diff --git a/HDVF/doc/HDVF/fig/persistence_hole_inf.png b/HDVF/doc/HDVF/fig/persistence_hole_inf.png new file mode 100644 index 00000000000..58cc5616ab7 Binary files /dev/null and b/HDVF/doc/HDVF/fig/persistence_hole_inf.png differ diff --git a/HDVF/doc/HDVF/fig/primal_dual.png b/HDVF/doc/HDVF/fig/primal_dual.png new file mode 100644 index 00000000000..4f53c4d946b Binary files /dev/null and b/HDVF/doc/HDVF/fig/primal_dual.png differ diff --git a/HDVF/doc/HDVF/fig/simplicial_complex.png b/HDVF/doc/HDVF/fig/simplicial_complex.png new file mode 100644 index 00000000000..345bacc6313 Binary files /dev/null and b/HDVF/doc/HDVF/fig/simplicial_complex.png differ diff --git a/HDVF/doc/HDVF/fig/simplicial_complex_chains.png b/HDVF/doc/HDVF/fig/simplicial_complex_chains.png new file mode 100644 index 00000000000..35bf07d5820 Binary files /dev/null and b/HDVF/doc/HDVF/fig/simplicial_complex_chains.png differ diff --git a/HDVF/doc/HDVF/fig/simplicial_complex_coboundary.png b/HDVF/doc/HDVF/fig/simplicial_complex_coboundary.png new file mode 100644 index 00000000000..be2248eb43f Binary files /dev/null and b/HDVF/doc/HDVF/fig/simplicial_complex_coboundary.png differ diff --git a/HDVF/doc/HDVF/fig/simplicial_complex_homology.png b/HDVF/doc/HDVF/fig/simplicial_complex_homology.png new file mode 100644 index 00000000000..95293ec9746 Binary files /dev/null and b/HDVF/doc/HDVF/fig/simplicial_complex_homology.png differ diff --git a/HDVF/examples/HDVF/CMakeLists.txt b/HDVF/examples/HDVF/CMakeLists.txt new file mode 100644 index 00000000000..ead5cd2e4be --- /dev/null +++ b/HDVF/examples/HDVF/CMakeLists.txt @@ -0,0 +1,26 @@ +# Created by the script cgal_create_cmake_script +# This is the CMake script for compiling a CGAL application. + +cmake_minimum_required(VERSION 3.12...3.31) +project(HDVF_Examples) + +find_package(CGAL REQUIRED) + + +find_package(Eigen3 3.1.0 QUIET) #(requires 3.1.0 or greater) +include(CGAL_Eigen3_support) + + + +# create a target per cppfile +file( + GLOB cppfiles + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) +foreach(cppfile ${cppfiles}) + create_single_source_cgal_program("${cppfile}") +endforeach() + +if(TARGET CGAL::Eigen3_support) +target_link_libraries(hdvf_simplicial_4 PUBLIC CGAL::Eigen3_support) +endif() diff --git a/HDVF/examples/HDVF/arguments.h b/HDVF/examples/HDVF/arguments.h new file mode 100644 index 00000000000..de621c0c7d4 --- /dev/null +++ b/HDVF/examples/HDVF/arguments.h @@ -0,0 +1,394 @@ +#ifndef ARGUMENTS_H +#define ARGUMENTS_H + +#include + +using namespace std; +using namespace CGAL; +using namespace Homological_discrete_vector_field; + +enum class Algorithm { HDVF, DualHDVF, PerHDVF }; +enum class InputFormat { SIMP, OFF, CUB, PGM }; +enum class StarFiltrStd { FiltrX, FiltrY, FiltrZ }; + +struct Options +{ + std::string outfile_root = "res", in_file ; + Algorithm algorithm = Algorithm::HDVF ; + InputFormat in_format = InputFormat::OFF ; + int HDVF_opt = CGAL::Homological_discrete_vector_field::OPT_FULL ; + int scalar = 0 ; // 0 : Z, n : Z/nZ + bool with_export = true ; // Export reduction information to a file + bool with_vtk_export = false ; // Export generators/cogenerators and PSC labels as vtk + bool with_output = true ; // Consol output + bool with_frame = true ; // Only for persistent homology (cub and pgm in_files) + bool loop = false ; // Only for HDVF (enter an interaction loop for M, W, MW) + double BB_ratio = 1.5 ; // Only for persistent homolgy (off in_files: size of the bounding sphere) + bool primal = true ; // Only for cub_complexes (for dual construction, set primal to false) + StarFiltrStd star_filtr = StarFiltrStd::FiltrZ ; // Only for per_hdvf (when filtrataion is a standard lower star filtration) + bool random = false ; // Activate the computation of random perfect HDVF + bool verbose = false ; // Activate verbose mode (matrix output after each A operation for HDVF computation) + bool co_faces = false ; // Output co-faces of cohomology generators (or not) + + std::string comment() const + { + std::string str; + if (algorithm == Algorithm::HDVF) + str = "HDVF computation"; + else if (algorithm == Algorithm::DualHDVF) + str = "Alexander duality HDVF computation"; + else if (algorithm == Algorithm::PerHDVF) + str = "Persistent HDVF computation"; + else + str = "undefined"; + return str; + } +}; + +inline void usage() { + cout << "usage:" << endl ; + cout << "\talgorithm [options] in_file" << endl ; + cout << "with:" << endl ; + cout << "algorithm = hdvf (for hdvf (co)homology compuation)" << endl ; + cout << "\t dual_hdvf (for hdvf Alexander duality)" << endl ; + cout << "\t per_hdvf (for hdvf persistent (co)homology)" << endl ; + cout << "and options:" << endl ; + cout << "\t-outfile_root = xxx : sets the outfile root for outfiles names to xxx (default: res)" << endl ; + cout << "\t-opt (bnd|f|g|full) : HDVF option (default: full)" << endl ; + cout << "\t-s n : ring used to compute homology, 0: Z, n: Z/nZ (default: 0)" << endl ; + cout << "\t-rand : compute a random perfect HDVF (default: false, computing a random HDVF is slower)" << endl ; + cout << "\t-verbose : verbose HDVF computation, output reduction matrices after each A (default: false)" << endl ; + cout << "\t-with_output / -no_output : output / do not output the HDVF and reduction to the console (default: with_output)" << endl ; + cout << "\t-with_export / -no_export : export / do not export the HDVF and reduction to a file (default: with_export)" << endl ; + cout << "\t-with_vtk_export / -no_vtk_export : export / do not export the HDVF and reduction to vtk (default: no_export)" << endl ; + cout << "\t-cofaces_cohomology / -no_cofaces_cohomology : do not export co-faces of cohomology generators (default: co_faces_cohomology)" << endl ; + cout << "For CubComplex only:" << endl ; + cout << "\t-primal / -dual: build the primal / dual complex associated to data (default: primal)" << endl ; + cout << "For Alexander duality only:" << endl ; + cout << "\tFor SimpComplex:" << endl ; + cout << "\t\t-BB_ratio x : ratio for the closing bounding sphere (default: 1.5)" << endl ; + cout << "\tFor CubComplex:" << endl ; + cout << "\t\t-with_frame / -no_frame : enclose / do not inclose the complex in a larger bounding box (default: with_frame, adds 1 pixel around)" << endl ; + cout << "For persistent HDVF only:" << endl ; + cout << "\t-lower (x|y|z) : compute lower star filtration with degree x, y or z" << endl ; + cout << "\tFor other filtrations, define the filtration into the main and set #define OwnFiltration" << endl; +} + +inline Options read_arguments_hdvf(int argc, char** argv) { + Options opt_res ; + + // Minumum number of arguments: 2 + // HDVF_algorithm file + + if (argc <= 2) + { + cout << "HDVF wrong number of arguments. HDVF -h for help" << endl ; + throw "HDVF wrong number of arguments" ; + } + // Read algorithm from argv[0] (last word of the path - separated by /) + const string algo(argv[0]) ; + string word1 ; + { + std::stringstream string_parse(algo); + std::string tmp_word; + + std::getline(string_parse, tmp_word, '/') ; + word1 = tmp_word ; + while(std::getline(string_parse, tmp_word, '/')) + { + word1 = tmp_word ; + } + } + + if (word1.compare("main_hdvf") == 0) + opt_res.algorithm = Algorithm::HDVF ; + else if (word1.compare("main_dual_hdvf") == 0) + opt_res.algorithm = Algorithm::DualHDVF ; + else if (word1.compare("main_per_hdvf") == 0) + opt_res.algorithm = Algorithm::PerHDVF ; + else + { + cerr << "HDVF algorithm unknown. HDVF -h for help" << endl ; + throw "HDVF algorithm unknown" ; + } + + // Read in_file from argv[argc-1] ; + opt_res.in_file = argv[argc-1] ; + // Deduce the file type from the file name + string word ; + { + // Get the file name (last word of the path - separated by /) + std::stringstream string_parse(opt_res.in_file); + std::string tmp_word; + + std::getline(string_parse, tmp_word, '/') ; + word = tmp_word ; + while(std::getline(string_parse, tmp_word, '/')) + { + word = tmp_word ; + } + } + { + // Get the extension (last word of the word - separated by .) + string filename = word ; + std::stringstream string_parse(filename); + std::string tmp_word; + + std::getline(string_parse, tmp_word, '.') ; + word = tmp_word ; + while(std::getline(string_parse, tmp_word, '.')) + { + word = tmp_word ; + } + } + // Assign in_format accordingly + if (word.compare("simp")==0) + opt_res.in_format = InputFormat::SIMP ; + else if (word.compare("off")==0) + opt_res.in_format = InputFormat::OFF ; + else if (word.compare("cub")==0) + opt_res.in_format = InputFormat::CUB ; + else if (word.compare("pgm")==0) + opt_res.in_format = InputFormat::PGM ; + else + { + cerr << "HDVF input format unkown. HDVF -h for help" << endl ; + throw "HDVF input format unknown" ; + } + + + // Read next options + for(int i = 1; i +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace HDVF = CGAL::Homological_discrete_vector_field; + +typedef CGAL::Simple_cartesian Kernel; +typedef HDVF::Hdvf_traits_3 Traits; + +//typedef HDVF::Zp<5,int,true> Coefficient_ring; +typedef CGAL::Z2 Coefficient_ring; +typedef HDVF::Cubical_chain_complex Complex; +typedef HDVF::Hdvf_duality HDVF_type; +typedef HDVF::Duality_cubical_complex_tools Tools_type; +typedef HDVF::Sub_chain_complex_mask Sub_chain_complex; + +// PRIMAL / DUAL construction + +#define PRIMAL_DUAL true // or false + +// Frame or not the complex (add 1 pixel around) + +#define FRAME true // or false + +int main(int argc, char **argv) +{ + std::string filename ; + if (argc > 2) std::cout << "usage: dual_hdvf_cubical pgm_file" << std::endl; + else if (argc == 1) filename = "data/data_cubical/Eight_10.pgm"; + else filename = argv[1]; + + // Set variables to chose loading mode (PRIMAL or DUAL associated complex, pgm read using Khalimsky coordinates or not) + Complex::Cubical_complex_primal_dual primal_dual; + bool khalimsky; + if (PRIMAL_DUAL) { + primal_dual = Complex::PRIMAL; + khalimsky = true; + } + else { + primal_dual = Complex::DUAL; + khalimsky = false; + } + + // Load pgm object + HDVF::Cub_object_io cub_object ; + cub_object.read_pgm(filename, khalimsky); + + // Frame the object + if (FRAME) + cub_object.frame(); + + cub_object.print_infos(); + + // Build K and L + typename Tools_type::Complex_duality_data t(Tools_type::dualize_complex(cub_object, primal_dual)) ; + std::cout << "--- Triangulation built" << std::endl ; + std::shared_ptr L(t.L_complex) ; + std::shared_ptr K(t.K_complex) ; + std::cout << "--- K,L built" << std::endl ; + + std::cout << "----> complex informations" << std::endl ; + std::cout << "------> complex L" << std::endl ; + std::cout << *L; + std::cout << "------> subcomplex K" << std::endl ; + std::cout << *K << std::endl ; + + // Create and compute a perfect HDVF + HDVF_type hdvf(*L, *K, HDVF::OPT_FULL); + hdvf.compute_perfect_hdvf(); + + // Export K HDVF + hdvf.set_mask_K(); + CGAL::IO::write_VTK(hdvf, *L, "tmp/res_complex_K", false) ; + // Export L-K HDVF + hdvf.set_mask_L_K(); + CGAL::IO::write_VTK(hdvf, *L, "tmp/res_cocomplex_L_K", false) ; + // Compute pairing + std::vector pairs = hdvf.compute_pairing_hdvf(); + // Output pairing + for (const auto& pair : pairs) { + std::cout << "Sigma: " << pair.sigma << ", Tau: " << pair.tau << ", Dim: " << pair.dim << std::endl; + } + + return 0; +} diff --git a/HDVF/examples/HDVF/dual_hdvf_simplicial_cgal.cpp b/HDVF/examples/HDVF/dual_hdvf_simplicial_cgal.cpp new file mode 100644 index 00000000000..357bd628e93 --- /dev/null +++ b/HDVF/examples/HDVF/dual_hdvf_simplicial_cgal.cpp @@ -0,0 +1,81 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace HDVF = CGAL::Homological_discrete_vector_field; + +typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; +typedef HDVF::Hdvf_traits_3 Traits; + +//typedef CGAL::Zp<5,int,true> Coefficient_ring; +typedef CGAL::Z2 Coefficient_ring; +typedef HDVF::Simplicial_chain_complex Complex; +typedef HDVF::Hdvf_duality HDVF_type; +typedef HDVF::Duality_simplicial_complex_tools Tools_type; +typedef HDVF::Sub_chain_complex_mask Sub_chain_complex; +typedef CGAL::Surface_mesh Surface_mesh; + +int main(int argc, char **argv) +{ + std::string filename ; + if (argc > 2) std::cout << "usage: dual_hdvf_simplicial off_file" << std::endl; +// else if (argc == 1) filename = "data/data_simplicial/two_rings.off"; + else if (argc == 1) filename = "data/data_simplicial/three_triangles.off"; + else filename = argv[1]; + + // Load simplicial object into Surface_mesh + Surface_mesh sm; + CGAL::IO::read_polygon_mesh(filename, sm); + + if (sm.number_of_vertices() == 0) + { + std::cerr << "Emtpy mesh read" << std::endl; + throw("Emtpy mesh read"); + } + +// std::cout << sm; + + // Build K and L + typename Tools_type::Complex_duality_data t(Tools_type::dualize_complex(sm, 1.7, 1)); + std::cout << "--- Triangulation built" << std::endl ; + std::shared_ptr L(t.L_complex); + std::shared_ptr K(t.K_complex); + std::cout << "--- K,L built" << std::endl ; + + std::cout << "----> complex informations" << std::endl ; + std::cout << "------> complex L" << std::endl ; + std::cout << *L; + std::cout << "------> subcomplex K" << std::endl ; + std::cout << *K << std::endl ; + + // Create and compute a perfect HDVF + HDVF_type hdvf(*L, *K, HDVF::OPT_FULL); + hdvf.compute_perfect_hdvf(); + + // Export K HDVF + hdvf.set_mask_K(); + CGAL::IO::write_VTK(hdvf, *L, "tmp/res_complex_K", false) ; + // Export L-K HDVF + hdvf.set_mask_L_K(); + CGAL::IO::write_VTK(hdvf, *L, "tmp/res_cocomplex_L_K", false) ; + // Compute pairing + std::vector pairs = hdvf.compute_pairing_hdvf(); + // Output pairing + for (const auto& pair : pairs) { + std::cout << "Sigma: " << pair.sigma << ", Tau: " << pair.tau << ", Dim: " << pair.dim << std::endl; + } + + return 0; +} diff --git a/HDVF/examples/HDVF/dual_hdvf_simplicial_tetgen.cpp b/HDVF/examples/HDVF/dual_hdvf_simplicial_tetgen.cpp new file mode 100644 index 00000000000..846eb93bf35 --- /dev/null +++ b/HDVF/examples/HDVF/dual_hdvf_simplicial_tetgen.cpp @@ -0,0 +1,72 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace HDVF = CGAL::Homological_discrete_vector_field; + +typedef CGAL::Simple_cartesian Kernel; +typedef HDVF::Hdvf_traits_3 Traits; + +//typedef CGAL::Zp<5,int,true> Coefficient_ring; +typedef CGAL::Z2 Coefficient_ring; +typedef HDVF::Simplicial_chain_complex Complex; +typedef HDVF::Hdvf_duality HDVF_type; +typedef HDVF::Duality_simplicial_complex_tools Tools_type; +typedef HDVF::Sub_chain_complex_mask Sub_chain_complex; + +int main(int argc, char **argv) +{ + std::string filename ; + if (argc > 2) std::cout << "usage: dual_hdvf_simplicial off_file" << std::endl; + else if (argc == 1) filename = "data/mesh_data/two_rings.off"; + else filename = argv[1]; + + // Load simplicial object + HDVF::Mesh_object_io mesh ; + mesh.read_off(filename); + + mesh.print_infos(); + + // Build K and L + typename Tools_type::Complex_duality_data t = Tools_type::dualize_complex(mesh, 1.5, "tmp/file_K_closed.off", 1) ; + std::cout << "--- Triangulation built" << std::endl ; + std::shared_ptr L(t.L_complex) ; + std::shared_ptr K(t.K_complex) ; + std::cout << "--- K,L built" << std::endl ; + + std::cout << "----> complex informations" << std::endl ; + std::cout << "------> complex L" << std::endl ; + std::cout << L; + std::cout << "------> subcomplex K" << std::endl ; + std::cout << K << std::endl ; + + // Create and compute a perfect HDVF + HDVF_type hdvf(*L, *K, HDVF::OPT_FULL); + hdvf.compute_perfect_hdvf(); + + // Export K HDVF + hdvf.set_mask_K(); + CGAL::IO::write_VTK(hdvf, *L, "tmp/res_complex_K", false) ; + // Export L-K HDVF + hdvf.set_mask_L_K(); + CGAL::IO::write_VTK(hdvf, *L, "tmp/res_cocomplex_L_K", false) ; + // Compute pairing + std::vector pairs = hdvf.compute_pairing_hdvf(); + // Output pairing + for (const auto& pair : pairs) { + std::cout << "Sigma: " << pair.sigma << ", Tau: " << pair.tau << ", Dim: " << pair.dim << std::endl; + } + + return 0; +} diff --git a/HDVF/examples/HDVF/hdvf_cubical.cpp b/HDVF/examples/HDVF/hdvf_cubical.cpp new file mode 100644 index 00000000000..64d300b3465 --- /dev/null +++ b/HDVF/examples/HDVF/hdvf_cubical.cpp @@ -0,0 +1,66 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace HDVF = CGAL::Homological_discrete_vector_field; + +//typedef int Coefficient_ring; +//typedef CGAL::Z2 Coefficient_ring; +typedef CGAL::Zp<5, char, true> Coefficient_ring; + +typedef CGAL::Simple_cartesian Kernel; +//typedef HDVF::Hdvf_traits_3 Traits; +typedef HDVF::Hdvf_traits_2 Traits; + +int main(int argc, char **argv) +{ + using Complex = HDVF::Cubical_chain_complex ; + using HDVF_type = HDVF::Hdvf ; + + std::string filename ; + if (argc > 2) std::cout << "usage: example_hdvf_cubical pgm_file" << std::endl; + else if (argc == 1) filename = "data/cub_data/Eight_3D.pgm"; + else filename = argv[1]; + + // Choose between PRIMAL and DUAL construction + const Complex::Cubical_complex_primal_dual primal_dual = Complex::PRIMAL; + // Adapt pgm loading into Cub_complex accordingly + const bool khalimsky_coords = (primal_dual == Complex::PRIMAL) ? true : false ; + + HDVF::Cub_object_io mesh ; + + // Load pgm into cub object + mesh.read_pgm(filename, khalimsky_coords); + mesh.print_infos(); + + // Build simplicial chain complex + Complex complex(mesh, primal_dual); + + std::cout << complex; + + // Build empty HDVF + HDVF_type hdvf(complex, HDVF::OPT_FULL) ; + + // Compute a perfect HDVF + hdvf.compute_perfect_hdvf(); + // hdvf.compute_rand_perfect_hdvf(); + + // Output HDVF to console + hdvf.write_matrices(); + hdvf.write_reduction(); + + // Output HDVF to vtk + CGAL::IO::write_VTK(hdvf, complex, "tmp/res", true) ; + + return 0; +} diff --git a/HDVF/examples/HDVF/hdvf_simplicial.cpp b/HDVF/examples/HDVF/hdvf_simplicial.cpp new file mode 100644 index 00000000000..4f8b2c1f91c --- /dev/null +++ b/HDVF/examples/HDVF/hdvf_simplicial.cpp @@ -0,0 +1,64 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace HDVF = CGAL::Homological_discrete_vector_field; + +//typedef int Coefficient_ring; +//typedef CGAL::Z2 Coefficient_ring; +typedef CGAL::Zp<5, char, true> Coefficient_ring; + +typedef CGAL::Simple_cartesian Kernel; +typedef HDVF::Hdvf_traits_3 Traits; + +int main(int argc, char **argv) +{ +#if 1 + using Complex = HDVF::Simplicial_chain_complex ; + using HDVF_type = HDVF::Hdvf ; + + std::string filename ; + if (argc > 2) std::cerr << "usage: example_hdvf_simplicial off_file" << std::endl; + else if (argc == 1) filename = "data/mesh_data/two_rings.off"; + else filename = argv[1]; + + // Load cub object + HDVF::Mesh_object_io mesh ; + mesh.read_off(filename); + + mesh.print_infos(); + + // Build simplicial chain complex + Complex complex(mesh); + + std::cout << complex; + + // Build empty HDVF + HDVF_type hdvf(complex, HDVF::OPT_FULL) ; + + // Compute a perfect HDVF + hdvf.compute_perfect_hdvf(); + // hdvf.compute_rand_perfect_hdvf(); + + // Output HDVF to console + hdvf.write_matrices(); + hdvf.write_reduction(); + + // Output HDVF to vtk + CGAL::IO::write_VTK(hdvf, complex, "tmp/res", true) ; + + // Save HDVF to .hdvf file + hdvf.write_hdvf_reduction("tmp/test.hdvf") ; + +#endif + return 0; +} diff --git a/HDVF/examples/HDVF/hdvf_simplicial_4.cpp b/HDVF/examples/HDVF/hdvf_simplicial_4.cpp new file mode 100644 index 00000000000..5221b87ec7c --- /dev/null +++ b/HDVF/examples/HDVF/hdvf_simplicial_4.cpp @@ -0,0 +1,72 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace HDVF = CGAL::Homological_discrete_vector_field; + +//typedef int Coefficient_ring; +//typedef CGAL::Z2 Coefficient_ring; +typedef CGAL::Zp<5, char, true> Coefficient_ring; + +typedef CGAL::Epick_d> Kernel; +typedef HDVF::Hdvf_traits_d Traits; + +int main(int argc, char **argv) +{ +#if 1 + using Complex = HDVF::Simplicial_chain_complex ; + using HDVF_type = HDVF::Hdvf ; + + std::string nodes_filename, simp_filename ; + if (argc > 3) std::cerr << "usage: hdvf_simplicial_4 nodes_file simp_file" << std::endl; + else if (argc == 1) { + nodes_filename = "data/dim_4/klein4.nodes"; + simp_filename = "data/dim_4/klein4.simp"; + } + else { + nodes_filename = argv[1]; + simp_filename = argv[2]; + } + + // Load nodes + simplicial data + HDVF::Mesh_object_io mesh ; + mesh.read_nodes_file(nodes_filename); + mesh.read_simp(simp_filename); + + mesh.print_infos(); + + // Build simplicial chain complex + Complex complex(mesh); + + std::cout << complex; + + // Build empty HDVF + HDVF_type hdvf(complex, HDVF::OPT_FULL) ; + + // Compute a perfect HDVF + hdvf.compute_perfect_hdvf(); + // hdvf.compute_rand_perfect_hdvf(); + + // Output HDVF to console + hdvf.write_matrices(); + hdvf.write_reduction(); + + // Output HDVF to vtk + Traits::to_point3 = Traits::pca_frame_builder(complex.points()); + CGAL::IO::write_VTK(hdvf, complex, "tmp/res", true) ; + + // Save HDVF to .hdvf file + hdvf.write_hdvf_reduction("tmp/test.hdvf") ; + +#endif + return 0; +} diff --git a/HDVF/examples/HDVF/hdvf_surface_mesh_simplicial.cpp b/HDVF/examples/HDVF/hdvf_surface_mesh_simplicial.cpp new file mode 100644 index 00000000000..2e339197af0 --- /dev/null +++ b/HDVF/examples/HDVF/hdvf_surface_mesh_simplicial.cpp @@ -0,0 +1,68 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace HDVF = CGAL::Homological_discrete_vector_field; + +//typedef int Coefficient_ring; +//typedef CGAL::Z2 Coefficient_ring; +typedef CGAL::Zp<5, char, true> Coefficient_ring; + +typedef CGAL::Simple_cartesian Kernel; +typedef HDVF::Hdvf_traits_3 Traits; + +typedef CGAL::Surface_mesh Surface_mesh; + +int main(int argc, char **argv) +{ +#if 1 + using Complex = HDVF::Simplicial_chain_complex ; + using HDVF_type = HDVF::Hdvf ; + + std::string filename ; + if (argc > 2) std::cerr << "usage: hdvf_surface_mesh_simplicial off_file" << std::endl; + else if (argc == 1) filename = "data/mesh_data/two_rings.off"; + else filename = argv[1]; + + // Load simplicial object + Surface_mesh sm; + CGAL::IO::read_polygon_mesh(filename, sm); + HDVF::Surface_mesh_io mesh(sm) ; + + mesh.print_infos(); + + // Build simplicial chain complex + Complex complex(mesh); + + std::cout << complex; + + // Build empty HDVF + HDVF_type hdvf(complex, HDVF::OPT_FULL) ; + + // Compute a perfect HDVF + hdvf.compute_perfect_hdvf(); + // hdvf.compute_rand_perfect_hdvf(); + + // Output HDVF to console + hdvf.write_matrices(); + hdvf.write_reduction(); + + // Output HDVF to vtk + CGAL::IO::write_VTK(hdvf, complex, "tmp/res", true) ; + + // Save HDVF to .hdvf file + hdvf.write_hdvf_reduction("tmp/test.hdvf") ; +#endif + return 0; +} diff --git a/HDVF/examples/HDVF/main_dual_hdvf.cpp b/HDVF/examples/HDVF/main_dual_hdvf.cpp new file mode 100644 index 00000000000..5cc9664c466 --- /dev/null +++ b/HDVF/examples/HDVF/main_dual_hdvf.cpp @@ -0,0 +1,313 @@ +// Dual HDVF computation (command line version) +// -------- +// Computes a "perfect" dual HDVF from a object +// (the object is embedded into a "ball") +// and provides a batch mode to specify arguments +// For help: dual_hdvf -h +// -------- +// A. Bac +// -------- + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "arguments.h" +namespace HDVF = CGAL::Homological_discrete_vector_field; + +// ------- A ring +// For Z/nZ other than Z (ie. n=0) and Z/2Z, uncomment and set the following define properly + +//#define SCALAR 5 + +using Kernel = CGAL::Simple_cartesian; +using Traits = HDVF::Hdvf_traits_3; + +template +void mesh_complex_output(const MeshType& mesh, const Complex& L, const HDVF::Sub_chain_complex_mask& K, const Options& options) +{ + if (options.with_output) + { + // Mesh + std::cout << "----> mesh informations" << std::endl ; + mesh.print_infos() ; + + // Complex + std::cout << "----> complex informations" << std::endl ; + std::cout << "------> complex L" << std::endl ; + std::cout << L; + std::cout << "------> subcomplex K" << std::endl ; + std::cout << K << std::endl ; + } +} + +inline std::ostream& dual_pairs_output(const std::vector& pairs, std::ostream& out=std::cout) +{ + out << "Pairs found by compute_perfect_hdvf:" << std::endl; + for (const auto& pair : pairs) { + out << "Sigma: " << pair.sigma << ", Tau: " << pair.tau << ", Dim: " << pair.dim << std::endl; + } + return out ; +} + +template +void dual_HDVF_pair (HDVF::Hdvf_duality& dual_hdvf, const Options &options) +{ + // Compute pairing + std::vector pairs = dual_hdvf.compute_pairing_hdvf() ; + + if (options.with_output) + { + dual_pairs_output(pairs) ; + } + if (options.with_export) + { + std::string file(options.outfile_root+"_pairs.txt") ; + std::ofstream out ( file, std::ios::out | std::ios::trunc); + + if ( ! out . good () ) { + std::cerr << "hdvf: with_export. Fatal Error:\n " << file << " not found.\n"; + throw std::runtime_error("File Parsing Error: File not found"); + } + + dual_pairs_output(pairs, out) ; + out.close() ; + } +} + +template +HDVF::Hdvf_duality& dual_HDVF_comput (const Complex& L, HDVF::Sub_chain_complex_mask& K, const Options &options) +{ + using Coefficient_ring = typename Complex::Coefficient_ring; + using HDVF_type = HDVF::Hdvf_duality ; + using SubCCType = HDVF::Sub_chain_complex_mask ; + + HDVF_type& hdvf(*(new HDVF_type(L, K, options.HDVF_opt))); + + std::cout << "----> START computing dual HDVF" << std::endl ; + if (options.random) + hdvf.compute_rand_perfect_hdvf() ; + else + hdvf.compute_perfect_hdvf() ; + std::cout << "------> END computing dual HDVF" << std::endl ; + + if (options.with_output) + { + std::cout << "----> reduction" << std::endl ; + hdvf.write_reduction() ; + } + if (options.with_export) + { + std::cout << "----> exporting..." << std::endl ; + std::string file(options.outfile_root+"_reduction.txt") ; + std::ofstream out ( file, std::ios::out | std::ios::trunc); + + if ( ! out . good () ) { + std::cerr << "hdvf: with_export. Fatal Error:\n " << file << " not found.\n"; + throw std::runtime_error("File Parsing Error: File not found"); + } + out << "----> reduction" << std::endl ; + hdvf.write_reduction(out) ; + + out.close() ; + } + return hdvf ; +} + + +template +void main_code (const Options &options) +{ +#ifndef CUST_FILTRATION + // Standard lower star filtration along x,y or z + using Degree = double ; +#else + // TODO +#endif + + /// SIMP format + if (options.in_format == InputFormat::SIMP) + { +// using Complex = AbstractSimpComplex ; +// using HDVF_type = HDVF ; +// +// // MeshObject +// MeshObject mesh ; +// mesh.read_simp(options.in_file) ; +// +// // Complex +// Complex complex(mesh); +// +// mesh_complex_output(mesh, complex, options) ; +// +// // HDVF computation, export, output +// HDVF_type hdvf(HDVF_comput(complex, options)) ; +// +// // Export to vtk +// // None for SIMP format + std::cout << "not yet..." << std::endl ; + throw("not yet") ; + } + /// OFF format + else if (options.in_format == InputFormat::OFF) + { + using Complex = HDVF::Simplicial_chain_complex ; + using HDVF_type = HDVF::Hdvf_duality ; + using ToolsType = HDVF::Duality_simplicial_complex_tools ; + using SubCCType = HDVF::Sub_chain_complex_mask ; + + // MeshObject + HDVF::Mesh_object_io mesh ; + mesh.read_off(options.in_file) ; + + // Build L (bounding sphere meshed with tetgen), K and L-K + + typename ToolsType::Complex_duality_data t = ToolsType::dualize_complex(mesh) ; + std::shared_ptr L(t.L_complex) ; + std::shared_ptr K(t.K_complex) ; + + // Output/export mesh and complex + + mesh_complex_output, Complex>(mesh, *L, *K, options) ; + + // HDVF computation, export, output + HDVF_type& hdvf(dual_HDVF_comput(*L, *K, options)) ; + + // Export to vtk + if (options.with_vtk_export) + { + std::cout << "----> exporting to vtk" << std::endl ; + // K + { + hdvf.set_mask_K() ; + CGAL::IO::write_VTK(hdvf, *L, options.outfile_root+"_complex_K", options.co_faces) ; + } + // L-K + { + hdvf.set_mask_L_K() ; + CGAL::IO::write_VTK(hdvf, *L, options.outfile_root+"_cocomplex_L_K", options.co_faces) ; + } + } + + // Compute pairing + dual_HDVF_pair(hdvf, options) ; + } + // CubComplex + else if ((options.in_format == InputFormat::PGM) || (options.in_format == InputFormat::CUB)) + { + using Complex = HDVF::Cubical_chain_complex ; + using HDVF_type = HDVF::Hdvf_duality ; + using SubCCType = HDVF::Sub_chain_complex_mask ; + using ToolsType = HDVF::Duality_cubical_complex_tools ; + + HDVF::Cub_object_io mesh ; + typename Complex::Cubical_complex_primal_dual primal_dual(Complex::PRIMAL) ; + if (options.primal) + { + if (options.in_format == InputFormat::PGM) + mesh.read_pgm(options.in_file, true) ; // Read with Khalimsky coordinates (for primal) + else + mesh.read_cub(options.in_file, true) ; // Read with Khalimsky coordinates (for primal) + } + else // dual + { + if (options.in_format == InputFormat::PGM) + mesh.read_pgm(options.in_file, false) ; // Read with pixel coordinates (for dual) + else + mesh.read_cub(options.in_file, false) ; // Read with pixel coordinates (for dual) + primal_dual = Complex::DUAL ; + } + + // Frame (add 1 voxel around data) + if (options.with_frame) + { + mesh.frame() ; + } + + // Build L, K and L-K + + typename ToolsType::Complex_duality_data p = ToolsType::dualize_complex(mesh, primal_dual) ; + std::shared_ptr L(p.L_complex) ; + std::shared_ptr K(p.K_complex) ; + + // Output/export mesh and complex + + mesh_complex_output, Complex>(mesh, *L, *K, options) ; + + // HDVF computation, export, output + HDVF_type& hdvf(dual_HDVF_comput(*L, *K, options)) ; + + // Export to vtk + if (options.with_vtk_export) + { + std::cout << "----> exporting to vtk" << std::endl ; + // K + { + hdvf.set_mask_K() ; + CGAL::IO::write_VTK(hdvf, *L, options.outfile_root+"_complex_K", options.co_faces) ; + } + // L-K + { + hdvf.set_mask_L_K() ; + CGAL::IO::write_VTK(hdvf, *L, options.outfile_root+"_cocomplex_L_K", options.co_faces) ; + } + } + + // Compute pairing + dual_HDVF_pair(hdvf, options) ; + } +} + +// Main + +int main(int argc, char **argv) +{ + if (argc <= 2) + usage() ; + else + { + for (int i=0;i(options) ; + } + else if (options.scalar == 2) + { + using Coefficient_ring = CGAL::Z2 ; + main_code(options) ; + } + else + { + std::cerr << "Z" << options.scalar << " not instantiated, use the #define at line 27" << std::endl ; + } +#else + typedef CGAL::Zp Coefficient_ring; +#endif + } + + return 0 ; +} + diff --git a/HDVF/examples/HDVF/main_hdvf.cpp b/HDVF/examples/HDVF/main_hdvf.cpp new file mode 100644 index 00000000000..dc3833d8aca --- /dev/null +++ b/HDVF/examples/HDVF/main_hdvf.cpp @@ -0,0 +1,266 @@ +// HDVF computation (command line version) +// -------- +// Computes a "perfect" Hdvf and provides a batch mode to specify arguments +// For help: hdvf -h +// -------- +// A. Bac +// -------- + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "arguments.h" + +namespace HDVF = CGAL::Homological_discrete_vector_field; + +using Kernel = CGAL::Simple_cartesian; +using Traits = HDVF::Hdvf_traits_3; + +// ------- A ring +// For Z/nZ other than Z (ie. n=0) and Z/2Z, uncomment and set the following define properly + +//#define SCALAR 5 + +// Hdvf computation, output and export + +template +void mesh_complex_output(const MeshType& mesh, const Complex& complex, const Options& options) +{ + if (options.with_output) + { + // Mesh + std::cout << "----> mesh informations" << std::endl ; + mesh.print_infos() ; + + // Complex + std::cout << "----> complex informations" << std::endl; + std::cout << complex; + } +} + +template +HDVF::Hdvf& HDVF_comput (const Complex& complex, const Options &options) +{ + typedef typename Complex::Coefficient_ring Coefficient_ring; + HDVF::Hdvf& hdvf(*(new HDVF::Hdvf(complex, options.HDVF_opt))); + std::vector pairs ; + if (!options.random) + pairs = hdvf.compute_perfect_hdvf(options.verbose); + else + pairs = hdvf.compute_rand_perfect_hdvf(options.verbose); + + if (options.with_output) + { + std::cout << "----> pairs found by computePerfectHDVF" << std::endl ; + std::cout << pairs ; + std::cout << "----> reduction" << std::endl ; + hdvf.write_reduction() ; + } + if (options.with_export) + { + std::string file(options.outfile_root+"_reduction.txt") ; + std::ofstream out ( file, std::ios::out | std::ios::trunc); + + if ( ! out . good () ) { + std::cerr << "hdvf: with_export. Fatal Error:\n " << file << " not found.\n"; + throw std::runtime_error("File Parsing Error: File not found"); + } + out << "----> pairs found by computePerfectHDVF" << std::endl ; + out << pairs ; + out << "----> reduction" << std::endl ; + hdvf.write_reduction(out) ; + + out.close() ; + } + return hdvf ; +} + +template +void main_code (const Options &options) +{ + /// SIMP format + if (options.in_format == InputFormat::SIMP) + { + using Complex = HDVF::Abstract_simplicial_chain_complex ; + using HDVF_type = HDVF::Hdvf ; + + // MeshObject + HDVF::Mesh_object_io mesh ; + mesh.read_simp(options.in_file) ; + + // Complex + Complex complex(mesh); + + mesh_complex_output, Complex>(mesh, complex, options) ; + + // Hdvf computation, export, output + HDVF_type hdvf(HDVF_comput(complex, options)) ; + + // Export to vtk + // None for SIMP format + } + /// OFF format + else if (options.in_format == InputFormat::OFF) + { + using Complex = HDVF::Simplicial_chain_complex ; + using HDVF_type = HDVF::Hdvf ; + + // MeshObject + HDVF::Mesh_object_io mesh ; + mesh.read_off(options.in_file) ; + + // Complex + Complex complex(mesh); + + mesh_complex_output, Complex>(mesh, complex, options) ; + + // Hdvf computation, export, output + HDVF_type hdvf(HDVF_comput(complex, options)) ; + + // Loop on operations with vtk export + if (options.loop) + { + auto output_vtk_simp = [options](HDVF_type &hdvf, Complex& complex) + { + CGAL::IO::write_VTK(hdvf, complex, options.outfile_root, options.co_faces) ; + } ; + + HDVF::interaction_loop(hdvf, complex, output_vtk_simp) ; + } + // Export to vtk + else if (options.with_vtk_export) + { + std::cout << "----> exporting to vtk" << std::endl ; + CGAL::IO::write_VTK(hdvf, complex, options.outfile_root, options.co_faces) ; + } + } + // CubComplex + else if ((options.in_format == InputFormat::PGM) || (options.in_format == InputFormat::CUB)) + { + using Complex = HDVF::Cubical_chain_complex ; + using HDVF_type = HDVF::Hdvf ; + + HDVF::Cub_object_io mesh ; + typename Complex::Cubical_complex_primal_dual primal_dual(Complex::PRIMAL) ; + if (options.primal) + { + if (options.in_format == InputFormat::PGM) + mesh.read_pgm(options.in_file, true) ; // Read with Khalimsky coordinates (for primal) + else + mesh.read_cub(options.in_file, true) ; // Read with Khalimsky coordinates (for primal) + } + else // dual + { + if (options.in_format == InputFormat::PGM) + mesh.read_pgm(options.in_file, false) ; // Read with pixel coordinates (for dual) + else + mesh.read_cub(options.in_file, false) ; // Read with pixel coordinates (for dual) + primal_dual = Complex::DUAL ; + } + + // Complex + Complex complex(mesh, primal_dual); + + mesh_complex_output, Complex>(mesh, complex, options) ; + + // Hdvf computation, export, output + HDVF_type hdvf(HDVF_comput(complex, options)) ; + + // Loop on operations with vtk export + if (options.loop) + { + auto output_vtk_cub = [options](HDVF_type &hdvf, Complex& complex) + { + CGAL::IO::write_VTK(hdvf, complex, options.outfile_root, options.co_faces) ; + } ; + + HDVF::interaction_loop(hdvf, complex, output_vtk_cub) ; + } + // Export to vtk + else if (options.with_vtk_export) + { + std::cout << "----> exporting to vtk" << std::endl ; + CGAL::IO::write_VTK(hdvf, complex, options.outfile_root, options.co_faces) ; + } + } +} + +// Main + +int main(int argc, char **argv) +{ + if (argc <= 2) + usage() ; + else + { + for (int i=0;i gamma(4) ; + std::cout << "before: " << gamma.is_null() << std::endl ; + gamma.set_coefficient(2, 1) ; + std::cout << "after: " << gamma.is_null() << std::endl ; + gamma.set_coefficient(2, 0) ; + std::cout << "after2: " << gamma.is_null() << std::endl ; + std::cout << "END TEST" << std::endl ; + + // ----- Definition of the Coefficient_ring +#ifndef SCALAR + if (options.scalar == 0) + { + using Coefficient_ring = int ; + main_code(options) ; + } + else if (options.scalar == 2) + { +// using Coefficient_ring = HDVF::Zp<2,int8_t> ; + using Coefficient_ring = CGAL::Z2 ; + main_code(options) ; + } + else + { + std::cerr << "Z" << options.scalar << " not instantiated, use the #define at line 27" << std::endl ; + } +#else + typedef CGAL::Zp Coefficient_ring; +#endif + } + + return 0 ; +} + + + + + + + + + + + + + + + diff --git a/HDVF/examples/HDVF/main_per_hdvf.cpp b/HDVF/examples/HDVF/main_per_hdvf.cpp new file mode 100644 index 00000000000..752bcc4ea03 --- /dev/null +++ b/HDVF/examples/HDVF/main_per_hdvf.cpp @@ -0,0 +1,318 @@ +// Persistent HDVF computation (command line version) +// -------- +// Computes a "perfect" persistent HDVF from a given filtration +// and provides a batch mode to specify arguments +// For help: per_hdvf -h +// -------- +// A. Bac +// -------- + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "arguments.h" + +namespace HDVF = CGAL::Homological_discrete_vector_field; + +// ------- A ring +// For Z/nZ other than Z (ie. n=0) and Z/2Z, uncomment and set the following define properly + +//#define SCALAR 5 + +using Kernel = CGAL::Simple_cartesian; +using Traits = HDVF::Hdvf_traits_3; + +// ------- Filtration + +// For a "custom" filtration, please use the following filtration function and set the +//#define CUST_FILTRATION + +// Degree type definition +#ifndef CUST_FILTRATION +using Degree = double ; +#else // Custom filtration +/// Define proper Degree below +using Degree = double ; +#endif + +// Custom filtration +#ifdef CUST_FILTRATION + +// Example returning x+z value for each vertex +template +std::function degree_function (const Complex& complex) +{ + std::function deg_fun_f = [&complex](int i) + { + const std::vector Xi(complex.point(i)) ; + /// Define proper filtration value below + return Xi.at(0)+Xi.at(2) ; + } ; + return deg_fun_f ; +} + +#endif + +template +void mesh_complex_output(const MeshType& mesh, const Complex& complex, const Options& options) +{ + if (options.with_output) + { + // Mesh + std::cout << "----> mesh informations" << std::endl ; + mesh.print_infos() ; + + // Complex + std::cout << "----> complex informations" << std::endl ; + std::cout << complex; + } +} + +template +HDVF::Hdvf_persistence& per_HDVF_comput (const Complex& complex, const FiltrationType& f, const Options &options) +{ + typedef typename Complex::Coefficient_ring CoefficientType; + typedef HDVF::Hdvf_persistence HDVF_type ; + HDVF_type& hdvf(*(new HDVF_type(complex, f, options.HDVF_opt, options.with_vtk_export))); + + std::cout << "----> START computing persistent homology" << std::endl ; + hdvf.compute_perfect_hdvf() ; + std::cout << "------> END computing persistent homology" << std::endl ; + + if (options.with_output) + { + std::cout << "----> perHDVF" << std::endl ; + hdvf.print_hdvf_persistence_info(std::cout); + std::cout << "----> reduction" << std::endl ; + hdvf.write_reduction() ; + std::cout << "----> persistent diagram" << std::endl ; + std::cout << hdvf ; + } + if (options.with_export) + { + std::cout << "----> exporting..." << std::endl ; + std::string file(options.outfile_root+"_reduction.txt") ; + std::ofstream out ( file, std::ios::out | std::ios::trunc); + + if ( ! out . good () ) { + std::cerr << "hdvf: with_export. Fatal Error:\n " << file << " not found.\n"; + throw std::runtime_error("File Parsing Error: File not found"); + } + out << "----> perHDVF" << std::endl ; + hdvf.print_hdvf_persistence_info(out); + out << "----> reduction" << std::endl ; + hdvf.write_reduction(out) ; + + out.close() ; + + std::string file_per(options.outfile_root+"_per.txt") ; + std::ofstream out_per ( file_per, std::ios::out | std::ios::trunc); + + if ( ! out_per . good () ) { + std::cerr << "hdvf: with_export. Fatal Error:\n " << file << " ! found.\n"; + throw std::runtime_error("File Parsing Error: File not found"); + } + out_per << "----> persistent diagram" << std::endl ; + out_per << hdvf ; + + out_per.close() ; + } + return hdvf ; +} + +template +FiltrationType& build_filtration(const Complex& complex, const Options& options) +{ + typedef FiltrationType Filtration; + std::function deg_function ; +#ifndef CUST_FILTRATION + // Standard lower star filtration along x,y or z + if (options.star_filtr == StarFiltrStd::FiltrX) + deg_function = (degree_function(complex, Kernel::Compute_x_3())) ; + else if (options.star_filtr == StarFiltrStd::FiltrY) + deg_function = (degree_function(complex, Kernel::Compute_y_3())) ; + else if (options.star_filtr == StarFiltrStd::FiltrZ) + deg_function = (degree_function(complex, Kernel::Compute_z_3())) ; + else + { + std::cout << "Unknown lower start filtration" << std::endl ; + throw("Unknown lower start filtration") ; + } +#else + // Use degree_function defined above + deg_function = (degree_function(complex)) ; +#endif + + std::cout << "----> START building filtration" << std::endl ; + Filtration& f = *(new Filtration(complex, deg_function)) ; + std::cout << "------> END building filtration" << std::endl ; + return f ; +} + + +template +void main_code (const Options &options) +{ + /// SIMP format + if (options.in_format == InputFormat::SIMP) + { +// using Complex = AbstractSimpComplex ; +// using HDVF_type = HDVF ; +// +// // MeshObject +// MeshObject mesh ; +// mesh.read_simp(options.in_file) ; +// +// // Complex +// Complex complex(mesh); +// +// mesh_complex_output(mesh, complex, options) ; +// +// // HDVF computation, export, output +// HDVF_type hdvf(HDVF_comput(complex, options)) ; +// +// // Export to vtk +// // None for SIMP format + std::cout << "not yet..." << std::endl ; + throw("not yet") ; + } + /// OFF format + else if (options.in_format == InputFormat::OFF) + { + using Complex = HDVF::Simplicial_chain_complex ; + using FiltrationType = HDVF::Filtration_lower_star ; + using HDVF_type = HDVF::Hdvf_persistence ; + + // MeshObject + HDVF::Mesh_object_io mesh ; + mesh.read_off(options.in_file) ; + + // Complex + Complex complex(mesh); + + // Build filtration + FiltrationType& f(build_filtration(complex, options)) ; + + mesh_complex_output, Complex>(mesh, complex, options) ; + + // HDVF computation, export, output + HDVF_type& hdvf(per_HDVF_comput(complex,f, options)) ; + + // Export to vtk + if (options.with_vtk_export) + { + std::cout << "----> exporting to vtk" << std::endl ; + CGAL::IO::write_VTK(hdvf, complex, options.outfile_root, options.co_faces) ; + } + } + // CubComplex + else if ((options.in_format == InputFormat::PGM) || (options.in_format == InputFormat::CUB)) + { + using Complex = HDVF::Cubical_chain_complex ; + using FiltrationType = HDVF::Filtration_lower_star ; + using HDVF_type = HDVF::Hdvf_persistence ; + + HDVF::Cub_object_io mesh ; + typename Complex::Cubical_complex_primal_dual primal_dual(Complex::PRIMAL) ; + if (options.primal) + { + if (options.in_format == InputFormat::PGM) + mesh.read_pgm(options.in_file, true) ; // Read with Khalimsky coordinates (for primal) + else + mesh.read_cub(options.in_file, true) ; // Read with Khalimsky coordinates (for primal) + } + else // dual + { + if (options.in_format == InputFormat::PGM) + mesh.read_pgm(options.in_file, false) ; // Read with pixel coordinates (for dual) + else + mesh.read_cub(options.in_file, false) ; // Read with pixel coordinates (for dual) + primal_dual = Complex::DUAL ; + } + + // Complex + Complex complex(mesh, primal_dual); + + mesh_complex_output, Complex>(mesh, complex, options) ; + + // Build filtration + FiltrationType& f(build_filtration(complex, options)) ; + + // HDVF computation, export, output + HDVF_type& hdvf(per_HDVF_comput(complex,f, options)) ; + + // Export to vtk + if (options.with_vtk_export) + { + std::cout << "----> exporting to vtk" << std::endl ; + CGAL::IO::write_VTK(hdvf, complex, options.outfile_root, options.co_faces) ; + } + } +} + +// Main + +int main(int argc, char **argv) +{ + if (argc <= 2) + usage() ; + else + { + for (int i=0;i(options) ; + } + else if (options.scalar == 2) + { + using CoefficientType = CGAL::Z2 ; + main_code(options) ; + } + else + { + std::cerr << "Z" << options.scalar << " not instantiated, use the #define at line 27" << std::endl ; + } +#else + typedef CGAL::Zp CoefficientType; +#endif + } + + return 0 ; +} + + + + + + + + + + + + + + + diff --git a/HDVF/examples/HDVF/matrix_chain.cpp b/HDVF/examples/HDVF/matrix_chain.cpp new file mode 100644 index 00000000000..e028502927e --- /dev/null +++ b/HDVF/examples/HDVF/matrix_chain.cpp @@ -0,0 +1,35 @@ +#include +#include + +#include + +typedef CGAL::OSM::Sparse_chain Column_chain; +typedef CGAL::OSM::Sparse_matrix Column_matrix; + +int main () +{ + // Create a column-major sparse matrix + Column_matrix M(5,4) ; + + // Fill coefficients + CGAL::OSM::set_coefficient(M, 0, 1, 1) ; + CGAL::OSM::set_coefficient(M, 0, 2, -1) ; + CGAL::OSM::set_coefficient(M, 2, 1, 2) ; + + // Iterate over non empty columns + for(CGAL::OSM::Bitboard::iterator it_col = M.begin(); it_col != M.end(); ++it_col) + { + std::cout << "col: " << *it_col << std::endl ; + // Get a constant reference over the column (complexity O(1)) + const Column_chain& col(CGAL::OSM::cget_column(M, *it_col)); + // Iterate over the column + for (Column_chain::const_iterator it = col.begin(); it != col.end(); ++it) + { + std::cout << "row: " << it->first << " - coef: " << it->second << std::endl ; + } + } + // Direct output of the matrix with << operator + std::cout << "M: " << M << std::endl; + + return 0 ; +} diff --git a/HDVF/examples/HDVF/persistent_hdvf_simplicial.cpp b/HDVF/examples/HDVF/persistent_hdvf_simplicial.cpp new file mode 100644 index 00000000000..4ff0f53571a --- /dev/null +++ b/HDVF/examples/HDVF/persistent_hdvf_simplicial.cpp @@ -0,0 +1,108 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace HDVF = CGAL::Homological_discrete_vector_field; + +//typedef int Coefficient_ring; +//typedef CGAL::Z2 Coefficient_ring; +typedef CGAL::Zp<5, char, true> Coefficient_ring; + +typedef CGAL::Simple_cartesian Kernel; +typedef HDVF::Hdvf_traits_3 Traits; +typedef Kernel::Compute_z_3 Compute_z; +typedef Kernel::Point_3 Point_3; + +typedef HDVF::Simplicial_chain_complex Complex; +typedef double Degree; +typedef HDVF::Filtration_lower_star FiltrationType; +typedef HDVF::Hdvf_persistence HDVF_type; + + +int main(int argc, char **argv) +{ + + std::string filename; + if (argc > 2) std::cout << "usage: persistent_hdvf_simplicial off_file" << std::endl; + else if (argc == 1) filename = "data/mesh_data/two_rings.off"; + else filename = argv[1]; + + // Load cub object + HDVF::Mesh_object_io mesh ; + mesh.read_off(filename); + + mesh.print_infos(); + + // Build simplicial chain complex + Complex complex(mesh); + + std::cout << complex; + + /* Example 1 : build a lower star filtration "slicing" the object according to the "z" coordinate of vertices + -> GeometricChainComplex required in this case + */ + { + // --- First: build the function mapping the index of a vertex to its degree (here the "z" coordinate of a vertex + Compute_z compute_z = Kernel().compute_z_3_object() ; + std::function f(HDVF::degree_function(complex, compute_z)); + + // -- Second: build the associated lower star filtration + FiltrationType filtration(complex, f); + + + // Build empty persistent HDVF (with vtk export activated) + HDVF_type hdvf(complex, filtration, HDVF::OPT_FULL, true); + + // Compute a perfect HDVF + hdvf.compute_perfect_hdvf(); + + // Output HDVF to console + hdvf.write_matrices(); + hdvf.write_reduction(); + std::cout << hdvf ; + + // Output HDVF to vtk + CGAL::IO::write_VTK(hdvf, complex, "tmp/res", true) ; + } + + /* Example 2 : build a lower star filtration "slicing" the object according to the index of vertices (we can imagine any other filtration that does not depend on the geometry (e.g. color of vertices...) + -> AbstractChainComplex in this case + */ + { + // --- First: build the function mapping the index of a vertex to its degree (here the index itself) + std::function f = [&complex](size_t i) + { + return Degree(i) ; + } ; + + // -- Second: build the associated lower star filtration + FiltrationType filtration(complex, f); + + + // Build empty persistent HDVF (with vtk export activated) + HDVF_type hdvf(complex, filtration, HDVF::OPT_FULL, true); + + // Compute a perfect HDVF + hdvf.compute_perfect_hdvf(); + + // Output HDVF to console + hdvf.write_matrices(); + hdvf.write_reduction(); + std::cout << hdvf ; + + // Output HDVF to vtk + CGAL::IO::write_VTK(hdvf, complex, "tmp/res2", true) ; + } + + return 0; +} diff --git a/HDVF/examples/HDVF/test_hdvf_example.cpp b/HDVF/examples/HDVF/test_hdvf_example.cpp new file mode 100644 index 00000000000..682a68c23a2 --- /dev/null +++ b/HDVF/examples/HDVF/test_hdvf_example.cpp @@ -0,0 +1,311 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace HDVF = CGAL::Homological_discrete_vector_field; + +typedef int CoefficientType; +//typedef CGAL::Zp<5,int,true> CoefficientType; +//typedef CGAL::Z2 CoefficientType; + +typedef CGAL::Simple_cartesian Kernel; +typedef HDVF::Hdvf_traits_3 Traits; + +int main(int argc, char **argv) +{ + using Complex = HDVF::Cubical_chain_complex ; + using HDVF_type = HDVF::Hdvf ; + + if (argc != 2) + { + std::cout << "usage: test_CubObject cub_file" << std::endl; + } + else + { + // Load cub object + HDVF::Cub_object_io mesh ; + mesh.read_cub(argv[1], true); + + mesh.print_infos(); + + // Build complex (PRIMAL construction) + Complex complex(mesh, Complex::PRIMAL); + + std::cout << complex; + + // Build empty HDVF + HDVF_type hdvf(complex, HDVF::OPT_FULL) ; + + // Build HDVF step by step + // hdvf.A(7,0,1); + // hdvf.A(2,5,0); + // hdvf.A(3,8,0); + hdvf.A(5,0,1); + std::cout << "Step 1: is_perfect_hdvf " << hdvf.is_perfect_hdvf() << std::endl ; + + hdvf.compute_perfect_hdvf(); + // hdvf.compute_rand_perfect_hdvf(); + std::cout << "Step 2: is_perfect_hdvf " << hdvf.is_perfect_hdvf() << std::endl ; + + // Output HDVF to console + hdvf.write_matrices(); + hdvf.write_reduction(); + + // Output HDVF to vtk + CGAL::IO::write_VTK(hdvf, complex, "res") ; + + // Test get_annotation + + // Compute the annotation of cycle1 in the homology basis + std::vector > crit = hdvf.psc_flags(HDVF::CRITICAL); + std::vector criticals = hdvf.psc_flags(HDVF::CRITICAL, 1) ; + HDVF_type::Column_chain cycle1(hdvf.homology_chain(criticals.at(0), 1)) ; + HDVF_type::Column_chain annot1(hdvf.get_annotation(cycle1,1)); + std::cout << "Cycle1:" << cycle1 << std::endl ; + std::cout << "Annotation of cycle 1: " << annot1 << std::endl ; + + // Compute the annotation of cycle2 (outer cycle) in the homology basis + HDVF_type::Column_chain cycle2(complex.number_of_cells(1)) ; + cycle2.set_coefficient(0, 1) ; + cycle2.set_coefficient(1, 1) ; + cycle2.set_coefficient(2, 1) ; + cycle2.set_coefficient(3, 1) ; + cycle2.set_coefficient(4, 1) ; + cycle2.set_coefficient(9, 1) ; + cycle2.set_coefficient(7, 1) ; + cycle2.set_coefficient(10, 1) ; + cycle2.set_coefficient(11, 1) ; + cycle2.set_coefficient(12, 1) ; + HDVF_type::Column_chain annot2(hdvf.get_annotation(cycle2,1)); + std::cout << "Cycle2:" << cycle1 << std::endl ; + CGAL::IO::write_VTK(complex, "cycle2.vtk", cycle2, 1) ; + std::cout << "Annotation of cycle 2: " << annot2 << std::endl ; + + // Test get_coannotation + + // Compute the co-annotation of cycle1 in the cohomology basis + HDVF_type::Row_chain cocycle1((hdvf.cohomology_chain(criticals.at(0), 1)).transpose()) ; + HDVF_type::Row_chain coannot1(hdvf.get_coannotation(cocycle1,1)); + std::cout << "Co-cycle1:" << cocycle1 << std::endl ; + std::cout << "Co-annotation of co-cycle 1: " << coannot1 << std::endl ; + + + // Compute the co-annotation of cycle2 (outer cycle) in the homology basis + HDVF_type::Row_chain cocycle2(complex.number_of_cells(1)) ; + cocycle2.set_coefficient(5, 1) ; + cocycle2.set_coefficient(6, 1) ; + HDVF_type::Row_chain coannot2(hdvf.get_coannotation(cocycle2,1)); + std::cout << "Co-cycle2: " << cocycle2 << std::endl ; + std::cout << "Co-annotation of co-cycle 2: " << coannot2 << std::endl ; + + // Test are_same_cycles + HDVF_type::Column_chain cycle3(cycle1) ; + cycle3 += CGAL::OSM::cget_column(complex.boundary_matrix(2), 0); // Add the boundary of the 2-cell + std::cout << "Cycle3: " << cycle3 << std::endl ; + Complex::chain_to_vtk(complex, "cycle3.vtk", cycle3, 1) ; + std::cout << "are_same_cycles cycle1 and cycle3: " << hdvf.are_same_cycles(cycle1, cycle3, 1) << std::endl ; + + HDVF_type::Column_chain cycle4(cycle3) ; + cycle4 += hdvf.homology_chain(criticals.at(1), 1) ; // Cycle4: cycle3 + second hole + std::cout << "Cycle4: " << cycle4 << std::endl ; + CGAL::IO::write_VTK(complex, "cycle4.vtk", cycle4, 1) ; + std::cout << "are_same_cycles cycle1 and cycle4: " << hdvf.are_same_cycles(cycle1, cycle4, 1) << std::endl ; + + // Test are_same_cocycles + HDVF_type::Row_chain cocycle3(cocycle1) ; + cocycle3 += CGAL::OSM::get_row(complex.boundary_matrix(1), 0); // Add the coboundary of 0-cell + std::cout << "Coycle3: " << cocycle3 << std::endl ; + std::cout << "are_same_cocycles cocycle1 and cocycle3: " << hdvf.are_same_cocycles(cocycle1, cocycle3, 1) << std::endl ; + + HDVF_type::Row_chain cocycle4(cocycle3) ; + cocycle4 += (hdvf.cohomology_chain(criticals.at(1), 1).transpose()) ; // Cycle4: cycle3 + second cohomology generator + std::cout << "Cocycle4: " << cocycle4 << std::endl ; + std::cout << "are_same_cocycles cocycle1 and cocycle4: " << hdvf.are_same_cocycles(cocycle1, cocycle4, 1) << std::endl ; + + std::cout << "--------------" << std::endl ; + + typedef CGAL::OSM::Sparse_chain Column_chain; + typedef CGAL::OSM::Sparse_chain Row_chain; + typedef CGAL::OSM::Sparse_matrix Column_matrix; + typedef CGAL::OSM::Sparse_matrix Row_matrix; + // Create a column-major sparse matrix + Column_matrix M(5,4) ; + + // Fill coefficients + CGAL::OSM::set_coefficient(M, 0, 1, 1) ; + CGAL::OSM::set_coefficient(M, 0, 2, -1) ; + CGAL::OSM::set_coefficient(M, 2, 1, 2) ; + + // Iterate over non empty columns + for(CGAL::OSM::Bitboard::iterator it_col = M.begin(); it_col != M.end(); ++it_col) + { + std::cout << "col: " << *it_col << std::endl ; + // Get a constant reference over the column (complexity O(1)) + const Column_chain& col(CGAL::OSM::cget_column(M, *it_col)); + // Iterate over the column + for (Column_chain::const_iterator it = col.begin(); it != col.end(); ++it) + { + std::cout << "row: " << it->first << " - coef: " << it->second << std::endl ; + } + } + // Direct output of the matrix with << operator + std::cout << "M: " << M << std::endl; + + // Create a row-major sparse matrix + Row_matrix MM(5,4) ; + + // Fill coefficients + CGAL::OSM::set_coefficient(MM, 0, 1, 1) ; + CGAL::OSM::set_coefficient(MM, 0, 2, -1) ; + CGAL::OSM::set_coefficient(MM, 2, 1, 2) ; + + // Test write_matrix + const std::string filename("test.osm") ; + + std::ofstream out ( filename, std::ios::out | std::ios::trunc); + if ( ! out . good () ) { + std::cerr << "Out fatal Error:\n " << filename << " not found.\n"; + throw std::runtime_error("File Parsing Error: File not found"); + } + + write_matrix(M, out); + write_matrix(MM, out); + + out.close(); + + std::ifstream in ( filename ); + if ( ! in . good () ) { + std::cerr << "In fatal Error:\n " << filename << " not found.\n"; + throw std::runtime_error("File Parsing Error: File not found"); + } + Column_matrix M2 ; + Row_matrix MM2 ; + + read_matrix(M2, in) ; + read_matrix(MM2, in) ; + + in.close(); + + out << "M:" << M << std::endl ; + out << "M2:" << M2 << std::endl ; + out << "MM:" << MM << std::endl ; + out << "MM2:" << MM2 << std::endl ; + + Column_matrix M3 ; + write_matrix(M3,out); + + // Test save_hdvf + + // Test save_hdvf + const std::string filename2("test.hdvf") ; + + std::ofstream out2 ( filename2, std::ios::out | std::ios::trunc); + if ( ! out2 . good () ) { + std::cerr << "Out fatal Error:\n " << filename2 << " not found.\n"; + throw std::runtime_error("File Parsing Error: File not found"); + } + hdvf.write_hdvf_reduction(out2) ; + + out2.close() ; + + std::ifstream in2 ( filename2 ); + if ( ! in2 . good () ) { + std::cerr << "In fatal Error:\n " << filename << " not found.\n"; + throw std::runtime_error("File Parsing Error: File not found"); + } + + HDVF_type hdvf2(complex); + + hdvf2.read_hdvf_reduction(in2) ; + + in2.close(); + + out << "=============== HDVF" << std::endl ; + hdvf.write_reduction() ; + out << "=============== HDVF2" << std::endl ; + hdvf2.write_reduction() ; + } + + // Test == operators + + // CChains + HDVF_type::Column_chain c1(4), c2(4), c3 ; + c1.set_coefficient(1,1) ; + c1.set_coefficient(3,-1) ; + + c2.set_coefficient(3,-1) ; + c2.set_coefficient(1,1) ; + + c3 = c2 ; + c3.set_coefficient(2, 1) ; + + std::cout << "c1: " << c1 << std::endl ; + std::cout << "c2: " << c2 << std::endl ; + std::cout << "c3: " << c3 << std::endl ; + std::cout << "c1 == c2 : " << (c1 == c2) << std::endl ; + std::cout << "c1 == c3 : " << (c1 == c3) << std::endl ; + + // CMatrices + HDVF_type::Column_matrix M1(3,4), M2(3,4), M3(3,4) ; + CGAL::OSM::set_coefficient(M1, 0, 1, 1) ; + CGAL::OSM::set_coefficient(M1, 0, 2, -1) ; + CGAL::OSM::set_coefficient(M1, 1, 1, 2) ; + CGAL::OSM::set_coefficient(M1, 2, 1, -2) ; + + CGAL::OSM::set_coefficient(M2, 0, 1, 1) ; + CGAL::OSM::set_coefficient(M2, 2, 1, -2) ; + CGAL::OSM::set_coefficient(M2, 0, 2, -1) ; + CGAL::OSM::set_coefficient(M2, 1, 1, 2) ; + + M3 = M2 ; + CGAL::OSM::set_coefficient(M3, 2, 2, 3) ; + + std::cout << "M1 == M2 : " << (M1 == M2) << std::endl ; + std::cout << "M1 == M3 : " << (M1 == M3) << std::endl ; + + // RChains + HDVF_type::Row_chain cc1(4), cc2(4), cc3 ; + cc1.set_coefficient(1,1) ; + cc1.set_coefficient(3,-1) ; + + cc2.set_coefficient(3,-1) ; + cc2.set_coefficient(1,1) ; + + cc3 = cc2 ; + cc3.set_coefficient(2, 1) ; + + std::cout << "cc1: " << cc1 << std::endl ; + std::cout << "cc2: " << cc2 << std::endl ; + std::cout << "cc3: " << cc3 << std::endl ; + std::cout << "cc1 == cc2 : " << (cc1 == cc2) << std::endl ; + std::cout << "cc1 == cc3 : " << (cc1 == cc3) << std::endl ; + + // CMatrices + HDVF_type::Row_matrix MM1(3,4), MM2(3,4), MM3(3,4) ; + CGAL::OSM::set_coefficient(MM1, 0, 1, 1) ; + CGAL::OSM::set_coefficient(MM1, 0, 2, -1) ; + CGAL::OSM::set_coefficient(MM1, 1, 1, 2) ; + CGAL::OSM::set_coefficient(MM1, 2, 1, -2) ; + + CGAL::OSM::set_coefficient(MM2, 0, 1, 1) ; + CGAL::OSM::set_coefficient(MM2, 2, 1, -2) ; + CGAL::OSM::set_coefficient(MM2, 0, 2, -1) ; + CGAL::OSM::set_coefficient(MM2, 1, 1, 2) ; + + MM3 = MM2 ; + CGAL::OSM::set_coefficient(MM3, 2, 2, 3) ; + + std::cout << "MM1 == MM2 : " << (MM1 == MM2) << std::endl ; + std::cout << "MM1 == MM3 : " << (MM1 == MM3) << std::endl ; + + return 0; +} diff --git a/HDVF/include/CGAL/HDVF/Abstract_simplicial_chain_complex.h b/HDVF/include/CGAL/HDVF/Abstract_simplicial_chain_complex.h new file mode 100644 index 00000000000..d29b5658519 --- /dev/null +++ b/HDVF/include/CGAL/HDVF/Abstract_simplicial_chain_complex.h @@ -0,0 +1,448 @@ +// Copyright (c) 2025 LIS Marseille (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Alexandra Bac + +#ifndef CGAL_HDVF_ABSTRACT_SIMPLICIAL_CHAIN_COMPLEX_H +#define CGAL_HDVF_ABSTRACT_SIMPLICIAL_CHAIN_COMPLEX_H + +#include + +#include +#include +#include +#include +#include +#include + +namespace CGAL { +namespace Homological_discrete_vector_field { + +/*! + \ingroup PkgHDVFRef + + The class `Abstract_simplicial_chain_complex` represents (topological) chain complexes associated to abstract simplicial complexes. + + An abstract simplicial complex is a set of simplices, also called cells (class `Simplex`) such that: all the faces of a given simplex also belong to the complex and any two simplices intersect exactly along a common face. + + + + A simplex of dimension q contains exactly q+1 vertices (we will thus denote it by \f$\langle v_0, \ldots, v_q \rangle\f$, vertex indices **must be sorted**). A 0-cell is thus a vertex, a 1-cell contains two vertices (edge), a 2-cell contains three vertices (triangle) while a 3-cell is a tetrahedron. + + The boundary map of the complex is computed by the constructor of the class using the standard formula: + \f[ \partial_q\left( \langle v_0, \ldots, v_q \rangle\right) = \sum_{i=0}^q (-1)^i\cdot \langle v_0, \ldots, \widehat{v_i}, \cdots, v_q \rangle\f] + where \f$\langle v_0, \ldots, \widehat{v_i}, \cdots, v_q \rangle\f$ denotes the \f$q-1\f$-simplex with \f$v_i\f$ omitted. Hence, matrices of boundary maps are stored in each dimension using sparse matrices (in column-major mode). + + Let us also point out that cells are indexed along each dimension, thus each simplex is uniquely determined by its dimension and its index in this dimension. + + \cgalModels{AbstractChainComplex} + + \tparam CoefficientRing a model of the `IntegralDomainWithoutDivision` concept. + */ + + +template +class Abstract_simplicial_chain_complex { +public: + /*! \brief Type of coefficients used to compute homology. */ + typedef CoefficientRing Coefficient_ring; + + /** + * \brief %Default constructor (empty simplicial complex of dimension `q`). + * + * Builds an empty abstract simplicial complex of dimension `q`. + */ + Abstract_simplicial_chain_complex(int q = 0) ; + + /** + * \brief Constructor from a `Mesh_object_io`. + * + * Builds the abstract simplicial complex associated to a triangular mesh (i.e. performs the down closure of cells and set the boundary matrices in any dimension). + * + * \param mesh A `Mesh_object_io` containing a triangular mesh. + */ + template + Abstract_simplicial_chain_complex(const Mesh_object_io& mesh); + + /** \brief Type of column-major chains */ + typedef CGAL::OSM::Sparse_chain Column_chain; + /** \brief Type of row-major chains */ + typedef CGAL::OSM::Sparse_chain Row_chain ; + /** \brief Type of column-major sparse matrices */ + typedef CGAL::OSM::Sparse_matrix Column_matrix; + + + /** + * \brief Assignment operator for abstract simplicial chain complexes. + * + * Stores a copy of an abstract simplicial chain complex in `*this`. + * + * \param complex The abstract simplicial chain complex which will be copied. + */ + Abstract_simplicial_chain_complex& operator= (const Abstract_simplicial_chain_complex& complex) + { + _dim = complex._dim; + _ind2simp = complex._ind2simp; + _simp2ind = complex._simp2ind; + _nb_cells = complex._nb_cells; + _d = complex._d; + return *this ; + } + + // Methods of the AbstractChainComplex concept + + /** + * \brief Returns the boundary of the cell id_cell in dimension q. + * + * Returns a copy of the column-major chain stored in the boundary matrix of dimension dim: boundary of the cell id_cell in dimension q. + * + * \param id_cell %Index of the cell. + * \param q Dimension of the cell. + * + * \return The column-major chain containing the boundary of the cell id_cell in dimension q. + */ + Column_chain d(size_t id_cell, int q) const + { + if ((q > 0) && (q <= _dim)) + return OSM::get_column(_d[q], id_cell); + else + return Column_chain(0) ; + } + + /** + * \brief Returns the co-boundary of the cell id_cell in dimension q. + * + * Returns a row-major chain containing the co-boundary of the cell id_cell in dimension q (so actually a row of the boundary matrix). + * + * \warning As the boundary matrix is stored column-major, this entails crossing the full matrix to extract the row coefficients (O(number of non empty columns)) + * + * \param id_cell %Index of the cell. + * \param q Dimension of the cell. + * + * \return The row-major chain containing the co-boundary of the cell id_cell in dimension q. + */ + Row_chain cod(size_t id_cell, int q) const + { + if ((q < _dim) && (q >= 0)) + return OSM::get_row(_d[q+1], id_cell); + else + return Row_chain(0) ; + } + + /** + * \brief Returns the dimension of the complex. + * + * Returns the dimension of the simplicial complex (i.e. largest dimension of cells). + * + * \return The dimension of the complex. + */ + int dimension() const { return _dim ;} + + /** + * \brief Returns the number of cells in a given dimension. + * + * \param q Dimension along which the number of cells is returned. + * + * \return Number of cells in dimension q. + */ + size_t number_of_cells(int q) const + { + if ((q >=0) && (q<=_dim)) + return _nb_cells[q] ; + else + return 0 ; + } + + /** \brief Returns the simplex of index i in dimension q. */ + const Simplex& index_to_cell (size_t i, int q) const + { + return _ind2simp.at(q).at(i); + } + + /** \brief Returns the index of a given simplex. */ + size_t cell_to_index (const Simplex& simplex) const + { + const int q(simplex.dimension()); + return _simp2ind.at(q).at(simplex); + } + + /** + * \brief Returns a constant reference to the vector of boundary matrices (along each dimension). + * + * Returns a constant reference to the vector of boundary matrices along each dimension. The q-th element of this vector is a column-major sparse matrix containing the boundaries of q-cells (i.e. rows encode q-1 cells and columns q cells). + * + * \return Returns a constant reference to the vector of column-major boundary matrices along each dimension. + */ + const std::vector & boundary_matrices() const + { + return _d ; + } + + /** + * \brief Returns a copy of the dim-th boundary matrix (i.e.\ column-major matrix of \f$\partial_q\f$). + * + * It is a column-major sparse matrix containing the boundaries of q-cells (i.e. rows encode q-1 cells and columns q cells). + * + * \param q Dimension of the boundary matrix (i.e. columns will contain the boundary of dimension q cells). + * + * \return A column-major sparse matrix containing the matrix of the boundary operator of dimension q. + */ + const Column_matrix & boundary_matrix(int q) const + { + return _d.at(q) ; + } + + /** + * \brief Returns 0-simplex indices included in the cell with index id_cell of dimension q. + * + * Returns the 0-simplex indices included in the cell with index id_cell of dimension q. + * + * \warning This does not come to return vertex indices, as dimension 0 simplices enumerate vertices in any order. For instance, if an abstract simplicial complex is built from 3 vertices {1,2,3} such that the enumeration of dimension 0 simplices is: + * id0: 3, id1 : 2, id2: 1 + * then the bottom_faces of the 1-simplex {1,2} are two 0-simplices with id 2 and 1. + * + * \param id_cell %Index of the cell. + * \param q Dimension of the cell. + * + * \return A vector of 0-simplex indices. + */ + std::vector bottom_faces(size_t id_cell, int q) const + { + std::vector verts(_ind2simp.at(q).at(id_cell).vertices()) ; + std::vector res ; + // For each vertex in verts, compute the corresponding dimension 0 cell + for (size_t vert_id : verts) + { + const size_t i(_simp2ind.at(0).at(Simplex(std::vector({vert_id})))) ; + res.push_back(i) ; + } + return res ; + } + + /*! + * \brief Returns the cofaces of a given chain in dimension `q`. + * + * The resulting chain lies in dimension `q`+1 and is null if this dimension exceeds the dimension of the complex. + */ + template + Column_chain cofaces_chain (OSM::Sparse_chain chain, int q) const + { + typedef OSM::Sparse_chain ChainType; + // Compute the cofaces + if (q < dimension()) + { + Column_chain fstar_cofaces(number_of_cells(q+1)) ; + for (typename ChainType::const_iterator it = chain.cbegin(); it != chain.cend(); ++it) + { + // Set the cofaces of it->first in dimension dim+1 + Row_chain cofaces(cod(it->first,q)) ; + for (typename Row_chain::const_iterator it2 = cofaces.cbegin(); it2 != cofaces.cend(); ++it2) + fstar_cofaces.set_coefficient(it2->first, 1) ; + } + return fstar_cofaces ; + } + else + return Column_chain(0) ; + } + +protected: + /* + * \brief Prints informations on the complex. + * + * Displays the number of cells in each dimension and the boundary matrix in each dimension. + */ + std::ostream& print_complex(std::ostream& out = std::cout) const; +public: + /** + * \brief Prints informations on the complex. + * + * Displays the number of cells in each dimension and the boundary matrix in each dimension. + */ + template + friend std::ostream& operator<< (std::ostream& out, const Abstract_simplicial_chain_complex<_CT>& complex); + + /** \brief Get (unique) object Id. + * For comparison of constant references to the complex. + */ + size_t get_id () const { return _complex_id; } + +protected: + /* \brief Dimension of the complex */ + int _dim; + /* \brief Vector of simplices along each dimension: _ind2simp.at(q) contains the vector of all simplices of dimension q */ + std::vector > _ind2simp ; + /* \brief Vector of maps associating indices to simplices in each dimension: _simp2ind.at(q) maps q-simplices to their index in _ind2simp */ + std::vector > _simp2ind ; + /* \brief Vector of number of cells in each dimension */ + std::vector _nb_cells ; + /* \brief Vector of column-major boundary matrices: _d.at(q) is the matrix of the boundary operator in dimension q. */ + mutable std::vector _d; // Boundary matrices + + // Protected methods + + /* + * \brief Method filling the matrix _d.at(q), i.e. computing the boundaries of cells of dimension q. + * + * Compute the boundary with the standard simplicial complex definition: + * \f[\partial (\{v_0,\ldots,v_q\}) = \sum_{i=0}^q (-1)^q \{v_0,\ldots, \hat{v_i},\ldots,v_q\}\f] + * + * \param q Dimension considered for computation. + */ + void compute_d(int q) const; + + /* + * \brief Method inserting a simplex (and its faces if necessary) into the abstract simplicial complex. + * + * The method (recursively) inserts the simplex into `_ind2simp` and `_simp2ind` and its faces. If the simplex is already present, the method does nothing. + * + * \warning `insert_simplex()` does not update the boundary matrix. + * + * \param tau The simplex inserted. + */ + void insert_simplex(const Simplex& tau); + +private: + /* \brief Static counter for objects ids. + * Initialized to 0. + */ + static size_t _id_generator ; // Initialisation 0 + /* \brief Unique object id (for comparison of constant references to the complex). */ + const size_t _complex_id ; +}; + +// Initialization of _id_generator +template +size_t Abstract_simplicial_chain_complex::_id_generator(0); + +//constructors +template +Abstract_simplicial_chain_complex::Abstract_simplicial_chain_complex(int q) : _dim(q), _complex_id(_id_generator++) { + // Initialize vectors of Simplices and cell counts + _ind2simp.resize(_dim + 1); + _simp2ind.resize(_dim + 1); + _nb_cells.resize(_dim + 1, 0); + // Initialize boundary matrix + _d.resize(_dim+1); +} + +template +template +Abstract_simplicial_chain_complex::Abstract_simplicial_chain_complex(const Mesh_object_io& mesh) : _complex_id(_id_generator++) { + // Initialize attributes + + _dim = std::abs(mesh.dim); + + // Initialize vectors of simplices and cell counts + _ind2simp.resize(_dim + 1); + _simp2ind.resize(_dim + 1); + _nb_cells.resize(_dim + 1, 0); + + // Iterate through the mesh cells and add them to the complex + for (const auto& cell : mesh.cells) { + insert_simplex(cell) ; + } + + _d.resize(_dim+1); + for (int dim = 0; dim <= _dim; ++dim) { + compute_d(dim); + } +} + +// Recursive insertion method +template +void Abstract_simplicial_chain_complex::insert_simplex(const Simplex& tau) { + int q = tau.dimension(); + if (q == -1) return; + + if (_simp2ind[q].find(tau) == _simp2ind[q].end()) { + size_t i = _ind2simp[q].size(); + _ind2simp[q].push_back(tau); + _simp2ind[q][tau] = i; + _nb_cells[q]++; + + std::vector bord = tau.boundary(); + for (const auto& sigma : bord) { + insert_simplex(sigma); + } + } +} + +// compute _d boundary matrix +template +void Abstract_simplicial_chain_complex::compute_d(int dim) const { + size_t nb_lignes = (dim == 0) ? 0 : _nb_cells[dim - 1]; + _d[dim] = Column_matrix(nb_lignes, _nb_cells[dim]); + + // Iterate through the cells of dimension dim + if (dim>0) + { + for (size_t i = 0; i < _nb_cells[dim]; ++i) { + // Boundary of the i-th simplex of dimension dim + const Simplex& s = _ind2simp[dim][i]; + std::vector bord = s.boundary(); + + // Create a chain with the correct size + Column_chain chain(nb_lignes); + + // For each element of the boundary + for (size_t j = 0; j < bord.size(); ++j) { + auto it = _simp2ind[dim - 1].find(bord[j]); // Find the index of Simplex j + if (it != _simp2ind[dim - 1].end()) { // If Simplex j is found + size_t ind_j = it->second; // Retrieve the index of Simplex j + chain.set_coefficient(ind_j, (j % 2 == 0) ? 1 : -1); + } + else + throw "compute_d boundary simplex not found!"; + } + + // Insert the chain into the corresponding column of the delta matrix + OSM::set_column(_d[dim], i, chain); + } + } +} + +// Method to display the complex's information +template +std::ostream& Abstract_simplicial_chain_complex::print_complex(std::ostream& out_stream) const { + out_stream << "Complex dimension: " << _dim << std::endl; + + // Total number of cells + size_t nb_total_cells = 0; + for (size_t i = 0; i <= _dim; ++i) { + nb_total_cells += _nb_cells[i]; + } + out_stream << "Total number of cells: " << nb_total_cells << std::endl; + + // Cells per dimension + for (int q = 0; q <= _dim; ++q) { + out_stream << "--- dimension " << q << std::endl; + out_stream << number_of_cells(q) << " cells" << std::endl ; + for (size_t j = 0; j < _nb_cells.at(q); ++j) { + Simplex s(this->_ind2simp.at(q).at(j)); + out_stream << j << " -> " << s << " -> " << _simp2ind.at(q).at(s) << std::endl; + } + } + + // Boundary matrices + out_stream << "---------------------------" << std::endl << "Boundary matrices" << std::endl; + for (int q = 1; q <= _dim; ++q) + out_stream << "_d[" << q << "] : " << _d[q].dimensions().first << "x" << _d[q].dimensions().second << std::endl << _d[q] << std::endl; + return out_stream ; +} + +template +std::ostream& operator<< (std::ostream& out, const Abstract_simplicial_chain_complex& complex) +{ + return complex.print_complex(out); +} + +} /* end namespace Homological_discrete_vector_field */ +} /* end namespace CGAL */ + +#endif // CGAL_HDVF_ABSTRACT_SIMPLICIAL_CHAIN_COMPLEX_H diff --git a/HDVF/include/CGAL/HDVF/Cub_object_io.h b/HDVF/include/CGAL/HDVF/Cub_object_io.h new file mode 100644 index 00000000000..79d0ab29973 --- /dev/null +++ b/HDVF/include/CGAL/HDVF/Cub_object_io.h @@ -0,0 +1,315 @@ +// Copyright (c) 2025 LIS Marseille (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Alexandra Bac + +#ifndef CGAL_HDVF_CUB_OBJECT_H +#define CGAL_HDVF_CUB_OBJECT_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace CGAL { +namespace Homological_discrete_vector_field { + +// ------ For cubical complexes +/** \brief Type of cells coordinates in Cub_object_io (Khalimsky or voxel coordinates) */ +typedef std::vector IOCubCellType ; +/** \brief Type of pre-chains in Cub_object_io (list of cells without coefficients). */ +typedef std::vector IOCubChainType ; + +/*! + \ingroup PkgHDVFIOClasses + The class `Cub_object_io` is an intermediate %IO class, used to load binary volumes and produce cubical complexes. + + */ + +template +class Cub_object_io +{ +public: + int dim = 0 ; // Dimension of the complex + std::vector ncubs ; // Number of cubs in each dimension + std::vector N ; // Size of BB along each dimension + std::vector cubs ; + bool khalimsky ; + + /* \brief Default constructor. + * + * Create an empty Cub_object_io of dimension 3. + */ + Cub_object_io() : dim(3), ncubs(std::vector(4)), N(std::vector(4)), khalimsky(false) {} + + /** + * \brief Constructor from a vector of cells. + * + * Cells coordinates are given in Khalimsky coordinates if the boolean `khal` is `true`, and as integer indices of voxels otherwise. + * + */ + Cub_object_io(int d, const std::vector &vcubs, bool khal = false) : dim(d), ncubs(std::vector(d+1)), N(std::vector(d+1)), cubs(vcubs), khalimsky(khal) + { check_dimension() ;} + + /* \brief Copy constructor. */ + Cub_object_io(const Cub_object_io &m) : dim(m.dim), ncubs(m.ncubs), N(m.N), cubs(m.cubs), khalimsky(m.khalimsky) {} + + // Mesh operations + void clear_cubs() { cubs.clear() ; for (size_t i=0; i sizes ; + + size_t tmp ; + std::stringstream sseizes (inputLine); + while (sseizes >> tmp) + sizes.push_back(tmp) ; + + std::cout << "dimensions : " ; + for (size_t i=0; i(dim) ; + for (size_t i=0; i> tmp ; + if (tmp) // pixel on + { + cubs.push_back(index_to_coords(i, khal)) ; + ++ncubs[dim] ; + } + } + + if (khal) + { + // Change N to Khalimsky maxima + for(int i=0; i " << line << "\n"; + throw std::runtime_error("File Parsing Error: Invalid file"); + } + } + std::istringstream is( line ); + + if (line_number == 1) // Header 1 + is >> dim ; + else if (line_number == 2) // Header 2 + { + for (int i=0; i> N[i] ; + } + else // Cub line + { + IOCubCellType cub ; + + size_t v; + while ( is >> v ) + cub.push_back(v); + if (cub.size() != dim) + std::cout << "Discard line " << line_number << " cub of invalid dimension" << std::endl ; + else + { + // Add this cub to cubs + ++ncubs[cub_dim(cub)] ; + + if (khalimsky) + cubs.push_back(cub) ; + else + { + size_t ind(khal_to_index(cub)) ; + cubs.push_back(index_to_coords(ind, false)) ; + } + } + } + } + infile.close() ; + return true ; + } + + bool write_cub(const std::string &filename) ; + + void print_infos (size_t level = 0) const + { + std::cout << "Cub_object_io infos - dim : " << dim << ", cubs : " << cubs.size() << std::endl ; + for (int q=0; q < dim; ++q) + std::cout << "\tSize along dim " << q << " : " << N[q] << std::endl ; + for (int q = 0; q <= dim; ++q) + std::cout << "Cubs of dim " << q << " : " << ncubs[q] << std::endl ; + if (level == 1) // Print coordinates of cubes + { + for (size_t i=0; i + +#ifndef CGAL_HDVF_CUBICAL_CHAIN_COMPLEX_H +#define CGAL_HDVF_CUBICAL_CHAIN_COMPLEX_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace CGAL { +namespace Homological_discrete_vector_field { + +/*! + \ingroup PkgHDVFRef + + The class `Cubical_chain_complex` represents (topological) chain complexes associated to cubical complexes. + + \section Description Description + + A cubical complex is a set of "square" cells such that: all the faces of a given cell also belong to the complex and any two cells intersect exactly along a common face. + + + + A cell in a cubical complex of dimension q is the %cartesian product of \f$q\f$ intervals \f$[n_1,n_1+\delta_1]\times [n_q,n_q+\delta_q] \f$ with \f$n_i\in \mathbb N \f$ and \f$\delta_i = 0,1\f$. + The dimension of such a cell is \f$\sum_{i=1}^q \delta_i\f$. + A 0-cell is thus a vertex, a 1-cell contains one non zero delta (edge along one of the axes), a 2-cell contains two non zero deltas (square along two of the axes), while a 3-cell is a cube... + + For instance, \f$v = [2,2]\times[1,1]\times[0,0]\f$, \f$e = [0,1]\times[1,1]\times[0,0]\f$ and \f$f = [2,3]\times[0,1]\times[1,1]\f$. + + Given a cubical complex of dimension \f$q\f$ included in \f$[0,B_1]\times\cdots \times[0,B_q]\f$, Khalimsky coordinates associate to each cell of the complex a unique coordinate in \f$[0,2B_1]\times\cdots \times[0,2B_q]\f$: + \f[ + \begin{array}{rcl} + \sigma & \mapsto & \mathrm{khal}(\sigma) \\ + [n_1,n_1+\delta_1]\times\cdots \times [n_q,n_q+\delta_q] & \,\to\, & (2n_1+\delta_1,\ldots,2n_1+\delta_1) \\ + \end{array}\f] + we set \f$N_i = 2B_i+1\f$ the size of the Khalimsky bounding box along the \f$i\f$th axis. + + The dimension of a cell is the given by the number of odd coordinates. + In our previous example, \f$B_1=3, B_2=2, B_3=1\f$, hence \f$N_1=7, N_2=5, N_3=3\f$ and we get the following Khalimsky coordinates: \f$\mathrm{khal}(v) = (4,2,0)\f$, \f$\mathrm{khal}(e) = (1,2,0)\f$ and \f$\mathrm{khal}(f) = (5,1,2)\f$. + + The boundary map of the complex is computed by the constructor of the class using the standard formula with Khalimsky coordinates. Given a cell \f$\sigma\f$ with \f$\mathrm{khal}(\sigma) = (x_1,\ldots,x_q)\f$ + \f[ \partial(x_1,\ldots,x_q) = \sum_{\substack{i=0\\x_i\text{ odd}}}^q (-1)^{j(i)} \left( (x_1,\ldots,x_i+1,\ldots, x_q) - (x_1,\ldots,x_i-1,\ldots, x_q)\right)\f] + where \f$j(i)\f$ is the number of odd coordinates between \f$x_1\f$ and \f$x_i\f$. + + + + Let us also point out that besides Khalimsky coordinates, cells are indexed along each dimension, thus each cell is uniquely determined by its dimension and its index in this dimension (called "base index"). + + \section SecHdvfImplementation Implementation Details + + As described above, Khalimsky coordinates provide a convenient tool to identify cells of any dimension. Hence a complex of dimension \f$q\f$ can be encoded by a Boolean matrix of size \f$N_1\times\cdots\times N_q\f$ (with previous notations). For convenience, this matrix is vectorized and thus the complex is stored in a Boolean vector (denoted by `_cells`) of size \f$N_1\times \cdots \times N_q\f$. + + Cells of any dimension are thus repesented by a given element of this Boolean vector and the corresponding index is called their *Boolean index*. + + As stated above, besides this Boolean representation, topological computations require to identify the bases of cells in any dimension (bases of the free chain groups). Hence, a cell of dimension \f$d\f$ is also identified by its index in the basis of \f$d\f$-dimensional cells. This index is called its *basis index*. The vector `_bool2base` stores, for each dimension, the map between Boolean and base indices, while the `_base2bool` stores, for each dimension, the permutation between base and Boolean indices. + + + + \cgalModels{GeometricChainComplex} + + \tparam CoefficientRing a model of the `IntegralDomainWithoutDivision` concept. + + \tparam Traits a geometric traits class model of the `HDVFTraits` concept. + */ + + +template +class Cubical_chain_complex { +public: + /*! \brief Type of coefficients used to compute homology. */ + typedef CoefficientRing Coefficient_ring; + + /** \brief Type of vertex coordinates */ + typedef typename Traits::Point Point ; + /** \brief Type of vtk export vertex coordinates */ + typedef typename Traits::Point3 Point3 ; +private: + /* \brief Vector of VTK types associated to cells in each dimension + e.g. {1, 3, 8, 11} */ + static const std::vector VTK_cubtypes ; + +public: + /** \brief Type used to encode primal or dual construction. */ + enum Cubical_complex_primal_dual {PRIMAL, DUAL}; + + /** + * \brief Default constructor (empty cubical complex). + * + * Builds an empty cubical complex. + */ + Cubical_chain_complex() : _complex_id(_id_generator++) {} ; + + /** + * \brief Constructor from a Cub_object_io (builds PRIMAL or DUAL associated complex depending on `type`). + * + * Builds the cubical complex associated to a a set of cells (vertices, edges, squares, cubes...), ie.\ performs the down closure of cells and set the boundary matrices in any dimension. Given a set of cells: + * + * - if the `type` is PRIMAL, the constructor builds the associated complex as such (see below middle), which comes to encode \f$3^q-1\f$ connectivity (with \f$q\f$ the dimension of the complex) + * - if the `type` is DUAL and the Cub_object_io contains only cells of maximal dimension (ie.\ binary object), the constructor build the dual associated complex (see below right), which comes to encode \f$2q\f$ connectivity (with \f$q\f$ the dimension of the complex) + * + * + * + * \param cub A Cub_object_io containing a set of "cubical" cells. + * \param type Type of construction used (PRIMAL or DUAL). + */ + Cubical_chain_complex(const Cub_object_io& cub,Cubical_complex_primal_dual type); + +// /** \brief Friend class `Duality_cubical_complex_tools` provides tools for Alexander duality. */ +// friend Duality_cubical_complex_tools ; + + /** \brief Type of column-major chains */ + typedef CGAL::OSM::Sparse_chain Column_chain; + /** \brief Type of row-major chains */ + typedef CGAL::OSM::Sparse_chain Row_chain ; + /** \brief Type of column-major sparse matrices */ + typedef CGAL::OSM::Sparse_matrix Column_matrix; + + /** + * \brief Assignment operator for cubical chain complexes. + * + * Stores a copy of an cubical chain complex in *this. + * + * \param complex The cubical chain complex which will be copied. + */ + Cubical_chain_complex& operator=(const Cubical_chain_complex& complex) + { + _dim = complex._dim; + _size_bb = complex._size_bb; + _P = complex._P; + _cells = complex._cells; + _base2bool = complex._base2bool; + _bool2base = complex._bool2base; + _visited_cells = complex._visited_cells; + return *this; + } + + // Methods of the AbstractChainComplex concept + + /** + * \brief Returns the boundary of the cell id_cell in dimension q. + * + * Returns a copy of the column-major chain stored in the boundary matrix of dimension q: boundary of the cell id_cell in dimension q. + * + * \param id_cell Index of the cell. + * \param q Dimension of the cell. + * + * \return The column-major chain containing the boundary of the cell id_cell in dimension q. + */ + Column_chain d(size_t id_cell, int q) const + { + if (q > 0) + return OSM::cget_column(_d[q], id_cell); + else + return Column_chain(0) ; + } + + /** + * \brief Returns the co-boundary of the cell id_cell in dimension q. + * + * Returns a row-major chain containing the co-boundary of the cell id_cell in dimension q (so actually a row of the boundary matrix). + * + * \warning As the boundary matrix is stored column-major, this entails crossing the full matrix to extract the row coefficients (O(number of non empty columns)) + * + * \param id_cell Index of the cell. + * \param q Dimension of the cell. + * + * \return The row-major chain containing the co-boundary of the cell id_cell in dimension q. + */ + Row_chain cod(size_t id_cell, int q) const + { + if (q < _dim) + return OSM::get_row(_d[q+1], id_cell); + else + return Row_chain(0) ; + } + + /** + * \brief Returns the number of cells in a given dimension. + * + * \param q Dimension along which the number of cells is returned. + * + * \return Number of cells in dimension q. + */ + size_t number_of_cells(int q) const + { + if ((q >=0) && (q <= _dim)) + return _base2bool.at(q).size() ; + else + return 0 ; + } + + /** + * \brief Returns the dimension of the complex. + * + * Returns the dimension of the cubical complex. + * + * \return The dimension of the complex.. + */ + int dimension() const + { + return _dim ; + } + + /** \brief Get the size of the Khalimsky bounding box. + * + * The Khalimsky coordinate of a cell `c` in dimension `q` is strictly lesser than `size_bb().at(q)`. + */ + std::vector size_bb() const { + return _size_bb; + } + + /** \brief Returns the total size of the complex. + * + * Khalimsky coordinates in dimension `q` are bounded above by `size_bb().at(q)`; the total memory size of the complex is the product of these sizes. + */ + size_t size() const { + return _P.at(dimension()); + } + + /** \brief Returns Khalimsky coordinates of the cell of "basis index" i in dimension q. + * In `AbstractChainComplex`, cells of dimenson `q` are identified by their index in the basis of \f$q\f$-cells together with their dimension. In the context of cubical complexes, this index is called "basis index" (to distinguish from its "Boolean index", see above). + * + * \param i "basis index" of a cell. + * \param q dimension of the cell. + * \returns Khalimsky coordinates of the cell. + */ + std::vector index_to_cell (size_t i, int q) const + { + const size_t id_bool(_base2bool.at(q).at(i)); + return bindex_to_cell(id_bool); + } + + /** \brief Returns the "basis index" of a cell given by its Khalimsky coordinates. + * + * In `AbstractChainComplex`, cells of dimenson `q` are identified by their index in the basis of \f$q\f$-cells together with their dimension. In the context of cubical complexes, this index is called "basis index" (to distinguish from its "Boolean index", see above). + * + * \param cell Khalimsky coordinates of a cell (of dimension \f$q\f$). + * \returns The "basis index" of the cell among \f$q\f$-cells. + */ + size_t cell_to_index (std::vector cell) const + { + const size_t id_bool(cell_to_bindex(cell)); + const int q(dimension(cell)); + return (_bool2base.at(q)).at(id_bool); + } + + /** \brief Returns the dimension of a cell (given in Khalimsky coordinates). */ + int dimension(const std::vector& cell) const; + + /** \brief Returns Khalimsky coordinates of the cell of Boolean index i. + * + * In `Cubical_chain_complex`, a cells of dimenson `q`, besides its index in the basis of \f$q\f$-cells, has an index in the vectorisation of the Khalimsky representation, called its "Boolean index" (see above). + * + * \param i The "Boolean" index of a cell. + * \returns Khalimsky coordinates of the cell. + */ + std::vector bindex_to_cell (size_t i) const { + if (i > _P[_dim]) + throw std::invalid_argument("Index exceeds the size of Boolean vector"); + std::vector khal(_dim); + for (size_t k = 0; k < _dim; ++k) { + khal[k] = i % _size_bb[k]; + i /= _size_bb[k]; + } + return khal; + } + + /** \brief Returns the Boolean index of a cell given by its Khalimsky coordinates. + * + * In `Cubical_chain_complex`, a cells of dimenson `q`, besides its index in the basis of \f$q\f$-cells, has an index in the vectorisation of the Khalimsky representation, called its "Boolean index" (see above). + * + * \param cell Khalimsky coordinates of a cell. + * \returns The "Boolean" index of the cell. + */ + size_t cell_to_bindex (std::vector cell) const { + if (cell.size() != _dim) { + throw std::invalid_argument("Dimension of cell does not match dimension of the complex"); + } + + size_t cell_index = 0; + for (size_t i = 0; i < _dim; ++i) { + cell_index += cell[i] * _P[i]; + } + + // verify if cell_index is within bounds of _cells + if ((cell_index >= 0) && (cell_index < _P.at(_dim))) { + return cell_index; + } else { + std::cerr << "Invalid cell index in _cells: " << cell_index << std::endl; + throw std::out_of_range("Cell index out of bounds in _cells"); + } + } + + /** + * \brief Returns a constant reference to the vector of boundary matrices (along each dimension). + * + * Returns a constant reference to the vector of boundary matrices along each dimension. The q-th element of this vector is a column-major sparse matrix containing the boundaries of q-cells (ie.\ rows encode q-1 cells and columns q cells). + * + * \return Returns a constant reference to the vector of column-major boundary matrices along each dimension. + */ + const std::vector & boundary_matrices() const + { + return _d ; + } + + /** + * \brief Returns a copy of the dim-th boundary matrix (ie.\ column-major matrix of \f$\partial_q\f$). + * + * It is a column-major sparse matrix containing the boundaries of dim-cells (ie.\ rows encode q-1 cells and columns q cells). + * + * \param q Dimension of the boundary matrix (ie.\ columns will contain the boundary of dimension q cells). + * + * \return A column-major sparse matrix containing the matrix of the boundary operator of dimension q. + */ + const Column_matrix & boundary_matrix(int q) const + { + return _d.at(q) ; + } + + + /** + * \brief Returns dimension 0 cell indices included in the cell with index `id_cell` of dimension `q`. + * + * Returns the dimension 0 vertex indices included in the cell with index `id_cell` of dimension `q`. + * + * \param id_cell Index of the cell. + * \param q Dimension of the cell. + * + * \return A vector of 0-cell indices. + */ + std::vector bottom_faces(size_t id_cell, int q) const + { + // Khalimsky coordinates of the cell + const std::vector coords(bindex_to_cell(_base2bool.at(q).at(id_cell))) ; + return khal_to_verts(coords) ; + } + + /** + * \brief Returns the cofaces of a given chain in dimension `q`. + * + * The resulting chain lies in dimension `q+1` and is null if this dimension exceeds the dimension of the complex. + */ + template + Column_chain cofaces_chain (OSM::Sparse_chain chain, int q) const + { + typedef OSM::Sparse_chain ChainType; + // Compute the cofaces + if (q < dimension()) + { + Column_chain fstar_cofaces(number_of_cells(q+1)) ; + for (typename ChainType::const_iterator it = chain.cbegin(); it != chain.cend(); ++it) + { + // Set the cofaces of it->first in dimension dim+1 + Row_chain cofaces(cod(it->first,q)) ; + for (typename Row_chain::const_iterator it2 = cofaces.cbegin(); it2 != cofaces.cend(); ++it2) + fstar_cofaces.set_coefficient(it2->first, 1) ; + } + return fstar_cofaces ; + } + else + return Column_chain(0) ; + } + +protected: + /** + * \brief Prints informations on the complex. + * + * Displays the number of cells in each dimension and the boundary matrix in each dimension. + */ + std::ostream& print_complex(std::ostream& out = std::cout) const { + for (int q = 0; q <= _dim; ++q) { + out << "-------- dimension " << q << std::endl; + out << "cells of dimension " << q << " : " << _base2bool.at(q).size() << std::endl; + for (size_t id_base = 0; id_base < _base2bool.at(q).size(); ++id_base) { + size_t id_bool = _base2bool.at(q).at(id_base); + std::vector khal = bindex_to_cell(id_bool); + out << id_base << " -> " << id_bool << " -> " << _bool2base.at(q).at(id_bool) << " -> "; + for (size_t k : khal) out << k << " "; + out << std::endl; + } + + if (_base2bool[q].size() > 0 && q <= _dim) { + out << "border matrix of dimension " << q << std::endl; + out << _d[q] << std::endl; + } + } + return out; + } + +public: + /** + * \brief Prints informations on the complex. + * + * Displays the number of cells in each dimension and the boundary matrix in each dimension. + */ + template + friend std::ostream& operator<<(std::ostream& out, const Cubical_chain_complex<_CT,_Traits>& complex) ; + + /** \brief Gets (unique) object Id. + * For comparison of constant references to the complex. + */ + size_t get_id () const { return _complex_id; } + +protected: + /* Compute the point associated to the ith 0-cell */ + Point compute_point(size_t i) const + { + const std::vector coords(bindex_to_cell(_base2bool.at(0).at(i))) ; + std::vector res ; + for (size_t c : coords) + res.push_back(c/2. + .5) ; + // If necessary "fill" with zeros + if (res.size() < Traits::Dimension::value){ + for (int i=res.size(); i& points() const + { + return _points; + } + + /// END Methods of the Cubical_chain_complex concept + + // VTK export + + /* + * \brief Exports a cubical complex (plus, optionally, labels) to a VTK file. + * + * The method generates legacy text VTK files. Labels are exported as such in a VTK property, together with CellID property, containing the index of each cell. + * + * \tparam LabelType Type of labels provided (default: int). + * + * \param K Cubical complex exported. + * \param filename Output file root (output filenames will be built from this root). + * \param labels Pointer to a vector of labels in each dimension. (*labels).at(q) is the set of integer labels of cells of dimension q. If labels is NULL, only CellID property is exported. + * \param label_type_name Typename used in vtk export (e.g. "int" or "unsigned_long", see VTK manual ). + */ + template + static void chain_complex_to_vtk(const Cubical_chain_complex &K, const std::string &filename, const std::vector > *labels=NULL, std::string label_type_name = "int") + { + bool with_scalars = (labels != NULL) ; + + // Load out file... + std::ofstream out ( filename, std::ios::out | std::ios::trunc); + + if ( ! out . good () ) { + std::cerr << "CubComplex_to_vtk. Fatal Error:\n " << filename << " not found.\n"; + throw std::runtime_error("File Parsing Error: File not found"); + } + + // Header + out << "# vtk DataFile Version 2.0" << std::endl ; + out << "generators" << std::endl ; + out << "ASCII" << std::endl ; + out << "DATASET UNSTRUCTURED_GRID" << std::endl ; + + // Points + size_t nnodes = K.number_of_cells(0) ; + out << "POINTS " << nnodes << " double" << std::endl ; + for (size_t n = 0; n < nnodes; ++n) + { + Point3 p(Traits::to_point3(K.point(n))) ; + out << p ; + out << std::endl ; + } + + size_t ncells_tot = 0, size_cells_tot = 0 ; + std::vector types ; + std::vector scalars ; + std::vector ids ; + // all cells must be printed + { + // Cells up to dimension 3 + // Size : size of a cell of dimension q : 2^q + for (int q=0; q<=K.dimension(); ++q) + { + ncells_tot += K.number_of_cells(q) ; + const size_t size_cell = 1<::VTK_cubtypes.at(0)) ; + if (with_scalars) + { + scalars.push_back((*labels).at(0).at(i)) ; + ids.push_back(i) ; + } + } + // Cells of higher dimension + for (int q=1; q<=K.dimension(); ++q) + { + const size_t size_cell = 1< verts(K.khal_to_verts(K.bindex_to_cell(K._base2bool.at(q).at(id)))) ; + out << size_cell << " " ; + for (size_t i : verts) + out << i << " " ; + out << std::endl ; + types.push_back(Cubical_chain_complex::VTK_cubtypes.at(q)) ; + if (with_scalars) + { + scalars.push_back((*labels).at(q).at(id)) ; + ids.push_back(id) ; + } + } + } + + // CELL_TYPES + out << "CELL_TYPES " << ncells_tot << std::endl ; + for (int t : types) + out << t << " " ; + out << std::endl ; + } + + if (with_scalars) + { + // CELL_LABEL + out << "CELL_DATA " << ncells_tot << std::endl ; + out << "SCALARS Label " << label_type_name << " 1" << std::endl ; + out << "LOOKUP_TABLE default" << std::endl ; + for (LabelType s : scalars) + out << s << " " ; + out << std::endl ; + // CELL_IDs + out << "SCALARS CellId " << "int" << " 1" << std::endl ; + out << "LOOKUP_TABLE default" << std::endl ; + for (size_t i : ids) + out << i << " " ; + out << std::endl ; + } + out.close() ; + } + + /* + * \brief Exports a chain over a cubical complex to a VTK file. + * + * The method generates legacy text VTK files. All the cells of the chain with non zero coefficient are exported. If a cellId is provided, labels are exported in a VTK property (2 for all cells, 0 for cell of index cellId). The index of each cell is exported in a CellID property. + * + * \param K Cubical complex exported. + * \param filename Output file root (output filenames will be built from this root). + * \param chain Sparse_chain exported (all the cells with non-zero coefficients in the chain are exported to vtk). + * \param q Dimension of the cells of the chain. + * \param cellId If different from MAX_SIZE_T, labels are exported to distinguish cells of the chain (label 2) from cellId cell (label 0). + */ + static void chain_to_vtk(const Cubical_chain_complex &K, const std::string &filename, const OSM::Sparse_chain& chain, int q, size_t cellId = -1) ; + +protected: + // Methods to access data + + /* \brief Get _P (coefficients used for the vectorisation of the Khalimsky Boolean matrix) */ + std::vector get_P() const { + return _P; + } + + /* \brief Get the Boolean vector of the complex in Khalimsky coordinates */ + std::vector get_cells() const { + return _cells; + } + + /* \brief Get the vector of vectors _base2bool */ + std::vector > get_base2bool() const { + return _base2bool; + } + + /* \brief Get the vector of maps _bool2base */ + std::vector > get_bool2base() const { + return _bool2base; + } + + /* \brief Method computing the boundary of a given cell */ + Column_chain boundary_cell(size_t index_base, int dim) const { + // Ensure dim is within valid range + if (dim < 0 || dim >= _base2bool.size()) { + throw std::out_of_range("Invalid dimension: " + std::to_string(dim)); + } + + // Check if index_base is within bounds of _base2bool[dim] + if (index_base < 0 || index_base >= _base2bool[dim].size()) { + throw std::out_of_range("index_base " + std::to_string(index_base) + " not found in _base2bool[" + std::to_string(dim) + "]"); + } + + size_t nb_lignes = (dim == 0) ? 0 : number_of_cells(dim - 1); + Column_chain boundary(nb_lignes); + + size_t index_bool = _base2bool[dim][index_base]; + std::vector c = bindex_to_cell(index_bool); + + CoefficientRing sign = 1; + for (size_t i = 0; i < _dim; ++i) { + if (c[i] % 2 == 1) { + // compute the coefficient based on the number of odd entries in c from 0 to i-1 + sign *= -1; + + size_t cell1 = index_bool + _P[i]; + size_t cell2 = index_bool - _P[i]; + + // Insert the coefficient in the corresponding row of the boundary matrix + if (cell1 >= 0 && cell1 < _cells.size()) { + size_t index = _bool2base[dim - 1].at(cell1); + boundary.set_coefficient(index, sign); + } + if (cell2 >= 0 && cell2 < _cells.size()) { + size_t index = _bool2base[dim - 1].at(cell2); + boundary.set_coefficient(index, -sign); + } + } + } + + return boundary; + } + + /* \brief Check if a cell (given in Khalimsky coordinates) is valid */ + bool is_valid_cell(const std::vector& cell) const ; + + /* \brief Check if a cell (given by its Boolean index) is valid */ + bool is_valid_cell(size_t id_cell) const ; + + /* \brief Compute vertices of a cell given by its Khalimsky coordinates */ + std::vector khal_to_verts(std::vector c) const + { + std::vector > vertices, vertices_tmp ; + // Vertices are obtained by cartesian products + for (size_t i=0; i<_dim; ++i) + { + if ((c[i]%2) == 1) + { + if (vertices.size()==0) + { + vertices.push_back(std::vector(1,c[i]-1)) ; + vertices.push_back(std::vector(1,c[i]+1)) ; + } + else + { + vertices_tmp.clear() ; + for (std::vector vert : vertices) + { + std::vector tmp(vert) ; + tmp.push_back(c[i]-1) ; + vertices_tmp.push_back(tmp) ; + } + for (std::vector vert : vertices) + { + std::vector tmp(vert) ; + tmp.push_back(c[i]+1) ; + vertices_tmp.push_back(tmp) ; + } + vertices = vertices_tmp ; + } + } + else + { + if (vertices.size() == 0) + vertices.push_back(std::vector(1,c[i])) ; + else + { + vertices_tmp.clear() ; + for (std::vector vert : vertices) + { + std::vector tmp(vert) ; + tmp.push_back(c[i]) ; + vertices_tmp.push_back(tmp) ; + } + vertices = vertices_tmp ; + } + } + } + std::vector vertices_id ; + for (std::vector vert : vertices) + { + vertices_id.push_back(_bool2base[0].at(cell_to_bindex(vert))) ; + } + return vertices_id ; + } + + // Member data +protected: + /* \brief Dimension of the complex */ + int _dim; + /* \brief Size of the Khalimsky bounding box \f$(N_1,\ldots,N_{\mathrm{\_dim}})\f$*/ + std::vector _size_bb; + /* \brief Vector of coefficients used for vectorization of the Boolean representation */ + std::vector _P; + /* \brief Vectorized Boolean representation of the complex (true if a cell is present, false otherwise) */ + std::vector _cells; + /* \brief Maps from base indices to Boolean indices (ie.\ indices in _cell) in each dimension */ + std::vector> _base2bool; + /* \brief Maps from Boolean indices (ie.\ indices in _cells) to base indices in each dimension */ + std::vector> _bool2base; + /* \brief Vector of boundary matrices in each dimension */ + std::vector _d; + /* \brief Vector of points (vertices coordinates). */ + std::vector _points; +private: + std::vector _visited_cells; // Internal flag + /* \brief Static counter for objects ids. + * Initialized to 0. + */ + static size_t _id_generator ; + /* \brief Unique object id (for comparison of constant references to the complex). */ + const size_t _complex_id ; + + // Protected methods +protected: + /* Initialize _cells, _base2bool and _bool2base */ + void initialize_cells(const Cub_object_io& cub,Cubical_complex_primal_dual type); + + /* \brief compute the dimension of a cell (given by its Boolean index) */ + int dimension(size_t cell_index) const + { + return dimension(bindex_to_cell(cell_index)) ; + } + + /* \brief Computes voxel coordinates (in a binary object) from an index in a Boolean vector + * + * This function is used ONLY for DUAL construction from a binary object (binary image in 2D, binary volume in 3D...) + * Hence we consider a set of cells of maximal dimension vectorized to a Boolean vector. + */ + std::vector ind2vox(size_t index, std::vector B, size_t max_size) const + { + if (index > max_size) + throw std::invalid_argument("ind2vox : index exceeding size of Boolean vector"); + std::vector coords(_dim); + for (size_t k = 0; k < _dim; ++k) { + coords[k] = index % B[k]; + index /= B[k]; + } + return coords; + } + + /* \brief Computes an index in a Boolean vector from voxel coordinates (in a binary object) + * + * This function is used ONLY for DUAL construction from a binary object (binary image in 2D, binary volume in 3D...) + * Hence we consider a set of cells of maximal dimension vectorized to a Boolean vector. + */ + size_t vox2ind(const std::vector& base_indices, std::vector B, size_t max_size) const { + if (base_indices.size() != _dim) { + throw std::invalid_argument("Dimension of base_indices does not match _dim"); + } + + size_t cell_index = 0; + for (size_t i = 0; i < _dim; ++i) { + cell_index += base_indices[i] * B[i]; + } + + // Verfiy if cell_index is within bounds of _cells + if (cell_index >= 0 && cell_index < max_size) + return cell_index; + else { + std::cerr << "Invalid cell index in _cells: " << cell_index << std::endl; + throw std::out_of_range("Invalid cell index in _cells"); + } + } + + + /* \brief Computes the boundary matrix of dimension q */ + void compute_d(int q) ; + + /* \brief Insert a cell into the complex (and its faces if necessary) */ + void insert_cell(size_t cell); + + /* \brief Compute (the Boolean indices of) cells belonging to the boundary of `cell` (given by its Boolean index) */ + std::vector compute_boundaries(size_t cell) const; + +}; + +// Initialization of static VTK_cubtypes +template const +std::vector Cubical_chain_complex::VTK_cubtypes({1, 3, 8, 11}) ; + +// Initialization of _id_generator +template +size_t Cubical_chain_complex::_id_generator(0) ; + +// Constructor implementation +template +Cubical_chain_complex::Cubical_chain_complex(const Cub_object_io& cub,Cubical_complex_primal_dual type) : _dim(cub.dim), _size_bb(_dim+1), _P(_dim+1,1), _base2bool(_dim+1), _bool2base(_dim+1), _complex_id(_id_generator++) + +{ + // Initialize _size_bb and _P + if (type==PRIMAL) + _size_bb = cub.N; + else + { + for (int q=0; q<_dim; ++q) + _size_bb.at(q) = 2*cub.N.at(q)+1 ; + } + + for (size_t i = 1; i <= _dim; ++i) { + _P[i] = _P[i - 1] * _size_bb[i - 1]; + } + + _cells.resize(_P[_dim], false) ; + + // Initialize _visited_cells + _visited_cells.resize(_P[_dim], false) ; + + // Initialize _cells, _base2bool, and _bool2base + initialize_cells(cub,type); + + // Initialize _d + _d.resize(_dim+1); + for (int q = 0; q <= _dim; ++q) { + compute_d(q); + } + + // Initialize _points + compute_points(); +} + +// initialize_cells implementation +template +void Cubical_chain_complex::initialize_cells(const Cub_object_io& cub, Cubical_complex_primal_dual type) +{ + if (type == PRIMAL) + { + for (size_t i=0; i coords(cub.cubs.at(i)) ; + // compute the coordinates of the voxel in the dual complex + for (size_t i=0; i<_dim; ++i) + coords.at(i)*=2 ; + const size_t cell_index(cell_to_bindex(coords)) ; + + _cells.at(cell_index) = true ; + // Add the cell in _base2bool and _bool2base + const int dim(dimension(coords)) ; + const size_t n(_base2bool.at(dim).size()) ; + _base2bool.at(dim).push_back(cell_index) ; + _bool2base.at(dim)[cell_index] = n ; + } + + // for the cells with dim>0 + for (int q = 1; q <= _dim; ++q) { + + for (size_t i = 0; i < _P[_dim]; ++i) { + + if (dimension(bindex_to_cell(i)) == q) { + std::vector boundaries = compute_boundaries(i); + bool all_boundaries_present = true; + for (const auto& boundary_cell : boundaries) { + if (!_cells[boundary_cell]) { + all_boundaries_present = false; + break; + } + } + if (all_boundaries_present) { + _cells.at(i)= true ; + // Add the cell in _base2bool and _bool2base + const int dim(dimension(i)) ; + const size_t n(_base2bool.at(dim).size()) ; + _base2bool.at(dim).push_back(i) ; + _bool2base.at(dim)[i] = n ; + } + } + } + } + } + else + { + throw std::invalid_argument("Invalid Cubical_complex_primal_dual"); + } +} + + +// is_valid_cell implementation +template +bool Cubical_chain_complex::is_valid_cell(const std::vector& cell) const { + for (size_t i=0; i<_dim; ++i) { + if (cell[i] < 0 || cell[i] >= _size_bb[i]) { + return false; + } + } + return true; +} + +template +bool Cubical_chain_complex::is_valid_cell(size_t id_cell) const +{ + if ((id_cell < 0) || (id_cell > _P[_dim])) + return false; + else + return true; +} + + +// insert_cell implementation +template +void Cubical_chain_complex::insert_cell(size_t cell) { + if (!is_valid_cell(cell)) + throw std::out_of_range("insert_cell: trying to insert cell with invalid index"); + + // verify if the cell has already been visited + if (_visited_cells.at(cell)) { + return; // cell has already been visited + } + + std::vector cell_coords(bindex_to_cell(cell)); + int dim = dimension(cell_coords); + + _cells[cell] = true; + size_t cell_base_index = _base2bool[dim].size(); + _base2bool[dim].push_back(cell); + _bool2base[dim][cell] = cell_base_index; + + std::vector boundaries(compute_boundaries(cell)); + for (const auto& boundary : boundaries) { + if (!_cells[boundary]) { + insert_cell(boundary); + } + } + + // mark cell as visited + _visited_cells.at(cell) = true ; +} + + +// compute_d implementation +template +void Cubical_chain_complex::compute_d(int dim) { + size_t nb_lignes = (dim == 0) ? 0 : number_of_cells(dim - 1); + + _d[dim] = Column_matrix(nb_lignes, number_of_cells(dim)); + + // Iterate through the cells of dimension dim + for (size_t i = 0; i < number_of_cells(dim); ++i) { + // Boundary of the i-th cell of dimension dim + Column_chain boundary = boundary_cell(i, dim); + + // Insert the chain into the corresponding column of the boundary matrix + OSM::set_column(_d[dim], i, boundary); + } +} + +// dimension implementation +template +int Cubical_chain_complex::dimension(const std::vector& cell) const { + int dimension = 0; + for (size_t index : cell) { + if (index % 2 == 1) { // Un index impair indique une dimension plus Ć©levĆ©e + dimension++; + } + } + return dimension; +} + +// compute_boundaries implementation +template +std::vector Cubical_chain_complex::compute_boundaries(size_t idcell) const { + std::vector boundaries; + std::vector c = bindex_to_cell(idcell); + + for (size_t i = 0; i < _dim; ++i) { + if (c[i] % 2 == 1) + { + // compute the coefficient based on the number of odd entries in c from 0 to i-1 + size_t cell1 = idcell + _P[i]; + if (is_valid_cell(cell1)) + boundaries.push_back(cell1) ; + + size_t cell2 = idcell - _P[i]; + if (is_valid_cell(cell2)) + boundaries.push_back(cell2) ; + } + } + return boundaries; +} + +/* \brief Export chain of dimension q to vtk. + * -> Only cells of the chain are exported + * -> If a cell Id is provided scalars are exported (0 for the given cellId / 2 for other cells) + */ + +template +void Cubical_chain_complex::chain_to_vtk(const Cubical_chain_complex &K, const std::string &filename, const OSM::Sparse_chain& chain, int q, size_t cellId) +{ + bool with_scalars = (cellId != -1) ; + + // Load out file... + std::ofstream out ( filename, std::ios::out | std::ios::trunc); + + if ( ! out . good () ) { + std::cerr << "SimpComplex_chain_to_vtk. Fatal Error:\n " << filename << " not found.\n"; + throw std::runtime_error("File Parsing Error: File not found"); + } + + // Header + out << "# vtk DataFile Version 2.0" << std::endl ; + out << "generators" << std::endl ; + out << "ASCII" << std::endl ; + out << "DATASET UNSTRUCTURED_GRID" << std::endl ; + + // Points + size_t nnodes = K.number_of_cells(0) ; + out << "POINTS " << nnodes << " double" << std::endl ; + for (size_t n = 0; n < nnodes; ++n) + { + Point3 p(Traits::to_point3(K.point(n))) ; + out << p ; + for (int i = Traits::Dimension::value; i<3; ++i) + out << " 0"; + out << std::endl ; + } + + size_t ncells_tot = 0, size_cells_tot = 0 ; + std::vector types ; + std::vector scalars ; + std::vector ids ; + + // output only cells of the chain (dimension q) + { + // 1 - Compute the number of cells / size of encoding + { + const size_t size_cell = 1< khal(K.bindex_to_cell(K._base2bool.at(q).at(id))) ; + std::vector verts(K.khal_to_verts(K.bindex_to_cell(K._base2bool.at(q).at(id)))) ; + out << size_cell << " " ; + for (size_t i : verts) + out << i << " " ; + out << std::endl ; + types.push_back(Cubical_chain_complex::VTK_cubtypes.at(q)) ; + + if (with_scalars) + { + ids.push_back(id) ; + if (id != cellId) + scalars.push_back(2) ; + else + scalars.push_back(0) ; + } + } + } + } + // CELL_TYPES + out << "CELL_TYPES " << ncells_tot << std::endl ; + for (int t : types) + out << t << " " ; + out << std::endl ; + } + + if (with_scalars) + { + // CELL_TYPES + out << "CELL_DATA " << ncells_tot << std::endl ; + out << "SCALARS Label " << "int" << " 1" << std::endl ; + out << "LOOKUP_TABLE default" << std::endl ; + for (int s : scalars) + out << s << " " ; + out << std::endl ; + // CELL_IDs + out << "SCALARS CellId " << "int" << " 1" << std::endl ; + out << "LOOKUP_TABLE default" << std::endl ; + for (size_t i : ids) + out << i << " " ; + out << std::endl ; + } + out.close() ; +} + +template +std::ostream& operator<<(std::ostream& out, const Cubical_chain_complex& complex) +{ + return complex.print_complex(out); +} + +} /* end namespace Homological_discrete_vector_field */ +} /* end namespace CGAL */ + +#endif // CGAL_HDVF_CUBICAL_CHAIN_COMPLEX_H diff --git a/HDVF/include/CGAL/HDVF/Filtration_core.h b/HDVF/include/CGAL/HDVF/Filtration_core.h new file mode 100644 index 00000000000..d7012f27008 --- /dev/null +++ b/HDVF/include/CGAL/HDVF/Filtration_core.h @@ -0,0 +1,335 @@ +// Copyright (c) 2025 LIS Marseille (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Alexandra Bac + +#ifndef CGAL_HDVF_FILTRATION_CORE_H +#define CGAL_HDVF_FILTRATION_CORE_H + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace CGAL { +namespace Homological_discrete_vector_field { + + +/*! \brief Type for indexing uniquely a cell. + * - First element of the pair: index of the cell. + * - Second element of the pair: dimension of the cell. + */ +typedef std::pair Cell ; + + + +/*! + \ingroup PkgHDVFRef + + The class `Filtration_core` implements data structures and methods required by the `Filtration` concept. + + By definition, a filtration over a chain complex `K` is a sequence of sub complexes \f$\{K_t\,;\, t\in[d_1, \ldots, d_N]\}\f$, where \f$d_i\f$ are scalars, such that: + + - \f$\forall d\leqslant d'\f$, \f$K_d\subseteq K_{d'}\f$. + - \f$|K_{d_{i+1}}| = |K_{d_{i}}|+1\f$. + - As a consequence, any cell \f$\sigma\in K\f$, is "added" at a given *time* \f$i\f$ and the corresponding \f$d_i\f$ is called its *degree* (denoted by \f$\mathrm{deg}(\sigma)\f$). + - \f$K_{d_1}\f$ is an empty complex. + - \f$K_{d_N} = K\f$. + - For any cell \f$\sigma\in K\f$, if \f$\tau\f$ is a proper face of \f$\sigma\f$, then \f$\mathrm{deg}(\tau)\leqslant \mathrm{deg}(\sigma)\f$. + + In consequence, a filtration associated to a complex `K`: + - maps each cell to a scalar called its degree + - orders the cells of `K` (each cell has a unique index along the filtration called *time* along the filtration) such that: + - the index of a cell is larger that the indices of its faces, + - the degree map is increasing along the filtration. + + The class `Filtration_core` provides elementary constructors and methods used in derived filtrations. + + \cgalModels{Filtration} + + \tparam ChainComplex a model of the `AbstractChainComplex` concept (type of the underlying chain complex). + \tparam Degree the scalar type of degrees (a model of the `RealEmbeddable` concept). + */ + + +template +class Filtration_core +{ +public: + /*! \brief Type of coefficients used to compute homology. */ + typedef typename ChainComplex::Coefficient_ring Coefficient_ring; + + /*! \brief Type of value returned by the iterator. + */ + typedef struct { + Cell cell_dim ; + Degree degree ; + size_t time; + } Filtration_iter_value ; + +protected: + /** \brief Constant reference to the underlying chain complex. */ + const ChainComplex& _K ; + /** \brief Vector of cells of the filtration (full ordering of cells). */ + std::vector _filtration ; + /** \brief Vector of degrees of cells along the filtration. */ + std::vector _deg ; + + /** \brief Map from cells to their index in the filtration. */ + std::map _cell_to_t ; + + /*! + Type of column-major sparse matrices + */ + typedef CGAL::OSM::Sparse_matrix Column_matrix ; + + /*! + Type of row-major sparse matrices + */ + typedef CGAL::OSM::Sparse_matrix Row_matrix ; + /*! + Type of column-major chains + */ + typedef CGAL::OSM::Sparse_chain Column_chain ; + + /*! + Type of row-major chains + */ + typedef CGAL::OSM::Sparse_chain Row_chain ; +public: + /*! \brief Constructor of an empty filtration with an underlying chain complex. + * + *\param K A chain complex (a model of `AbstractChainComplex`), the underlying chain complex of the filtration. + */ + Filtration_core(const ChainComplex& K) : _K(K) + {} + + /*! \brief Constructor from a vector of cells (ordering of cells) and an associated vector of degrees. + * + * The constructor check that the filtration is valid (a cell is introduced in the filtration after its faces and the degree vector is increasing) and throw an exception if not. + * \param K A chain complex (a model of `AbstractChainComplex`), the underlying chain complex of the filtration. + * \param filtration An ordering of the cells of `K` encoded as a vector of its cells. + * \param deg The (increasing) vector of cell degrees. + */ + Filtration_core(const ChainComplex& K, const std::vector& filtration, const std::vector& deg) : _K(K), _filtration(filtration), _deg(deg) + { + if (!is_valid()) + throw ("Invalid filtration, Filtration_core constructor failed"); + } + + /** + * \brief Constructor by copy. + * + * Builds a `Filtration_core` by copy from another. + * + * \param f An initial `Filtration_core`. + */ + Filtration_core(const Filtration_core& f) : _K(f._K), _filtration(f._filtration), _cell_to_t(f._cell_to_t) {} + + /** \brief Iterator over a filtration. + * + * Iterate the cells of the filtration by increasing index (and hence increasing degree). + */ + struct iterator + { + // Iterator tags + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = Filtration_iter_value; + + /** \brief Iterator constructor + * + * \param f Constant reference on the `Filtration_core` iterated. + * \param i The initial index. + */ + iterator(const Filtration_core& f, std::size_t i=0) : _i(i), _f(f) {} + + /*! \brief Iterator dereference + * + * \returns A `Filtration_iter_value` structure containing the information of the current cell and its degree. + */ + value_type operator*() const + { + Filtration_iter_value res ; + res.cell_dim = _f._filtration.at(_i) ; + res.degree = _f._deg.at(_i) ; + res.time = _i; + return res ; + } + + /*! + * \brief Prefix incrementation. Moves to next cell in the filtration. + * + * \returns The reference to the current iterator. + */ + iterator& operator++() + { + ++_i; + return *this; + } + + /*! + * \brief Postfix incrementation. Moves to next cell in the filtration. + * \returns The pre-incremented iterator. + */ + iterator operator++(int) { iterator tmp = *this; ++(*this); return tmp; } + + /*! + * \brief Equality check. + * \returns True if the indices are equal. + */ + friend bool operator== (const iterator& a, const iterator& b) { return a._i == b._i; }; + + /*! + * \brief Inequality check. + * \returns True if the indices are different. + */ + friend bool operator!= (const iterator& a, const iterator& b) { return a._i != b._i; }; + + private: + std::size_t _i ; // Index along _persist + const Filtration_core& _f ; // Filtration_core iterated + }; + + /** + * \brief Iterator to the beginning of the filtration. + * + * \return The iterator to the beginning of the filtration. + */ + iterator begin() { return iterator(*this, 0) ; } + + /** + * \brief Iterator past-the-end of the filtration. + * + * \return The iterator past-the-end of the filtration. + */ + iterator end() { return iterator(*this, _filtration.size()) ; } + + // getters + /*! \brief Gets the filtration size. + */ + std::size_t size () const { return _filtration.size();} + + /*! \brief Gets the cell (that is cell index and dimension) at the index `i` of the filtration. + */ + Cell cell_index_dimension (std::size_t i) const { return _filtration.at(i); } + + /*! \brief Gets the degree of the `i`th element of the filtration. + */ + Degree degree (std::size_t i) const { return _deg.at(i); } + + // Output filtration + /*! \brief Overload of the `<<`operator for filtrations. + */ + friend std::ostream & operator<<(std::ostream & out, const Filtration_core &f) + { + const std::size_t N(f._filtration.size()) ; + for (std::size_t i=0; i (" << f._filtration.at(i).first << "- dim " << f._filtration.at(i).second << " , " << f._deg.at(i) << ") " << std::endl ; + } + return out ; + } + + /** + * \brief Exports the filtration time indices. + * + * The method exports the time index of every cells in each dimension. + * + * \returns A vector containing, for each dimension, the vector of labels by cell index. + */ + std::vector > export_filtration () const + { + std::vector > labels(_K.dimension()+1) ; + for (int q=0; q<=this->_K.dimension(); ++q) + { + for (std::size_t i = 0; i_K.number_of_cells(q); ++i) + { + const Cell cell(i,q) ; + const std::size_t t(_cell_to_t.at(cell)); + labels.at(q).push_back(t) ; + } + } + return labels ; + } + + /*! \brief Checks if a filtration is valid. + * Checks that cells are ordered in increasing degrees and all cells have indices larger than their faces. + * \returns `true` if the filtration is valid, `false` otherwise + */ + bool is_valid() const ; + +protected: + /* \brief Sets _cell_to_t from the initial vector o cells. */ + void build_filtration_structure() ; + + /* Friend class: Hdvf_persistence. */ + template + friend class Hdvf_persistence ; +}; + +template +void Filtration_core::build_filtration_structure() +{ + for (std::size_t i = 0; i<_filtration.size(); ++i) + { + const Cell c(_filtration.at(i)) ; + // c : filtration index i, index in the basis reordered by filtration : j + _cell_to_t[c] = i ; + } +} + +template +bool Filtration_core::is_valid() const +{ + bool valid = true ; + for (std::size_t i=0; i<_filtration.size() && valid; ++i) + { + if (i>0) + valid = valid & (_deg.at(i) >= _deg.at(i-1)) ; + Cell c(_filtration.at(i)) ; + std::cout << i << " -> " << c.first << " dim "<< c.second << std::endl ; + const int q = c.second ; + if (q>0) + { + Column_chain dc = _K.d(c.first, q) ; + std::cout << "bnd : " << dc << std::endl ; + for (typename Column_chain::iterator it = dc.begin(); it != dc.end() && valid; ++it) + { + // Faces of c + const Cell face(it->first,q-1) ; + // Check if the face c belongs to the filtration + auto it_face(_cell_to_t.find(face)) ; + valid = valid & (it_face != _cell_to_t.end()) ; + if (!valid) + std::cout << "face not found" << std::endl ; + if (it_face != _cell_to_t.end()) + valid = valid & (_cell_to_t[face] < i) ; + if (!valid) + std::cout << "face " << it->first << " at time : " << _cell_to_t[face] << " with i : " << i << std::endl ; + } + } + if (!valid) + std::cout << "check failed : " << i << std::endl ; + } + return valid ; +} + +} /* end namespace Homological_discrete_vector_field */ +} /* end namespace CGAL */ + +#endif // CGAL_HDVF_FILTRATION_CORE_H diff --git a/HDVF/include/CGAL/HDVF/Filtration_lower_star.h b/HDVF/include/CGAL/HDVF/Filtration_lower_star.h new file mode 100644 index 00000000000..9472408b7b9 --- /dev/null +++ b/HDVF/include/CGAL/HDVF/Filtration_lower_star.h @@ -0,0 +1,238 @@ +// Copyright (c) 2025 LIS Marseille (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Alexandra Bac + +#ifndef CGAL_HDVF_FILTRATION_LOWER_STAR_H +#define CGAL_HDVF_FILTRATION_LOWER_STAR_H + +#include + +#include +#include +#include +#include +#include +#include + +namespace CGAL { +namespace Homological_discrete_vector_field { + +/* Standard functions for lower star filtration */ +/* Degree = double */ + +//std::function deg_fun_x = [&complex](size_t i) +//{ +// std::vector Xi(complex.point(i)) ; +// return (Xi.at(0)) ; +//} ; +// +//std::function deg_fun_z = [&complex](size_t i) +//{ +// std::vector Xi(complex.point(i)) ; +// return (Xi.at(2)) ; +//} ; + +/** \brief For lower star filtration along x: function mapping coordinates to x */ +std::function&)> f_x = [](const std::vector& v) +{ + return (v.at(0)) ; +} ; + +/** \brief For lower star filtration along y: function mapping coordinates to y */ +std::function&)> f_y = [](const std::vector& v) +{ + return (v.at(1)) ; +} ; + +/** \brief For lower star filtration along z: function mapping coordinates to z */ +std::function&)> f_z = [](const std::vector& v) +{ + return (v.at(2)) ; +} ; + +/** \brief Degree function from a coordinates to scalar map. */ +template +std::function degree_function (const ChainComplex& complex, const std::function& f) +{ + std::function deg_fun_f = [&complex, &f](size_t i) + { + const P& Xi(complex.point(i)) ; + return f(Xi) ; + } ; + return deg_fun_f ; +} + +/*! + \ingroup PkgHDVFRef + + The class `Filtration_lower_star` implements the lower star filtration on a given complex implementing the concept `AbstractChainComplex`. + + A filtration associated to a chain complex `K` associates to each cell of `K` a scalar value (called degree) such that the degree of a cell is larger than the degrees of its faces. + + Let `Degree` be a scalar type. The lower star filtration is a filtration obtained from a map \f$\mathrm{deg}\,:\, K_0 \to \mathrm{Degree}\f$ (where \f$K_0\f$ denotes the set of vertices of \f$K\f$) associating a degree to each vertex of the complex \f$K\f$. + + The map is extended to cells of any dimension by setting, for a cell \f$\sigma\f$: + \f[\mathrm{deg}(\sigma) = \max_{\substack{v\in K_0\\v\text{ face of }\sigma}} \mathrm{deg}(v) + \f] + + For geometric complexes, standard lower star filtrations are obtained by taking as a degree function the \f$x\f$, \f$y\f$ or \f$z\f$ coordinate of vertices. The image below illustrates such a filtration ((left) lower star filtration with a \f$z\f$ degree map on vertices, (right) lower star filtration with a \f$y\f$ degree map on vertices). + + + + + The `Filtration_lower_star` class provides constructors taking as input: + - either the vector of vertex degrees + - or a function mapping each vertex to its degree. + + \cgalModels{Filtration} + + \tparam ChainComplex a model of the `AbstractChainComplex` concept (type of the underlying chain complex). + \tparam Degree the scalar type of degrees, a model of `RealEmbeddable`. + */ + +template +class Filtration_lower_star : public Filtration_core +{ +private: + /*! \brief Type of coefficients used to compute homology. */ + typedef typename ChainComplex::Coefficient_ring Coefficient_ring; + /*! Type of parent filtration instance. */ + typedef Filtration_core Filtration_parent; + /*! Type of value returned by the iterator. */ + typedef typename Filtration_parent::Filtration_iter_value Filtration_iter_value; +public: + /*! \brief chain complex type. */ + typedef ChainComplex Chain_complex; + /*! + * \brief Copy constructor. + * + * Builds a filtration by copy from another. + * + * \param f An initial lower star filtration. + */ + Filtration_lower_star(const Filtration_lower_star& f) : Filtration_parent(f) {} + + /*! \brief Constructor from vertex degrees. + * + * The constructor computes all cell degrees as the minimum of the degrees of their vertices and sorts all the cells of the complex to fulfill the filtration ordering constraints. + * + * \param K Constant reference to the underlying complex. + * \param deg Vector of vertex degrees. + */ + Filtration_lower_star(const Chain_complex& K, const std::vector& deg) : Filtration_parent(K) + { + star_filtration(deg); + } + + /*! \brief Constructor from a function mapping vertices to degrees. + * + * The constructor computes all cell degrees as the minimum of the degrees of their vertices (obtained through `degree`) and sorts all the cells of the complex to fulfill the filtration ordering constraints. + * + * \param K Constant reference to the underlying complex. + * \param degree Function mapping vertices of `K` to their degree. + */ + Filtration_lower_star(const Chain_complex& K, std::function& degree) : Filtration_parent(K) + { + star_filtration(degree); + } + + +protected: + // Build lower-star filtration + /*! \brief Function building the filtration from the vector of vertex degrees. + */ + void star_filtration(const std::vector& deg) ; + + /*! \brief Function building the filtration from a function mapping vertices to their degree. + */ + void star_filtration(std::function& degree) ; +}; + + + +template +void Filtration_lower_star::star_filtration(const std::vector °) +{ + if (deg.size() != this->_K.number_of_cells(0)) + throw "Star filtration error : deg should provide one value by vertex" ; + + // Create filtration and degrees for all cells according to deg + // -> lower star: maximum degree of vertices + // -> upper star: minimum degree of vertices + std::vector tmp_filtration ; + std::vector tmp_deg ; + std::vector tmp_perm ; + for (size_t i=0; i_K.dimension(); ++q) + { + for (size_t i=0; i_K.number_of_cells(q); ++i) + { + tmp_filtration.push_back(Cell(i,q)) ; + + // Compute corresponding degree + // Vertices of the cell + std::vector verts(this->_K.bottom_faces(i,q)) ; + // Compute the degree of the cell + Degree d = deg.at(verts.at(0)) ; + for (size_t j=1; j d) + d = tmp_d ; + // If upper star filtration (for cohomology) + // if (tmp_d < d) + // d = tmp_d ; + + } + tmp_deg.push_back(d) ; + tmp_perm.push_back(tmp_perm.size()) ; + } + } + // Sort filtration + + // Create sorting function : lexicographic order over (deg, dim) + // Test if cell i < cell j + auto f_sort = [&tmp_filtration, &tmp_deg] (size_t i, size_t j) + { + // degree of i is lower than degree of j or (they are equal and the dimension of i is lower than the dimension of j) + return ((tmp_deg[i] < tmp_deg[j]) || ((tmp_deg[i] == tmp_deg[j]) && (tmp_filtration[i].second < tmp_filtration[j].second))) ; + } ; + // Sort -> create the right permutation + std::sort(tmp_perm.begin(), tmp_perm.end(), f_sort) ; + // Insert cells in the filtration and degrees accordingly + for (size_t i=0; i_filtration.push_back(tmp_filtration.at(tmp_perm.at(i))) ; + this->_deg.push_back(tmp_deg.at(tmp_perm.at(i))) ; + this->_cell_to_t[tmp_filtration.at(tmp_perm.at(i))] = i ; + } +} + +template +void Filtration_lower_star::star_filtration(std::function& degree) +{ + std::vector deg ; + for (size_t i=0; i_K.number_of_cells(0); ++i) + { + deg.push_back(degree(i)) ; + } + star_filtration(deg) ; +} + +} /* end namespace Homological_discrete_vector_field */ +} /* end namespace CGAL */ + +#endif // CGAL_HDVF_FILTRATION_LOWER_STAR_H diff --git a/HDVF/include/CGAL/HDVF/Geometric_chain_complex_tools.h b/HDVF/include/CGAL/HDVF/Geometric_chain_complex_tools.h new file mode 100644 index 00000000000..135f0430da0 --- /dev/null +++ b/HDVF/include/CGAL/HDVF/Geometric_chain_complex_tools.h @@ -0,0 +1,709 @@ +// Copyright (c) 2025 LIS Marseille (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Alexandra Bac + +#ifndef CGAL_HDVF_GEOMETRIC_CHAIN_COMPLEX_TOOLS_H +#define CGAL_HDVF_GEOMETRIC_CHAIN_COMPLEX_TOOLS_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * \class SimpComplexTools + * \brief Provides tools for SimpComplexes ((co)homology, duality, persistent homology). + * \brief Friend class of SimpComplex + */ + +namespace CGAL { +namespace IO { + +// GeometricChainComplex IO + +// Hdvf vtk export + +/*! + * \brief Exports all the `Hdvf` information of a geometric chain complex to vtk files. + * + * Export PSC labels and homology/cohomology generators (depending on HDVF options) associated to each critical cell to vtk files. + * + * \param hdvf Reference to the HDVF exported. + * \param complex Underlying geometric chain complex. + * \param filename Prefix of all generated files. + * \param co_faces Export the cohomology generator or its co-faces (sometimes more convenient for visualisation). + * + * Below, a sample mesh with, (left) homology generators, (right) two examples of cohomology generators (corresponding generators/co-generators bear similar colours): + * + * + * + * + * + * The same generators displayed through their co-faces: + * + * + * + * + * All homology / cohomology generators: + * + * + */ + +template typename ChainType = OSM::Sparse_chain, template typename SparseMatrixType = OSM::Sparse_matrix, typename VertexIdType = size_t> +void write_VTK (Homological_discrete_vector_field::Hdvf_core& hdvf, ChainComplex &complex, std::string filename = "test", bool co_faces = false) +{ + typedef typename ChainComplex::Coefficient_ring Coefficient_ring; + typedef Homological_discrete_vector_field::Hdvf_core HDVF_parent; + // Export PSC labelling + std::string outfile(filename+"_PSC.vtk") ; + std::vector > labels = hdvf.psc_labels() ; + ChainComplex::chain_complex_to_vtk(complex, outfile, &labels) ; + + if (hdvf.hdvf_opts() != Homological_discrete_vector_field::OPT_BND) + { + // Export generators of all critical cells + std::vector > criticals(hdvf.psc_flags(Homological_discrete_vector_field::CRITICAL)) ; + for (int q = 0; q <= complex.dimension(); ++q) + { + for (size_t c : criticals.at(q)) + { + // Homology generators + if (hdvf.hdvf_opts() & (Homological_discrete_vector_field::OPT_FULL | Homological_discrete_vector_field::OPT_G)) + { + std::string outfile_g(filename+"_hom_"+std::to_string(c)+"_dim_"+std::to_string(q)+".vtk") ; + // std::vector > labels = hdvf.export_label(G,c,q) ; + OSM::Sparse_chain chain(hdvf.homology_chain(c,q)) ; + ChainComplex::chain_to_vtk(complex, outfile_g, chain, q, c) ; + } + // Cohomology generators + if (hdvf.hdvf_opts() & (Homological_discrete_vector_field::OPT_FULL | Homological_discrete_vector_field::OPT_F)) + { + std::string outfile_f(filename+"_cohom_"+std::to_string(c)+"_dim_"+std::to_string(q)+".vtk") ; + OSM::Sparse_chain chain(hdvf.cohomology_chain(c, q)) ; + if (!co_faces) + { + ChainComplex::chain_to_vtk(complex, outfile_f, chain, q, c) ; + } + else + { + if (q < complex.dimension()) + { + ChainComplex::chain_to_vtk(complex, outfile_f, complex.cofaces_chain(chain, q), q+1, c) ; + } + } + } + } + } + } +} + +// Hdvf_persistence vtk export + +/** \brief Exports all the `HDVF_persistence` information of a geometric chain complex to vtk files. + * + * Export PSC labels and homology/cohomology generators (depending on HDVF options) associated to each persistent intervals to vtk files. + * + * \param per_hdvf Reference to the persistent HDVF exported. + * \param complex Underlying geometric chain complex. + * \param filename Prefix of all generated files. + * \param co_faces Export the cohomology generator or its co-faces (sometimes more convenient for visualisation). + */ + +template +void write_VTK (Homological_discrete_vector_field::Hdvf_persistence &per_hdvf, ChainComplex &complex, std::string filename = "per", bool co_faces = false) +{ + typedef typename ChainComplex::Coefficient_ring Coefficient_ring; + if (!per_hdvf.with_export()) + throw("Cannot export persistent generators to vtk: with_export is off!") ; + + using HDVF_type = Homological_discrete_vector_field::Hdvf_persistence; + using Persistence_interval = HDVF_type::Persistence_interval; + + // Export the filtration + std::string out_file_filtration = filename+"_filtration.vtk" ; + std::vector > filtr_labels = per_hdvf.filtration().export_filtration(); + ChainComplex::template chain_complex_to_vtk(complex, out_file_filtration, &filtr_labels, "unsigned_long") ; + + // Iterate over persistence diagram (iterator over non zero intervals) + // Batch informations are stored in file filename_infos.txt + std::ofstream info_file(filename+"_infos.txt") ; + size_t i = 0 ; + for (typename HDVF_type::iterator it = per_hdvf.begin(); it != per_hdvf.end(); ++it) + { + typename HDVF_type::Persistent_hole hole_data(*it) ; + const Persistence_interval hole(hole_data.hole) ; + // Export informations of this hole + /* V1 */ + info_file << i << " -> " << " --- duration : " << hole.duration() << " -- " ; + hole.insert(info_file); + info_file << std::endl ; + /* V2 */ + // info_file << i << " -> " << " --- duration : " << hole.duration() << " -- " << hole << std::endl ; + + if (hole.duration()>0) // Export vtk of "finite" holes + { + // Build name associated to the ith hole : filename_i + std::string out_file = filename+"_"+std::to_string(i) ; + // Export PSC labels to vtk + ChainComplex::chain_complex_to_vtk(complex, out_file+"_PSC.vtk", &hole_data.labelsPSC) ; + + // Export homology generators (g) + if (per_hdvf.hdvf_opts() & (Homological_discrete_vector_field::OPT_FULL | Homological_discrete_vector_field::OPT_G)) + { + // First generator : filename_i_g_sigma_q.vtk + { + const size_t id(hole_data.hole.cell_birth.first) ; + const int dim(hole_data.hole.cell_birth.second) ; + std::string tmp(out_file+"_hom_"+std::to_string(id)+"_"+std::to_string(dim)+".vtk") ; + ChainComplex::chain_to_vtk(complex, tmp, hole_data.homology_chain_birth, dim); + } + // Second generator : filename_i_g_tau_q+1.vtk + { + const size_t id(hole_data.hole.cell_death.first) ; + const int dim(hole_data.hole.cell_death.second) ; + std::string tmp(out_file+"_hom_"+std::to_string(id)+"_"+std::to_string(dim)+".vtk") ; + ChainComplex::chain_to_vtk(complex, tmp, hole_data.homology_chain_death, dim); + } + } + // Export cohomology generators (fstar) + if ((per_hdvf.hdvf_opts() == Homological_discrete_vector_field::OPT_FULL) || (per_hdvf.hdvf_opts() == Homological_discrete_vector_field::OPT_F)) + { + // First generator : filename_i_fstar_sigma_q.vtk + { + const size_t id(hole_data.hole.cell_birth.first) ; + const int dim(hole_data.hole.cell_birth.second) ; + std::string tmp(out_file+"_cohom_"+std::to_string(id)+"_"+std::to_string(dim)+".vtk") ; + if (co_faces) + { + ChainComplex::chain_to_vtk(complex, tmp, complex.cofaces_chain(hole_data.cohomology_chain_birth, dim), dim+1); + } + else + ChainComplex::chain_to_vtk(complex, tmp, hole_data.cohomology_chain_birth, dim); + } + // Second generator : filename_i_fstar_tau_q+1.vtk + { + const size_t id(hole_data.hole.cell_death.first) ; + const int dim(hole_data.hole.cell_death.second) ; + std::string tmp(out_file+"_cohom_"+std::to_string(id)+"_"+std::to_string(dim)+".vtk") ; + if (co_faces) + { + ChainComplex::chain_to_vtk(complex, tmp, complex.cofaces_chain(hole_data.cohomology_chain_death, dim), dim+1); + } + else + ChainComplex::chain_to_vtk(complex, tmp, hole_data.cohomology_chain_death, dim); + } + } + } + ++i ; + } + info_file.close() ; + // Export infinite holes by calling Hdvf_core write_VTK + (static_cast&, ChainComplex&, std::string, bool)>(CGAL::IO::write_VTK))(per_hdvf, complex, filename+"_inf", co_faces); +} + +// Hdvf_duality vtk export + +/** \brief Exports all the `HDVF_duality` information of a geometric chain complex to vtk files. + + Export PSC labels and homology/cohomology generators (depending on HDVF options) associated to each persistent intervals to vtk files. + + \param hdvf Reference to the HDVF exported. + \param complex Underlying geometric chain complex. + \param filename Prefix of all generated files. + \param co_faces Export the cohomology generator or its co-faces (sometimes more convenient for visualisation). + */ + +template +void write_VTK (Homological_discrete_vector_field::Hdvf_duality &hdvf, ChainComplex &complex, std::string filename = "test", bool co_faces = false) +{ + typedef typename ChainComplex::Coefficient_ring Coefficient_ring; + typedef Homological_discrete_vector_field::Hdvf_duality HDVF_parent; + // Export PSC labelling + std::string outfile(filename+"_PSC.vtk") ; + std::vector > labels = hdvf.psc_labels() ; + ChainComplex::chain_complex_to_vtk(complex, outfile, &labels) ; + + if (hdvf.hdvf_opts() != Homological_discrete_vector_field::OPT_BND) + { + // Export generators of all critical cells + std::vector > criticals(hdvf.psc_flags(Homological_discrete_vector_field::CRITICAL)) ; + for (int q = 0; q <= complex.dimension(); ++q) + { + for (size_t c : criticals.at(q)) + { + // Homology generators + if (hdvf.hdvf_opts() & (Homological_discrete_vector_field::OPT_FULL | Homological_discrete_vector_field::OPT_G)) + { + std::string outfile_g(filename+"_hom_"+std::to_string(c)+"_dim_"+std::to_string(q)+".vtk") ; + // std::vector > labels = hdvf.export_label(G,c,q) ; + OSM::Sparse_chain chain(hdvf.homology_chain(c,q)) ; + ChainComplex::chain_to_vtk(complex, outfile_g, chain, q, c) ; + } + // Cohomology generators + if (hdvf.hdvf_opts() & (Homological_discrete_vector_field::OPT_FULL | Homological_discrete_vector_field::OPT_F)) + { + std::string outfile_f(filename+"_cohom_"+std::to_string(c)+"_dim_"+std::to_string(q)+".vtk") ; + OSM::Sparse_chain chain(hdvf.cohomology_chain(c, q)) ; + if (!co_faces) + { + ChainComplex::chain_to_vtk(complex, outfile_f, chain, q, c) ; + } + else // Compute co-faces + { + if (q < complex.dimension()) + { + // Restrict the cofaces of the cohomology generator to the current sub chain complex + OSM::Sparse_chain cofaces_chain(complex.cofaces_chain(chain, q)) ; + Homological_discrete_vector_field::Sub_chain_complex_mask sub(hdvf.get_current_mask()); + sub.screen_chain(cofaces_chain, q+1); + // Display + ChainComplex::chain_to_vtk(complex, outfile_f, cofaces_chain, q+1, c) ; + } + } + } + } + } + } +} + +/** + * \brief Exports a model of `GeometricChainComplex` (plus, optionally, labels) to a VTK file. + * + * The method generates legacy text VTK files. Labels are exported as such in a VTK property, together with CellID property, containing the index of each cell. + * + * \tparam LabelType Type of labels provided (default: int). + * + * \param K Model of `GeometricChainComplex` exported. + * \param filename Output file root (output filenames will be built from this root). + * \param labels Pointer to a vector of labels in each dimension. (*labels).at(q) is the set of integer labels of cells of dimension q. If labels is NULL, only CellID property is exported. + * \param label_type_name Typename used in vtk export (e.g. "int" or "unsigned_long", see VTK manual ). + */ +template +static void write_VTK(const Chain_complex &K, const std::string &filename, const std::vector > *labels=NULL, std::string label_type_name = "int") { + Chain_complex::chain_complex_to_vtk(K, filename, labels, label_type_name); +} + +/** + * \brief Exports a chain over a model of `GeometricChainComplex` to a VTK file. + * + * The method generates legacy text VTK files. All the cells of the chain with non zero coefficient are exported. If a cellId is provided, labels are exported in a VTK property (2 for all cells, 0 for cell of index cellId). The index of each cell is exported in a CellID property. + * + * \param K Model of `GeometricChainComplex` exported. + * \param filename Output file root (output filenames will be built from this root). + * \param chain Sparse_chain exported (all the cells with non-zero coefficients in the chain are exported to vtk). + * \param q Dimension of the cells of the chain. + * \param cellId If different from MAX_SIZE_T, labels are exported to distinguish cells of the chain (label 2) from cellId cell (label 0). + */ +template +void write_VTK(const Chain_complex &K, const std::string &filename, const OSM::Sparse_chain& chain, int q, size_t cellId = -1){ + Chain_complex::chain_to_vtk(K, filename, chain, q, cellId); +} + +} /* end namespace IO */ + +namespace Homological_discrete_vector_field { + +// Duality tools + + +/** \brief Type returned by `dualize_complex()`. + * + * \tparam ChainComplex Type of the underlying chain complex. + * + * The structure contains a pair: + * - `L_complex`: a pointer over simplicial chain complex (homeomorphic to \f$\mathbb B^3\f$). + * - `K_complex`: a sub chain complex mask (encoding the initial mesh inside `*L_complex`) + */ +template +class Complex_duality_data_t { +public: + /** \brief Type of the underlying chain complex. */ + typedef ChainComplex Chain_complex; + /** \brief Type of sub chain complex mask encoding the sub complex `*K_complex`. */ + typedef Sub_chain_complex_mask Sub_chain_complex ; + /** \brief Pointer over a complex homeomorphic to \f$\mathbb B^3\f$. */ + std::shared_ptr L_complex ; + /** \brief Pointer over a sub chain complex mask encoding a sub complex of `*L_complex`. */ + std::shared_ptr K_complex ; + + /** \brief Default constructor*/ + Complex_duality_data_t() : L_complex(nullptr), K_complex(nullptr) {} + + /** \brief Constructor from a pointer over a chain complex and a sub chain complex mask. + * + * \warning The `Complex_duality_data_t` destructor deletes its underlying complex and sub chain complex mask. + */ + Complex_duality_data_t(std::shared_ptr L, std::shared_ptr K) : L_complex(L), K_complex(K) {} +// Complex_duality_data_t(const Complex_duality_data_t&) = delete; +} ; + + +// =========== Simplicial chain complex tools +// Build K sub-chain complex of L (homeomorphic to B^n) + +/*! + \ingroup PkgHDVFRef + +The class `Duality_simplicial_complex_tools` is dedicated to Alexander duality for 3D surface meshes. Starting from a simplicial chain complex (encoding a 3D surface mesh), it provides methods to embed the complex into a larger icosphere and generate a 3D constrained Delaunay triangulation. + +Technically, starting from a Simplicial_chain_complex `K_init`, the method `dualize_complex()` builds a `Simplicial_chain_complex` `L` and a `Sub_chain_complex_mask` `K`. +- L : complex built out of K_init together with a closing icosphere, meshed by tetgen (constrained Delaunay triangulation) +- K (Sub_chain_complex_mask) : Sub_chain_complex_mask identifying K_init inside L + +\tparam CoefficientRing a model of the `IntegralDomainWithoutDivision` concept providing the ring used to compute homology. + +\tparam Traits a geometric traits class model of the `HDVFTraits` concept. +*/ + +template +class Duality_simplicial_complex_tools { +public: + typedef typename Traits::Point Point ; + /** \brief Type of simplicial chain complex encoding `L`. */ + typedef Simplicial_chain_complex Chain_complex ; + /** \brief Type of sub chain complex mask encoding the sub complex `K`. */ + typedef Sub_chain_complex_mask Sub_chain_complex ; + /** \brief Type of 3D surface meshes. */ + typedef CGAL::Surface_mesh Triangle_mesh; + /** \brief Type returned by `dualize_complex()`. */ + typedef Complex_duality_data_t Complex_duality_data; + /** \brief Default constructor. */ + Duality_simplicial_complex_tools() {} + +private: + static std::shared_ptr compute_sub_chain_complex(const Chain_complex& _K, const Chain_complex &L){ + // Build the Sub_chain_complex_mask encoding K_init inside L + std::shared_ptr K(std::make_shared(L, false)); + // Visit all cells of K_init and activate the corresponding bit in K + for (int q=0; q<=_K.dimension(); ++q) + { + for (size_t i=0; i<_K.number_of_cells(q); ++i) + { + const Simplex& simplex(_K.index_to_cell(i,q)) ; + const size_t id(L.cell_to_index(simplex)); + K->set_bit_on(q, id) ; + } + } + return K; + } + +public: + /** \brief Generates a subcomplex \f$K\f$K and a complex \f$L\f$ with \f$K\subseteq L\f$ from a set of simplices `mesh_io`. + * + * On the one hand, a `Simplicial_chain_complex` is built from `mesh_io` (let us call if `_K`); on the other hand, this complex is embedded into a larger icosphere and a 3D constrained Delaunay tetraedrization is generated. + * Then \f$K\f$, \f$L\f$ and vertex coordinates are extracted and stored in a `Complex_duality_data` structure. + * + * After the function, `mesh_object_io` has been extended with the simplices of the closing icosphere. + * + * The following figures shows the resulting complex with an initial `Twirl` mesh (right - sectional view): + * + * + * + * \param mesh_io `Mesh_object_io` containing the initial set of simplicies (working mesh). + * \param BB_ratio Ratio of the "closing" icosphere diameter with respect to the diameter of the object's bounding box. + * \param out_file_prefix Prefix of tetgen intermediate files (default: "file_K_closed.off"). + * \param subdiv Subdivision level of the bounding icosphere. + */ + static Complex_duality_data dualize_complex (Mesh_object_io& mesh_io, double BB_ratio=1.5, const std::string& out_file_prefix = "file_K_closed.off", unsigned int subdiv = 2) + { + + std::cerr << "-- Starting dualize_complex" << std::endl; + std::cerr << "Imported set of simplices" << std::endl; + std::cout << mesh_io; + + // Create a temporary associated complex (to identify simplices of mesh_io) + Chain_complex* _K = new Chain_complex(mesh_io); + + // Closing mesh_object_io by adding the icosphere + // Compute a bounding icosphere + Point center = mesh_io.centroid() ; + double r = mesh_io.radius(center) ; + Icosphere_object_io ico(subdiv,center, BB_ratio*r) ; + std::cerr << "Icosphere generated" << std::endl; + ico.print_infos() ; + + // Add it to the mesh + mesh_io.push_back(ico) ; + std::cerr << "Mesh concatenation" << std::endl; + mesh_io.print_infos() ; + + // Write this mesh to an off file for Tetgen + mesh_io.write_off(out_file_prefix) ; + // const std::string tetgen_path(CMAKE_TETGEN_PATH) ; + // WARNING: use CGAL 3D triangulation to get rid of tetgen... + const std::string tetgen_path("/Users/umenohana/Dropbox/G-Mod/TopAlg/code/tetgen1.6.0/build/") ; + std::string tetgen_command = tetgen_path+"tetgen -pqkcYfe "+out_file_prefix+".off" ; + system(tetgen_command.c_str()) ; + + // Read the mesh built by tetgen for L + Tet_object_io tetL(out_file_prefix) ; + + // Build the output data structure + Complex_duality_data dualized_complex; + // Build the associated SimpComplex + dualized_complex.L_complex = std::make_shared(tetL) ; + std::shared_ptr L(dualized_complex.L_complex); + std::cout << "------ L:" << *L; + + // Build the Sub_chain_complex_mask encoding K_init inside L + dualized_complex.K_complex = compute_sub_chain_complex(*_K, *L); + // Remove the temporary complex + delete _K; + + return dualized_complex; + } + + /** \brief Generates a subcomplex \f$K\f$K and a complex \f$L\f$ with \f$K\subseteq L\f$ from a `Surface_mesh`. + * + * On the one hand, a `Simplicial_chain_complex` is built from the `Surface_mesh` (let us call it `_K`); on the other hand, this complex is embedded into a larger icosphere and a 3D constrained Delaunay tetraedrization is generated. + * Then \f$K\f$, \f$L\f$ and vertex coordinates are built and extracted (from both previous computation) and stored in a `Complex_duality_data` structure. + * + * After the function, `mesh_object_io` has been extended with the simplices of the closing icosphere. + * + * The following figures shows the resulting complex with an initial `Twirl` mesh (right - sectional view): + * + * + * + * \param mesh `Surface_mesh` containing the initial mesh. + * \param BB_ratio Ratio of the "closing" icosphere diameter with respect to the diameter of the object's bounding box. + * \param subdiv Subdivision level of the bounding icosphere. + */ + static Complex_duality_data dualize_complex (Triangle_mesh& mesh, double BB_ratio=1.5, unsigned int subdiv = 2) + { + typedef CGAL::Triangulation_data_structure_3, CGAL::Conforming_constrained_Delaunay_triangulation_cell_base_3> TDS; + typedef CGAL::Triangulation_3 Triangulation; + typedef CGAL::Conforming_constrained_Delaunay_triangulation_3 CCDT3; + std::cerr << "-- Starting dualize_complex" << std::endl; + std::cerr << "Imported mesh" << std::endl; + + // Create a temporary Surfaces_mesh_io (to load the mesh) + Surface_mesh_io K_init_mesh_io(mesh); + // Create a temporary associated complex (to identify simplices of mesh_io) + Chain_complex* _K = new Chain_complex(K_init_mesh_io); + std::cout << "------ _K:" << *_K; + + // Closing mesh_object_io by adding the icosphere + // Compute a bounding icosphere + Point center = K_init_mesh_io.centroid() ; + double r = K_init_mesh_io.radius(center) ; + Triangle_mesh ico; + CGAL::make_icosahedron(ico, center, r*BB_ratio); + CGAL::Subdivision_method_3::Loop_subdivision( + ico, CGAL::parameters::number_of_iterations(subdiv) + ); + + std::cerr << "Icosphere generated" << std::endl; + + // Add it to the mesh + mesh += ico ; + std::cerr << "Mesh concatenation" << std::endl; + std::string off_file("tmp/test_bounded.off"); + std::ofstream out (off_file , std::ios::out | std::ios::trunc); + + if ( ! out . good () ) { + std::cerr << "Cannot output dualized mesh to:\n " << off_file << " - not found.\n"; + throw std::runtime_error("File Parsing Error: File not found"); + } + out << mesh ; + out.close(); + + // Generate plc_facet_map + auto plc_facet_map = get(CGAL::face_patch_id_t(), mesh); + int cpt(0); + for (typename Triangle_mesh::Face_iterator it = mesh.faces_begin(); it!= mesh.faces_end(); ++it){ + plc_facet_map[*it] = cpt++; + } + + // Constrained Delaunay Tetraedrisation preserving plc_facet_map + auto ccdt = CGAL::make_conforming_constrained_Delaunay_triangulation_3(mesh, CGAL::parameters::plc_face_id(plc_facet_map)); + + // Detect refined constrained faces + std::vector > faces_constr(cpt); + for (typename Triangulation::Facet f : ccdt.constrained_facets()) { + if (ccdt.is_facet_constrained(f)) + { + const int i(ccdt.face_constraint_index(f)); + faces_constr.at(i).push_back(f); + } + } + for (int i=0; i1) + std::cout << i << ": " << faces_constr.at(i).size() << std::endl; + } + + Triangulation tri_L = std::move(ccdt).triangulation(); + + // Build the output object + Complex_duality_data dualized_complex; + // Build the associated Triangulation_3_io + Triangulation_3_io L_tri_io(tri_L); + // Build the associated SimpComplex + dualized_complex.L_complex = std::make_shared(L_tri_io) ; + std::shared_ptr L(dualized_complex.L_complex); + std::cout << "------ L:" << *L; + + std::vector > indices(L->dimension()+1); + for (int q=0; q<=L->dimension(); ++q){ + for (int i=0; inumber_of_cells(q); ++i){ + indices.at(q).push_back(i); + } + } + std::string vtk_file("tmp/test_CDT3.vtk"); + Chain_complex::chain_complex_to_vtk(*L, vtk_file, &indices, "int"); + + // Build the Sub_chain_complex_mask encoding K_init inside L + dualized_complex.K_complex = compute_sub_chain_complex(*_K, *L); + // Remove the temporary complex + delete _K; + + return dualized_complex; + } + + + /** \brief Exports a chain complex to a Mesh_object_io */ + static Mesh_object_io& export_mesh_object(const Chain_complex& _CC) + { + std::vector vcells ; + for (int q = 0; q <= _CC.dimension(); ++q) + for (size_t i = 0; i<_CC.number_of_cells(q); ++i) + { + const Simplex& s(_CC.index_to_cell(i,q)) ; + vcells.push_back(s.vertices()) ; + } + + std::vector coords; + for (auto it = _CC.points().begin(); it != _CC.points().end(); ++it) + coords.push_back(Point(*it)); + Mesh_object_io &m = *(new Mesh_object_io(-3, coords, vcells)) ; + return m ; + } +} ; + +// =========== Cubical chain complex tools +// Build K sub-chain complex of L (homeomorphic to B^n) +// Adjust L size + +/*! + \ingroup PkgHDVFRef + +The class `Duality_cubical_complex_tools` is dedicated to Alexander duality for 3D binary volumes. + +Starting from a Cubical_chain_complex `K_init`, the method `dualize_complex()` builds a `Cubical_chain_complex` `L` and `Sub_chain_complex_mask` `K`. +- L : complex built of the "full" bounding box of `K_init` +- K (Sub_chain_complex_mask) : Sub_chain_complex_mask identifying `K_init` inside `L` + +Use the `frame()` method from the `Cub_object_io` class to enlarge the bounding box (via a 1 pixel dilatation) if necessary. + +\tparam CoefficientRing a model of the `IntegralDomainWithoutDivision` concept providing the ring used to compute homology. + +\tparam Traits a geometric traits class model of the `HDVFTraits` concept. +*/ + +template +class Duality_cubical_complex_tools { +public: + /** \brief Type of cubical complexes used for the initial complex and \f$L\f$. */ + typedef Cubical_chain_complex Chain_complex ; + /** \brief Type of sub chain complex mask used to encode the sub-complex \f$K\f$. */ + typedef Sub_chain_complex_mask Sub_chain_complex ; + /** \brief Type returned by `dualize_complex()`. */ + typedef Complex_duality_data_t Complex_duality_data; + + // Constructor + Duality_cubical_complex_tools() {} + + + /** \brief Generates a subcomplex \f$K\f$K and a complex \f$L\f$ with \f$K\subseteq L\f$ from a cubical complex `K_init`. + * + * `L` is the bounding box of `K_init` (homeomorphic to a ball) and \f$K\f$ is a sub chain complex mask encoding `K_init`. + * + * The following figures shows the resulting complex with an initial simple cubical complex (right - sectional view): + * + * + * + * \param K_init Initial cubical chain complex (working mesh). + */ + static Complex_duality_data dualize_complex (const Chain_complex& K_init) + { + Cub_object_io tmp_L ; + tmp_L.dim = K_init.dimension() ; + tmp_L.N = K_init.size_bb() ; + tmp_L.ncubs.resize(tmp_L.dim+1) ; + // Visit all boolean indices in the BB of _CC and insert corresponding cells in the Cub_object_io of L + for (size_t i=0; i tmpkhal(K_init.bindex_to_cell(i)) ; + const int dtmp(K_init.dimension(tmpkhal)) ; + (tmp_L.ncubs)[dtmp] += 1 ; + tmp_L.cubs.push_back(tmpkhal) ; + } + + // Build the output object + Complex_duality_data dualized_complex; + dualized_complex.L_complex = std::make_shared(tmp_L, Chain_complex::PRIMAL); + std::shared_ptr L(dualized_complex.L_complex) ; + + // Build the Sub_chain_complex_mask corresponding to _CC + dualized_complex.K_complex = std::make_shared (Sub_chain_complex(*L, false)) ; + std::shared_ptr K(dualized_complex.K_complex) ; + // Visit all cells of _CC and activate the corresponding bit in K + for (int q=0; q<=K_init.dimension(); ++q) + { + for (size_t i=0; i khal(K_init.index_to_cell(i, q)) ; + const size_t j = L->cell_to_index(khal); + K->set_bit_on(q,j) ; + } + } + return dualized_complex; + } + + /** \brief Generates a subcomplex \f$K\f$K and a complex \f$L\f$ with \f$K\subseteq L\f$ from a `Cub_object_io` `K_init`. + * + * `L` is the bounding box of `K_init` (homeomorphic to a ball) and \f$K\f$ is a sub chain complex mask encoding `K_init`. + * + * The following figures shows the resulting complex with an initial simple cubical complex (right - sectional view): + * + * + * + * \param K_init Initial `Cub_object_io` (cubical object). + * \param primal_dual TBD + */ + static Complex_duality_data dualize_complex (const Cub_object_io& K_init, typename Chain_complex::Cubical_complex_primal_dual primal_dual) { + Chain_complex* _K = new Chain_complex(K_init, primal_dual); + Complex_duality_data res(dualize_complex(*_K)); + delete _K; + return res; + } +} ; + +} /* end namespace Homological_discrete_vector_field */ +} /* end namespace CGAL */ + +#endif // CGAL_HDVF_GEOMETRIC_CHAIN_COMPLEX_TOOLS_H diff --git a/HDVF/include/CGAL/HDVF/Hdvf.h b/HDVF/include/CGAL/HDVF/Hdvf.h new file mode 100644 index 00000000000..30dec27f84e --- /dev/null +++ b/HDVF/include/CGAL/HDVF/Hdvf.h @@ -0,0 +1,1483 @@ +// Copyright (c) 2025 LIS Marseille (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Alexandra Bac + +#ifndef CGAL_HDVF_HDVF_H +#define CGAL_HDVF_HDVF_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace CGAL { +namespace Homological_discrete_vector_field { + +/*! + \ingroup PkgHDVFRef + + The class `Hdvf` implements homology and cohomology computation via homological discrete vector fields (%HDVF for short). It derives from `Hdvf_core` and shares all its data and methods. + + But besides construction operations and methods (using the `A()` operation), the `Hdvf` class implements four other HDVF operations: R, M, W and MW together with appropriate "find_pair()" functions. These operations change the %HDVF (that is change homology / cohomology generators) and thus provide a convenient tool to move inside the "space of homology/cohomology computations". + +- `R()` operation is the "dual" of the A pairing operation (it cancels the pairing and turns back a `PRIMARY`/`SECONDARY` pair into a pair of `CRITICAL` cells) +- `M()` operation exchanges a `PRIMARY` \f$\pi\f$ and a `CRITICAL` cell \f$\gamma\f$ (under conditions) and modifies the homology generator associated to \f$\gamma\f$ (while preserving is associated cohomology generator) +- `W()` operation exchanges a `SECONDARY` \f$\sigma\f$ and a `CRITICAL` cell \f$\gamma\f$ (under conditions) and modifies the cohomology generator associated to \f$\gamma\f$ (while preserving is associated homology generator) +- `MW()` operation exchanges a `PRIMARY` \f$\pi\f$ and a `SECONDARY` cell \f$\sigma\f$ (under conditions). See the introduction to HDVF for more details on this operation. + +Using appropriate combinations of such operations, one can change a HDVF until corresponding homology or cohomology generators meet a given basis or delineate a hole. + + Let us consider the following simple cubical complex and a perfect HDVF (top) together with the three corresponding homology generators (bottom, highlighted in pink): + +
+ + + + + A W operation between cells of Khalimsky coordinates \f$\sigma = (3,2)\f$ (`CRITICAL`) and \f$\tau=(5,2)\f$ (`SECONDARY`) produces the following HDVF (homology generators did not change; note that cohomology generators are modified): + + + + + + + + Then, a MW operation between cells of Khalimsky coordinates \f$\sigma' = (4,1)\f$ (`PRIMARY`) and \f$\tau=(3,2)\f$ (`SECONDARY`) produces the following HDVF where homology generators become "minimal": + + + + + + + + Perfect HDVFs provide various topological results: + - Betti numbers: are well known topological descriptors (providing the number of holes in each dimension). For a perfect HDVF, Betti numbers equal the number of critical cells (that can be obtained through `psc_flags()` with the `CRITICAL``PSC_flag` as argument). + - Homology/cohomology generators are actually algebraic objects, namely chains. Methods `homology_chain()` and `cohomology_chain()` return the homology and cohomology generator chain associated to a given critical cell. VTK export functions output all the cells of such chains with non zero coefficients. Figures here above illustrate such homology and co-homology generators. + - Homology/cohomology annotation: use `get_annotation()` or `get_coannotation()` to get the annotation/co-annotation of a cycle/co-cycle in the homology/cohomology basis (as a chain of critical cells). + - Homology/cohomology comparison: use `are_same_cycles()` or `are_same_cocycles()` to check if two cycles (or co-cycles) belong to the same homology / cohomology class. + + \cgalFigureBegin{HDVFannotation,HDVF_annotations.png} + Illustration of cycles annotation and comparison. Left: Generator \f$g(\sigma)\f$ associated to the critical cell \f$\sigma\f$; Middle: A cycle \f$\alpha\f$ ; Right: A second cycle \f$\beta\f$. + + For \f$\mathbb Z_2\f$ homology, the annotation of \f$\alpha\f$ is \f$\sigma\f$ (\f$\alpha\f$ equals \f$g(\sigma)\f$ up to a boundary), while the annotation of \f$\beta\f$ is \f$\sigma+\tau+\gamma\f$ (\f$\beta\f$ equals \f$g(\sigma)+g(\tau)+g(\gamma)\f$ up to a boundary). + + Therefore, `are_same_cycles` will return `true` for \f$\alpha\f$ and \f$g(\sigma)\f$, but `false` for \f$\beta\f$ and \f$g(\sigma)\f$. + \cgalFigureEnd + +\cgalModels{HDVF} + +\tparam ChainComplex a model of the `AbstractChainComplex` concept, providing the type of abstract chain complex used. + */ + +template +class Hdvf : public Hdvf_core { +public: + /*! \brief Chain complex type */ + typedef ChainComplex Chain_complex; + + /*! \brief Type of coefficients used to compute homology. */ + typedef typename Chain_complex::Coefficient_ring Coefficient_ring; + + /*! + Type of parent Hdvf_core class. + */ + typedef Hdvf_core Base ; + + // Inherited types + using Column_chain = Base::Column_chain; + using Row_chain = Base::Row_chain; + using Column_matrix = Base::Column_matrix; + using Row_matrix = Base::Row_matrix; + + /** + * \brief Default constructor. + * + * Builds an" empty" HDVF associated to K (with all cells critical). By default, the HDVF option is set to OPT_FULL (full reduction computed). + * + * \param K A chain complex (a model of `AbstractChainComplex`) + * \param hdvf_opt Option for HDVF computation (`OPT_BND`, `OPT_F`, `OPT_G` or `OPT_FULL`) + */ + Hdvf(const Chain_complex& K, int hdvf_opt = OPT_FULL) ; + + /* + * \brief Constructor by copy. + * + * Builds a HDVF by copy from another, including options. + * + * \param hdvf An initial HDVF. + */ + Hdvf(const Hdvf& hdvf) : Hdvf_core(hdvf) { } + + /* + * \brief HDVF destructor. */ + ~Hdvf() { } + + // findPair functions for M + + /** + * \brief Finds a valid Cell_pair of dimension q for M. + * + * The function searches a pair of cells \f$(\pi, \gamma)\f$ with \f$\pi\f$ `PRIMARY` and \f$\gamma\f$ `CRITICAL`, valid for M (ie.\ such that \f$\langle f(\pi), \gamma \rangle\f$ invertible). It returns the first valid pair found by iterators. + * + * \param q Dimension of the pair searched. + * \param found Reference to a %Boolean variable. The method sets `found` to `true` if a valid pair is found, `false` otherwise. + */ + Cell_pair find_pair_M(int q, bool &found) const; + + /** + * \brief Finds a valid Cell_pair of dimension q for M cointaining `tau`. + * + * The function searches a pair of cells \f$(\pi, \gamma)\f$ with \f$\pi\f$ `PRIMARY` and \f$\gamma\f$ `CRITICAL` (one of them is `tau`), valid for M (ie.\ such that \f$\langle f(\pi), \gamma \rangle\f$ invertible). It returns the first valid pair found by iterators. + * + * \param q Dimension of the pair searched. + * \param found Reference to a %Boolean variable. The method sets `found` to `true` if a valid pair is found, `false` otherwise. + * \param tau Cell of dimension `q` to pair. + */ + Cell_pair find_pair_M(int q, bool &found, size_t tau) const; + + /** + * \brief Finds *all* valid Cell_pair of dimension q for M. + * + * The function searches all pairs of cells \f$(\pi, \gamma)\f$ with \f$\pi\f$ `PRIMARY` and \f$\gamma\f$ `CRITICAL`, valid for M (ie.\ such that \f$\langle f(\pi), \gamma \rangle\f$ invertible). + * It returns a vector of such pairs. + * + * \param q Dimension of the pair searched. + * \param found Reference to a %Boolean variable. The method sets `found` to `true` if a valid pair is found, `false` otherwise. + */ + std::vector find_pairs_M(int q, bool &found) const; + + /** + * \brief Finds *all* valid Cell_pair of dimension q for M cointaining `tau`. + * + * The function searches all pairs of cells \f$(\pi, \gamma)\f$ with \f$\pi\f$ `PRIMARY` and \f$\gamma\f$ `CRITICAL` (one of them is `tau`), valid for M (ie.\ such that \f$\langle f(\pi), \gamma \rangle\f$ invertible). + * It returns a vector of such pairs. + * + * \param q Dimension of the pair searched. + * \param found Reference to a %Boolean variable. The method sets `found` to `true` if a valid pair is found, `false` otherwise. + * \param tau Cell of dimension `q` to pair. + */ + std::vector find_pairs_M(int q, bool &found, size_t tau) const; + + // findPair functions for W + + /** + * \brief Finds a valid Cell_pair of dimension q for W. + * + * The function searches a pair of cells \f$(\sigma, \gamma)\f$ with \f$\sigma\f$ `SECONDARY` and \f$\gamma\f$ `CRITICAL`, valid for W (ie.\ such that \f$\langle g(\gamma), \sigma \rangle\f$ invertible). It returns the first valid pair found by iterators. + * + * \param q Dimension of the pair searched. + * \param found Reference to a %Boolean variable. The method sets `found` to `true` if a valid pair is found, `false` otherwise. + */ + Cell_pair find_pair_W(int q, bool &found) const; + + /** + * \brief Finds a valid Cell_pair of dimension q for W cointaining `tau`. + * + * The function searches a pair of cells \f$(\sigma, \gamma)\f$ with \f$\sigma\f$ `SECONDARY` and \f$\gamma\f$ `CRITICAL` (one of them is `tau`), valid for W (ie.\ such that \f$\langle g(\gamma), \sigma \rangle\f$ invertible). It returns the first valid pair found by iterators. + * + * \param q Dimension of the pair searched. + * \param found Reference to a %Boolean variable. The method sets `found` to `true` if a valid pair is found, `false` otherwise. + * \param tau Cell of dimension `q` to pair. + */ + Cell_pair find_pair_W(int q, bool &found, size_t tau) const; + + /** + * \brief Finds *all* valid Cell_pair of dimension q for W. + * + * The function searches all pairs of cells \f$(\sigma, \gamma)\f$ with \f$\sigma\f$ `SECONDARY` and \f$\gamma\f$ `CRITICAL`, valid for W (ie.\ such that \f$\langle g(\gamma), \sigma \rangle\f$ invertible). + * It returns a vector of such pairs. + * + * \param q Dimension of the pair searched. + * \param found Reference to a %Boolean variable. The method sets `found` to `true` if a valid pair is found, `false` otherwise. + */ + std::vector find_pairs_W(int q, bool &found) const; + + /** + * \brief Finds *all* valid Cell_pair of dimension q for W cointaining `tau`. + * + * The function searches all pairs of cells \f$(\sigma, \gamma)\f$ with \f$\sigma\f$ `SECONDARY` and \f$\gamma\f$ `CRITICAL` (one of them is `tau`), valid for W (ie.\ such that \f$\langle g(\gamma), \sigma \rangle\f$ invertible). It returns the first valid pair found by iterators. + * It returns a vector of such pairs. + * + * \param q Dimension of the pair searched. + * \param found Reference to a %Boolean variable. The method sets `found` to `true` if a valid pair is found, `false` otherwise. + * \param tau Cell of dimension `q` to pair. + */ + std::vector find_pairs_W(int q, bool &found, size_t tau) const; + + // findPair functions for MW + + /** + * \brief Finds a valid Cell_pair of dimension q for MW. + * + * The function searches a pair of cells \f$(\pi, \sigma)\f$ with \f$\pi\f$ `PRIMARY` and \f$\sigma\f$ `SECONDARY`, valid for MW (ie.\ such that \f$\langle h_{q-1}\partial_q(\pi), \sigma \rangle\f$ invertible and \f$\langle \partial_{q+1} h_q(\sigma), \pi \rangle\f$ invertible). It returns the first valid pair found by iterators. + * + * \param q Dimension of the pair searched. + * \param found Reference to a %Boolean variable. The method sets `found` to `true` if a valid pair is found, `false` otherwise. + */ + Cell_pair find_pair_MW(int q, bool &found) const; + + /** + * \brief Finds a valid Cell_pair of dimension q for MW cointaining `tau`. + * + * The function searches a pair of cells \f$(\pi, \sigma)\f$ with \f$\pi\f$ `PRIMARY` and \f$\sigma\f$ `SECONDARY` (one of them is `tau`), valid for MW (ie.\ such that \f$\langle h_{q-1}\partial_q(\pi), \sigma \rangle\f$ invertible and \f$\langle \partial_{q+1} h_q(\sigma), \pi \rangle\f$ invertible). It returns the first valid pair found by iterators. + * + * \param q Dimension of the pair searched. + * \param found Reference to a %Boolean variable. The method sets `found` to `true` if a valid pair is found, `false` otherwise. + * \param tau Cell of dimension `q` to pair. + */ + Cell_pair find_pair_MW(int q, bool &found, size_t tau) const; + + /** + * \brief Finds *all* valid Cell_pair of dimension q for MW. + * + * The function searches all pairs of cells \f$(\pi, \sigma)\f$ with \f$\pi\f$ `PRIMARY` and \f$\sigma\f$ `SECONDARY`, valid for MW (ie.\ such that \f$\langle h_{q-1}\partial_q(\pi), \sigma \rangle\f$ invertible and \f$\langle \partial_{q+1} h_q(\sigma), \pi \rangle\f$ invertible). + * It returns a vector of such pairs. + * + * \param q Dimension of the pair searched. + * \param found Reference to a %Boolean variable. The method sets `found` to `true` if a valid pair is found, `false` otherwise. + */ + std::vector find_pairs_MW(int q, bool &found) const; + + /** + * \brief Finds *all* valid Cell_pair of dimension q for W cointaining `tau`. + * + * The function searches all pairs of cells \f$(\pi, \sigma)\f$ with \f$\pi\f$ `PRIMARY` and \f$\sigma\f$ `SECONDARY` (one of them is `tau`), valid for MW (ie.\ such that \f$\langle h_{q-1}\partial_q(\pi), \sigma \rangle\f$ invertible and \f$\langle \partial_{q+1} h_q(\sigma), \pi \rangle\f$ invertible). + * It returns a vector of such pairs. + * + * \param q Dimension of the pair searched. + * \param found Reference to a %Boolean variable. The method sets `found` to `true` if a valid pair is found, `false` otherwise. + * \param tau Cell of dimension `q` to pair. + */ + std::vector find_pairs_MW(int q, bool &found, size_t tau) const; + + // Hdvf methods + + /** + * \brief R operation (cancels a A operation). + * + * A pair of cells \f$(\pi, \sigma)\f$ of respective dimension q and q+1, with \f$\pi\f$ `PRIMARY` and \f$\sigma\f$ `SECONDARY`, is valid for R if \f$\langle h(\pi), \sigma \rangle\f$ is invertible. After the R operation, \f$\pi\f$ and \f$\sigma\f$ become `CRITICAL`. The R method updates the reduction accordingly (in time \f$\mathcal O(n^2)\f$). + * + * \param pi First cell of the pair (dimension `q`) + * \param sigma Second cell of the pair (dimension `q+1`) + * \param q Dimension of the pair + */ + void R(size_t pi, size_t sigma, int q); + + /** + * \brief M operation. + * + * A pair of cells \f$(\pi, \gamma)\f$ of dimension q, with \f$\pi\f$ `PRIMARY` and \f$\gamma\f$ `CRITICAL`, is valid for M if \f$\langle f(\pi), \gamma \rangle\f$ is invertible. After the M operation, \f$\pi\f$ becomes `CRITICAL` and \f$\gamma\f$ become `PRIMARY`. The M method updates the reduction accordingly (in time \f$\mathcal O(n^2)\f$). + * + * \param pi First cell of the pair (dimension `q`) + * \param gamma Second cell of the pair (dimension `q`) + * \param q Dimension of the pair + */ + void M(size_t pi, size_t gamma, int q); + + /** + * \brief W operation. + * + * A pair of cells \f$(\sigma, \gamma)\f$ of dimension q, with \f$\sigma\f$ `SECONDARY` and \f$\gamma\f$ `CRITICAL`, is valid for W if \f$\langle g(\gamma), \sigma \rangle\f$ is invertible. After the W operation, \f$\sigma\f$ becomes `CRITICAL` and \f$\gamma\f$ become `SECONDARY`. The W method updates the reduction accordingly (in time \f$\mathcal O(n^2)\f$). + * + * \param sigma First cell of the pair (dimension `q`) + * \param gamma Second cell of the pair (dimension `q`) + * \param q Dimension of the pair + */ + void W(size_t sigma, size_t gamma, int q); + + /** + * \brief MW operation. + * + * A pair of cells \f$(\pi, \sigma)\f$ of dimension q, with \f$\pi\f$ `PRIMARY` and \f$\sigma\f$ `SECONDARY`, is valid for MW if \f$\langle h_{q-1}\partial_q(\pi), \sigma \rangle\f$ is invertible and \f$\langle \partial_{q+1} h_q(\sigma), \pi \rangle\f$ is invertible. After the MW operation, \f$\pi\f$ becomes `SECONDARY` and \f$\sigma\f$ become `PRIMARY`. The MW method updates the reduction accordingly (in time \f$\mathcal O(n^2)\f$). + * + * \param pi First cell of the pair (dimension `q`) + * \param sigma Second cell of the pair (dimension `q`) + * \param q Dimension of the pair + */ + void MW(size_t pi, size_t sigma, int q); + + /** + * \brief Gets the annotation of a cycle in the homology basis. + * + * The method returns the image of a cycle by the morphism \f$f\f$, that is, a linear combination of critical cells (corresponding to the decomposition of the cycle in the homology basis). + * If the annotation has a single non zero coefficient for a given critical cell \f$\sigma\f$, then the cycle belongs to the class of the homology generator \f$g(\sigma)\f$. + * + * \warning Will raise an error is the chain provided is not a cycle. + * \warning The HDVF must be perfect. + * + * \param chain The cycle to annotate in the homology basis. + * \param dim Dimension of the cycle. + */ + Column_chain get_annotation(Column_chain chain, int dim) const + { + // Check that the chain is a cycle (must belong to the kernel of the boundary operator) + Column_chain bnd(this->_DD_col.at(dim) * chain) ; + if (!bnd.is_null()) + throw("get_annotation: the chain provided is not a cycle"); + + // Compute the annotation + return (this->_F_row.at(dim) * chain + this->projection(chain, CRITICAL, 1)) ; + } + + /** + * \brief Gets the co-annotation of a co-cycle in the cohomology basis. + * + * The method returns the image of a co-cycle by the morphism \f$g^*\f$, that is, a linear combination of critical cells (corresponding to the decomposition of the co-cycle in the cohomology basis). + * If the annotation has a single non zero coefficient for a given critical cell \f$\sigma\f$, then the co-cycle belongs to the class of the cohomology generator \f$f^*(\sigma)\f$. + * + * \warning Will raise an error is the chain provided is not a co-cycle. + * \warning The HDVF must be perfect. + * + * \param chain The co-cycle to annotate in the homology basis. + * \param dim Dimension of the co-cycle. + */ + Row_chain get_coannotation(Row_chain chain, int dim) const + { + // Check that the chain is a co-cycle (must belong to the kernel of the boundary operator) + Row_chain bnd(chain * this->_DD_col.at(dim+1)) ; + if (!bnd.is_null()) + throw("get_coannotation: the chain provided is not a co-cycle"); + + // Compute the co-annotation + return (chain * this->_G_col.at(dim) + this->projection(chain, CRITICAL, 1)) ; + } + + /** + * \brief Checks if two cycles belong to the same homology class. + * + * \warning Will raise an error is chains provided are not cycles. + * \warning The HDVF must be perfect. + * + * \param chain1 First cycle. + * \param chain2 Second cycle. + * \param dim Dimension of both cycles. + */ + bool are_same_cycles (Column_chain chain1, Column_chain chain2, int dim) + { + // Check if both chains are cycles (must belong to the kernel of the boundary operator) + Column_chain bnd1(this->_DD_col.at(dim) * chain1), bnd2(this->_DD_col.at(dim) * chain2) ; + if (!bnd1.is_null()) + throw("get_annotation: chain1 is not a cycle"); + if (!bnd2.is_null()) + throw("get_annotation: chain2 is not a cycle"); + + Column_chain annot1(get_annotation(chain1, dim)), annot2(get_annotation(chain2, dim)) ; + return annot1 == annot2 ; + } + + /** + * \brief Checks if two co-cycles belong to the same cohomology class. + * + * \warning Will raise an error is chains provided are not co-cycles. + * \warning The HDVF must be perfect. + * + * \param chain1 First co-cycle. + * \param chain2 Second co-cycle. + * \param dim Dimension of both co-cycles. + */ + bool are_same_cocycles (Row_chain chain1, Row_chain chain2, int dim) + { + // Check if both chains are cocycles (must belong to the kernel of the boundary^* operator) + Row_chain cobnd1(chain1 * this->_DD_col.at(dim)), cobnd2(chain2 * this->_DD_col.at(dim)) ; + if (!cobnd1.is_null()) + throw("get_coannotation: chain1 is not a co-cycle"); + if (!cobnd2.is_null()) + throw("get_coannotation: chain2 is not a co-cycle"); + + Row_chain coannot1(get_coannotation(chain1, dim)), coannot2(get_coannotation(chain2, dim)) ; + return coannot1 == coannot2 ; + } +}; + +// Constructor for the Hdvf class +template +Hdvf::Hdvf(const ChainComplex& K, int hdvf_opt) : Hdvf_core(K, hdvf_opt) { } + + + +// Methods to find a pair of cells for M in dimension q +// -> = +- 1 with cell1 primary, cell2 critical +// First version: returns a pair of dimensions q +// Second version: returns all the pairs containing sigma + +// \brief find a valid Cell_pair for M in dimension q + +template +Cell_pair Hdvf::find_pair_M(int q, bool &found) const +{ + found = false; + Cell_pair p; + + if (this->_hdvf_opt & (OPT_F | OPT_FULL)) + { + // Search for +-1 in _F - iterate over rows + for (OSM::Bitboard::iterator it_row = this->_F_row[q].begin(); (it_row != this->_F_row[q].end() && !found); ++it_row) + { + const Row_chain &row(OSM::cget_row(this->_F_row[q],*it_row)); + + // Iterate through the entries of the row + for (typename Row_chain::const_iterator it = row.begin(); (it != row.end() && !found); ++it) { + if ((it->second == 1) || (it->second == -1)) { + // If an entry with coefficient 1 or -1 is found, set the pair and mark as found + p.sigma = it->first; // primary cell + p.tau = *it_row; // critical cell + p.dim = q; + found = true; + } + } + } + } + return p; +} + +// find a valid Cell_pair containing tau for M in dimension q + +template +Cell_pair Hdvf::find_pair_M(int q, bool &found, size_t tau) const +{ + found = false; + Cell_pair p; + if (this->_flag.at(q).at(tau) == SECONDARY) + return p ; // Empty / found false + + if (this->_hdvf_opt & (OPT_F | OPT_FULL)) + { + // If tau is primary, search for gamma such that =+-1 + if (this->_flag.at(q).at(tau) == PRIMARY) + { + // Get the column of tau + const Column_chain& col(OSM::get_column(this->_F_row.at(q), tau)) ; + for (typename Column_chain::const_iterator it = col.cbegin(); (it != col.cend() && !found); ++it) + { + if (abs(it->second) == 1) + { + p.sigma = tau ; + p.tau = it->first ; + p.dim = q ; + found = true ; + } + } + } + // If tau is critical, search for pi such that =+-1 + if (this->_flag.at(q).at(tau) == CRITICAL) + { + // Search along the row of tau + const Row_chain& row(OSM::cget_row(this->_F_row.at(q), tau)) ; + for (typename Row_chain::const_iterator it = row.cbegin(); (it != row.cend() && !found); ++it) + { + if (abs(it->second) == 1) + { + p.sigma = it->first ; // primary cell + p.tau = tau ; // critical cell + p.dim = q ; + found = true; + } + } + } + } + return p; +} + +// find all the valid Cell_pair for M in dimension q +template +std::vector Hdvf::find_pairs_M(int q, bool &found) const +{ + found = false; + std::vector pairs; + if (this->_hdvf_opt & (OPT_F | OPT_FULL)) + { + // Search for +-1 in _F - iterate over rows + for (OSM::Bitboard::iterator it_row = this->_F_row[q].begin(); it_row != this->_F_row[q].end() ; ++it_row) + { + const Row_chain &row(OSM::cget_row(this->_F_row[q],*it_row)); + + // Iterate through the entries of the row + for (typename Row_chain::const_iterator it = row.begin(); it != row.end(); ++it) { + if ((it->second == 1) || (it->second == -1)) { + // If an entry with coefficient 1 or -1 is found, set the pair and add it + Cell_pair p ; + p.sigma = it->first; // primary cell + p.tau = *it_row; // critical cell + p.dim = q; + pairs.push_back(p) ; + } + } + } + } + found = !pairs.empty() ; + return pairs; +} + +// find all the valid Cell_pair containing tau for M in dimension q +template +std::vector Hdvf::find_pairs_M(int q, bool &found, size_t tau) const +{ + found = false; + std::vector pairs; + if (this->_flag.at(q).at(tau) == SECONDARY) + return pairs ; // Empty / found false + + if (this->_hdvf_opt & (OPT_F | OPT_FULL)) + { + // If tau is primary, search for gamma such that =+-1 + if (this->_flag.at(q).at(tau) == PRIMARY) + { + std::cout << "------PRIMARY" << std::endl ; + // Get the column of tau + const Column_chain& col(OSM::get_column(this->_F_row.at(q), tau)) ; + for (typename Column_chain::const_iterator it = col.cbegin(); it != col.cend(); ++it) + { + if (abs(it->second) == 1) + { + Cell_pair p ; + p.sigma = tau ; + p.tau = it->first ; + p.dim = q ; + found = true ; + pairs.push_back(p); + } + } + } + // If tau is critical, search for pi such that =+-1 + if (this->_flag.at(q).at(tau) == CRITICAL) + { + std::cout << "------CRITICAL" << std::endl ; + const Row_chain& row(OSM::get_row(this->_F_row.at(q), tau)) ; + for (typename Row_chain::const_iterator it = row.cbegin(); it != row.cend(); ++it) + { + if (abs(it->second) == 1) + { + Cell_pair p ; + p.sigma = it->first ; // primary cell + p.tau = tau ; // critical cell + p.dim = q ; + pairs.push_back(p) ; + } + } + } + } + found = !pairs.empty() ; + return pairs; +} + +// Methods to find a pair of cells for W in dimension q +// -> = +- 1 with cell1 secondary, cell2 critical +// First version: returns a pair of dimensions q +// Second version: returns all the pairs containing sigma + +// find a valid Cell_pair for W in dimension q +template +Cell_pair Hdvf::find_pair_W(int q, bool &found) const +{ + found = false; + Cell_pair p; + + if (this->_hdvf_opt & (OPT_G | OPT_FULL)) + { + // Search for +-1 in _G - iterate over cols + for (OSM::Bitboard::iterator it_col = this->_G_col[q].begin(); (it_col != this->_F_row[q].end() && !found); ++it_col) + { + Column_chain &col(this->_G_col[q][*it_col]); + + // Iterate through the entries of the col + for (typename Column_chain::const_iterator it = col.begin(); (it != col.end() && !found); ++it) { + if ((it->second == 1) || (it->second == -1)) { + // If an entry with coefficient 1 or -1 is found, set the pair and mark as found + p.sigma = it->first; // secondary cell + p.tau = *it_col; // critical cell + p.dim = q; + found = true; + } + } + } + } + return p; +} + +// find a valid Cell_pair containing tau for W in dimension q +template +Cell_pair Hdvf::find_pair_W(int q, bool &found, size_t tau) const +{ + found = false; + Cell_pair p; + if (this->_flag.at(q).at(tau) == PRIMARY) + return p ; // Empty / found false + + if (this->_hdvf_opt & (OPT_G | OPT_FULL)) + { + // If tau is primary, search for gamma such that =+-1 + if (this->_flag.at(q).at(tau) == SECONDARY) + { + for (OSM::Bitboard::iterator it_col = this->_G_col.at(q).begin(); (it_col != this->_G_col.at(q).end() && !found); ++it_col) + { + if (abs(this->_G_col.at(q).get_coefficient(tau, *it_col)) == 1) + { + p.sigma = tau ; // secondary cell + p.tau = *it_col ; // critical cell + p.dim = q ; + found = true; + } + } + } + // If tau is critical, search for sigma such that =+-1 + if (this->_flag.at(q).at(tau) == CRITICAL) + { + Column_chain col(OSM::get_column(this->_G_col.at(q), tau)) ; + for (typename Column_chain::const_iterator it = col.cbegin(); (it != col.cend() && !found); ++it) + { + if (abs(it->second) == 1) + { + p.sigma = it->first ; // secondary cell + p.tau = tau ; // critical cell + p.dim = q ; + found = true; + } + } + } + } + return p; +} + +// find all the valid Cell_pair for W in dimension q +template +std::vector Hdvf::find_pairs_W(int q, bool &found) const +{ + found = false; + std::vector pairs; + + if (this->_hdvf_opt & (OPT_G | OPT_FULL)) + { + // Search for +-1 in _G - iterate over cols + for (OSM::Bitboard::iterator it_col = this->_G_col[q].begin(); it_col != this->_F_row[q].end(); ++it_col) + { + Column_chain &col = this->_G_col[q][*it_col]; + + // Iterate through the entries of the col + for (typename Column_chain::const_iterator it = col.begin(); it != col.end(); ++it) { + if ((it->second == 1) || (it->second == -1)) { + // If an entry with coefficient 1 or -1 is found, set the pair and mark as found + Cell_pair p ; + p.sigma = it->first; // secondary cell + p.tau = *it_col; // critical cell + p.dim = q; + pairs.push_back(p) ; + } + } + } + } + found = !pairs.empty() ; + return pairs; +} + +// find all the valid Cell_pair containing tau for W in dimension q +template +std::vector Hdvf::find_pairs_W(int q, bool &found, size_t tau) const +{ + found = false; + std::vector pairs; + if (this->_flag.at(q).at(tau) == PRIMARY) + return pairs ; // Empty / found false + + if (this->_hdvf_opt & (OPT_G | OPT_FULL)) + { + // If tau is primary, search for gamma such that =+-1 + if (this->_flag.at(q).at(tau) == SECONDARY) + { + for (OSM::Bitboard::iterator it_col = this->_G_col.at(q).begin(); it_col != this->_G_col.at(q).end(); ++it_col) + { + if (abs(get_coefficient(this->_G_col.at(q), tau, *it_col)) == 1) + { + Cell_pair p ; + p.sigma = tau ; // secondary cell + p.tau = *it_col ; // critical cell + p.dim = q ; + pairs.push_back(p) ; + } + } + } + // If tau is critical, search for sigma such that =+-1 + if (this->_flag.at(q).at(tau) == CRITICAL) + { + Column_chain col(OSM::get_column(this->_G_col.at(q), tau)) ; + for (typename Column_chain::const_iterator it = col.cbegin(); it != col.cend(); ++it) + { + if (abs(it->second) == 1) + { + Cell_pair p ; + p.sigma = it->first ; // secondary cell + p.tau = tau ; // critical cell + p.dim = q ; + pairs.push_back(p) ; + } + } + } + } + found = !pairs.empty() ; + return pairs; +} + +// Method to find a pair of cells for MW in dimension q +// -> = +- 1 and = +- 1 with cell1 primary, cell2 secondary +// First version: returns a pair of dimensions q +// Second version: returns all the pairs containing sigma + +// find a valid Cell_pair for MW in dimension q +template +Cell_pair Hdvf::find_pair_MW(int q, bool &found) const +{ + found = false; + Cell_pair p; + + if (this->_hdvf_opt & OPT_FULL) + { + // pi and sigma must at least satisfy that col pi of H_q and row sigma of H_q-1 are non zero + // iterate on H_q and H_q-1 accordingly + for (OSM::Bitboard::iterator it_pi = this->_H_col[q].begin(); (it_pi != this->_H_col[q].end() && !found); ++it_pi) + { + const size_t pi = *it_pi ; + Column_chain H11(OSM::get_column(this->_H_col.at(q), pi)) ; + Column_chain d_pi = this->_K.d(pi, q) ; + Column_chain projP_d_pi = this->projection(d_pi, PRIMARY, q-1) ; + for (size_t sigma = 0; (sigma < this->_H_col.at(q-1).dimensions().first && !found); ++sigma) + { + Row_chain H11q1(OSM::get_row(this->_H_col.at(q-1), sigma)) ; + if (!H11q1.is_null()) + { + Row_chain cod_sigma = this->_K.cod(sigma, q) ; + Row_chain projS_cod_sigma = this->projection(cod_sigma, SECONDARY, q+1) ; + + // Compute xi and xi' to test the validity of MW + + const Coefficient_ring xi = projS_cod_sigma * H11 ; + const Coefficient_ring xip = H11q1 * projP_d_pi ; + found = ((abs(xi) == 1) && (abs(xip) == 1)) ; + if (found) + { + p.sigma = pi ; // primary cell + p.tau = sigma ; // secondary cell + p.dim = q ; + } + } + } + } + } + return p; +} + +// find a valid Cell_pair containing tau for MW in dimension q +template +Cell_pair Hdvf::find_pair_MW(int q, bool &found, size_t tau) const +{ + found = false; + Cell_pair p; + + if (this->_flag.at(q).at(tau) == CRITICAL) + return p ; // Empty / found false + + if (this->_hdvf_opt & OPT_FULL) + { + // If tau is primary (rename pi), search for a valid sigma + if (this->_flag.at(q).at(tau) == PRIMARY) + { + const size_t pi(tau) ; + // Col pi of H_q and proj_P(d(pi)) must at least be non empty + Column_chain H11(OSM::get_column(this->_H_col.at(q), pi)) ; + Column_chain d_pi = this->_K.d(pi, q) ; + Column_chain projP_d_pi = this->projection(d_pi, PRIMARY, q-1) ; + + if (H11.is_null() || projP_d_pi.is_null()) + return p ; + + // Search for sigma with col_sigma(H_q-1) non empty + for (size_t sigma = 0; (sigma < this->_H_col.at(q-1).dimensions().first && !found); ++sigma) + { + Row_chain H11q1(OSM::get_row(this->_H_col.at(q-1), sigma)) ; + if (!H11q1.is_null()) + { + // and proj_S(cod(sigma)) non empty + Row_chain cod_sigma = this->_K.cod(sigma, q) ; + Row_chain projS_cod_sigma = this->projection(cod_sigma, SECONDARY, q+1) ; + if (!projS_cod_sigma.is_null()) + { + // test xi and xip + const Coefficient_ring xi = projS_cod_sigma * H11 ; + const Coefficient_ring xip = H11q1 * projP_d_pi ; + found = ((abs(xi) == 1) && (abs(xip) == 1)) ; + if (found) + { + p.sigma = pi ; // primary cell + p.tau = sigma ; // critical cell + p.dim = q ; + } + } + } + } + } + else // cell is secondary + { + // cout << "secondary" << endl ; + const size_t sigma(tau) ; + // Row sigma of H_q-1 and proj_S(cod(sigma)) must at least be non empty + Row_chain H11q1(OSM::get_row(this->_H_col.at(q-1), sigma)) ; + Row_chain cod_sigma = this->_K.cod(sigma, q) ; + Row_chain projS_cod_sigma = this->projection(cod_sigma, SECONDARY, q+1) ; + if (H11q1.is_null() || projS_cod_sigma.is_null()) + return p ; + + // Search for pi with col pi of H_q non empty + for (OSM::Bitboard::iterator it_pi = this->_H_col[q].begin(); (it_pi != this->_H_col[q].end() && !found); ++it_pi) + { + const size_t pi(*it_pi) ; + + Column_chain H11(OSM::get_column(this->_H_col.at(q), pi)) ; + Column_chain d_pi = this->_K.d(pi, q) ; + Column_chain projP_d_pi = this->projection(d_pi, PRIMARY, q-1) ; + // proj_P of d(pi) must also be non empty + if (!projP_d_pi.is_null()) + { + // test xi and xip + const Coefficient_ring xi = projS_cod_sigma * H11 ; + const Coefficient_ring xip = H11q1 * projP_d_pi ; + found = ((abs(xi) == 1) && (abs(xip) == 1)) ; + if (found) + { + p.sigma = pi ; // primary cell + p.tau = sigma ; // secondary cell + p.dim = q ; + } + } + } + } + } + return p; +} + +// find all the valid Cell_pair for MW in dimension q +template +std::vector Hdvf::find_pairs_MW(int q, bool &found) const +{ + found = false; + std::vector pairs; + + if (this->_hdvf_opt & OPT_FULL) + { + // pi and sigma must at least satisfy that col pi of H_q and row sigma of H_q-1 are non zero + // iterate on H_q and H_q-1 accordingly + for (OSM::Bitboard::iterator it_pi = this->_H_col[q].begin(); it_pi != this->_H_col[q].end(); ++it_pi) + { + const size_t pi = *it_pi ; + Column_chain H11(OSM::get_column(this->_H_col.at(q), pi)) ; + Column_chain d_pi = this->_K.d(pi, q) ; + Column_chain projP_d_pi = this->projection(d_pi, PRIMARY, q-1) ; + for (size_t sigma = 0; sigma < this->_H_col.at(q-1).dimensions().first; ++sigma) + { + Row_chain H11q1(OSM::get_row(this->_H_col.at(q-1), sigma)) ; + if (!H11q1.is_null()) + { + Row_chain cod_sigma = this->_K.cod(sigma, q) ; + Row_chain projS_cod_sigma = this->projection(cod_sigma, SECONDARY, q+1) ; + + // Compute xi and xi' to test the validity of MW + + const Coefficient_ring xi = projS_cod_sigma * H11 ; + const Coefficient_ring xip = H11q1 * projP_d_pi ; + found = ((abs(xi) == 1) && (abs(xip) == 1)) ; + if (found) + { + Cell_pair p; + p.sigma = pi ; // primary cell + p.tau = sigma ; // secondary cell + p.dim = q ; + pairs.push_back(p) ; + } + } + } + } + } + found = !pairs.empty() ; + return pairs; +} + +// find all the valid Cell_pair containing tau for MW in dimension q +template +std::vector Hdvf::find_pairs_MW(int q, bool &found, size_t tau) const +{ + found = false; + std::vector pairs; + if (this->_flag.at(q).at(tau) == CRITICAL) + return pairs ; // Empty / found false + + if (this->_hdvf_opt & OPT_FULL) + { + // If tau is primary (rename pi), search for a valid sigma + if (this->_flag.at(q).at(tau) == PRIMARY) + { + const size_t pi(tau) ; + // Col pi of H_q and proj_P(d(pi)) must at least be non empty + Column_chain H11(OSM::get_column(this->_H_col.at(q), pi)) ; + Column_chain d_pi = this->_K.d(pi, q) ; + Column_chain projP_d_pi = this->projection(d_pi, PRIMARY, q-1) ; + + if (H11.is_null() || projP_d_pi.is_null()) + return pairs ; + + // Search for sigma with col_sigma(H_q-1) non empty + for (size_t sigma = 0; sigma < this->_H_col.at(q-1).dimensions().first; ++sigma) + { + Row_chain H11q1(OSM::get_row(this->_H_col.at(q-1), sigma)) ; + if (!H11q1.is_null()) + { + // and proj_S(cod(sigma)) non empty + Row_chain cod_sigma = this->_K.cod(sigma, q) ; + Row_chain projS_cod_sigma = this->projection(cod_sigma, SECONDARY, q+1) ; + if (!projS_cod_sigma.is_null()) + { + // test xi and xip + const Coefficient_ring xi = projS_cod_sigma * H11 ; + const Coefficient_ring xip = H11q1 * projP_d_pi ; + found = ((abs(xi) == 1) && (abs(xip) == 1)) ; + if (found) + { + Cell_pair p ; + p.sigma = pi ; // primary cell + p.tau = sigma ; // critical cell + p.dim = q ; + pairs.push_back(p) ; + } + } + } + } + } + else // cell is secondary + { + // cout << "secondary" << endl ; + const size_t sigma(tau) ; + // Row sigma of H_q-1 and proj_S(cod(sigma)) must at least be non empty + Row_chain H11q1(OSM::get_row(this->_H_col.at(q-1), sigma)) ; + Row_chain cod_sigma = this->_K.cod(sigma, q) ; + Row_chain projS_cod_sigma = this->projection(cod_sigma, SECONDARY, q+1) ; + if (H11q1.is_null() || projS_cod_sigma.is_null()) + return pairs ; + + // Search for pi with col pi of H_q non empty + for (OSM::Bitboard::iterator it_pi = this->_H_col[q].begin(); it_pi != this->_H_col[q].end(); ++it_pi) + { + const size_t pi(*it_pi) ; + + Column_chain H11(OSM::get_column(this->_H_col.at(q), pi)) ; + Column_chain d_pi = this->_K.d(pi, q) ; + Column_chain projP_d_pi = this->projection(d_pi, PRIMARY, q-1) ; + // proj_P of d(pi) must also be non empty + if (!projP_d_pi.is_null()) + { + // test xi and xip + const Coefficient_ring xi = projS_cod_sigma * H11 ; + const Coefficient_ring xip = H11q1 * projP_d_pi ; + found = ((abs(xi) == 1) && (abs(xip) == 1)) ; + if (found) + { + Cell_pair p ; + p.sigma = pi ; // primary cell + p.tau = sigma ; // secondary cell + p.dim = q ; + pairs.push_back(p) ; + } + } + } + } + } + found = !pairs.empty() ; + return pairs; +} + + +// Method to perform operation R +// pi is in dimension q, sigma is in dimension q+1 +template +void Hdvf::R(size_t pi, size_t sigma, int q) { + //----------------------------------------------- Submatrices of H ---------------------------------------------------- + + if (this->_hdvf_opt & OPT_FULL) + { + // Output operation details to the console + std::cout << "R of " << pi << "(dim " << q << ") / " << sigma << "(dim " << q + 1 << ")" << std::endl; + + // Extract the relevant row and column chains from this->_H_col + Row_chain H12 = OSM::get_row(this->_H_col[q], sigma); // H12 is the row chain from this->_H_col[q] at index sigma + Column_chain H21 = OSM::get_column(this->_H_col[q], pi); // H21 is the column chain from this->_H_col[q] at index pi + + // Get the coefficient at the intersection of H12 and H21 + Coefficient_ring H11(H12[pi]); // H11 is the coefficient at row sigma and column pi + + // Assert that H11 is either 1 or -1 (check invertibility) + assert((H11 == 1) || (H11 == -1)); // !!!!! Test invertibility + Coefficient_ring H11_inv = H11; // Inverse of H11 (which is itself for 1 or -1) + + // Remove the contributions of pi from H12 and sigma from H21 + H12 /= std::vector({pi}); // Remove column pi from H12 + H21 /= std::vector({sigma}); // Remove row sigma from H21 + + // Remove the corresponding row and column from this->_H_col + remove_row(this->_H_col[q], sigma); // Remove row sigma from this->_H_col[q] + remove_column(this->_H_col[q], pi); // Remove column pi from this->_H_col[q] + + //---------------------------------------------- Submatrices of F ----------------------------------------------------- + + // Extract the relevant column chain from this->_F_row + Column_chain F11 = OSM::get_column(this->_F_row[q], pi); // F11 is the column chain from this->_F_row[q] at index pi + + // Remove the column pi from this->_F_row + remove_column(this->_F_row[q], pi); + + //--------------------------------------------- Submatrices of G ------------------------------------------------------ + + // Extract the relevant row chain from this->_G_col + Row_chain G11 = OSM::get_row(this->_G_col[q + 1], sigma); // G11 is the row chain from this->_G_col[q+1] at index sigma + + // Remove the row sigma from this->_G_col + remove_row(this->_G_col[q + 1], sigma); + + //--------------------------------------------- Update matrices ------------------------------------------------------- + + // Update this->_F_row[q] + // Subtract the product of (H12 * F11) and H11 from this->_F_row[q] + // Note: % operator returns a row matrix, so be careful with operations + this->_F_row[q] -= (F11 % H12) * H11_inv; + + // Set the row pi of this->_F_row[q] to (H12 * (-H11_inv)) + OSM::set_row(this->_F_row[q], pi, H12 * (-H11_inv)); + + // Update this->_G_col[q + 1] + // Subtract the product of (H21 * G11) and H11_inv from this->_G_col[q + 1] + this->_G_col[q + 1] -= (H21 * G11) * H11_inv; + + // Set the column sigma of this->_G_col[q + 1] to (H21 * (-H11_inv)) + OSM::set_column(this->_G_col[q + 1], sigma, H21 * (-H11_inv)); + + // Update this->_DD_col + // Compute the temporary matrix product F11 * G11 + this->_DD_col[q + 1] += (F11 * G11) * H11_inv; + + // Set the row pi in this->_DD_col[q + 1] to (G11 * H11_inv) + OSM::set_row(this->_DD_col[q + 1], pi, G11 * H11_inv); + + // Set the column sigma in this->_DD_col[q + 1] to (F11 * H11_inv) + OSM::set_column(this->_DD_col[q + 1], sigma, F11 * H11_inv); + + // Set the coefficient at (pi, sigma) in this->_DD_col to H11_inv + this->_DD_col[q + 1].set_coefficient(pi, sigma, H11_inv); + + // Update this->_H_col[q] + // Subtract the product of (H21 * H12) and H11_inv from this->_H_col[q] + this->_H_col[q] -= (H21 * H12) * H11_inv; + + // Perform additional updates + + // Extract boundary and coboundary chains + Column_chain bnd_pi(this->_K.d(pi, q)); // Boundary of pi in dimension q + Row_chain cobnd_sigma(this->_K.cod(sigma, q + 1)); // Coboundary of sigma in dimension q+1 + + // Project the boundary and coboundary chains onto PRIMARY and SECONDARY flags + Column_chain proj_P_pi(this->projection(bnd_pi, PRIMARY, q)); + Row_chain proj_S_sigma(this->projection(cobnd_sigma, SECONDARY, q + 1)); + + if (q > 0) { + // Update this->_DD_col[q] with projections and this->_F_row[q-1] + Column_chain c1(this->_F_row[q - 1] * proj_P_pi + this->projection(bnd_pi, CRITICAL, q)); + OSM::set_column(this->_DD_col[q], pi, c1); + OSM::set_column(this->_G_col[q], pi, this->_H_col[q - 1] * proj_P_pi); + } + + // Update this->_F_row[q+1] with this->projection and this->_H_col[q+1] + OSM::set_row(this->_F_row[q + 1], sigma, proj_S_sigma * this->_H_col[q + 1]); + + if (q + 2 <= this->_K.dimension()) { + // Update this->_DD_col[q+2] with projections + Row_chain c4(this->projection(cobnd_sigma, CRITICAL, q + 1) + proj_S_sigma * this->_G_col[q + 2]); + OSM::set_row(this->_DD_col[q + 2], sigma, c4); + } + + // Update flags + this->_flag[q][pi] = CRITICAL; // Set the PSC_flag of pi in dimension q to CRITICAL + ++this->_nb_C.at(q) ; + --this->_nb_P.at(q) ; + this->_flag[q + 1][sigma] = CRITICAL; // Set the PSC_flag of sigma in dimension q+1 to CRITICAL + ++this->_nb_C.at(q+1) ; + --this->_nb_S.at(q+1) ; + } + else + std::cout << "!!! R impossible with partial reduction options" << std::endl; +} + +// Method to perform operation M +// pi is in dimension q, gamma is in dimension q +template +void Hdvf::M(size_t pi, size_t gamma, int q) { + //----------------------------------------------- Submatrices of F ---------------------------------------------------- + + if (this->_hdvf_opt & OPT_FULL) + { + // Output the operation details to the console + std::cout << "M_" << q << "(" << pi << "," << gamma << ")" << std::endl; + + if (q == this->_K.dimension()) + throw("M operation in max dimension !!!") ; + + // Extract row and column chains from this->_F_row + Row_chain F12(OSM::get_row(this->_F_row[q], gamma)); // F12 is the row chain from this->_F_row[q] at index gamma + Column_chain F21(OSM::get_column(this->_F_row[q], pi)); // F21 is the column chain from this->_F_row[q] at index pi + + // Get the coefficient at the intersection of F12 and F21 + const Coefficient_ring F11(F12.get_coefficient(pi)); // F11 is the coefficient at row gamma and column pi + + // Assert that F11 is either 1 or -1 (for invertibility) + assert((F11 == 1) || (F11 == -1)); // !!!!! Test invertibility + Coefficient_ring F11_inv = F11; // Inverse of F11 (which is itself for 1 or -1) + + // Remove the contributions of pi from F12 and gamma from F21 + F12 /= std::vector({pi}); // Remove column pi from F12 + F21 /= std::vector({gamma}); // Remove row gamma from F21 + + // Remove the corresponding row and column from this->_F_row + remove_row(this->_F_row[q], gamma); // Remove row gamma from this->_F_row[q] + remove_column(this->_F_row[q], pi); // Remove column pi from this->_F_row[q] + + //--------------------------------------------- Submatrices of G ------------------------------------------------------ + + // Extract the relevant column chain from this->_G_col + remove_column(this->_G_col[q], gamma); // Remove column gamma from this->_G_col[q] + + //---------------------------------------------- Submatrices of H ----------------------------------------------------- + + // Extract the relevant column chain from this->_H_col + Column_chain H11(OSM::get_column(this->_H_col[q], pi)); // H11 is the column chain from this->_H_col[q] at index pi + + // Remove the column pi from this->_H_col + remove_column(this->_H_col[q], pi); + + //--------------------------------------------- Submatrices of DD_q+1 ------------------------------------------------------ + + // For DD_q+1 and DD_q: + // Extract the relevant row chains from this->_DD_col + Row_chain D11(OSM::get_row(this->_DD_col[q+1], gamma)); // D11 is the row chain from this->_DD_col[q+1] at index gamma + remove_row(this->_DD_col[q + 1], gamma); // Remove row gamma from this->_DD_col[q + 1] + + //--------------------------------------------- Submatrices of DD ------------------------------------------------------ + + // DD_q (corresponds to the column matrix of this->_DD_col) + if (q > 0) + remove_column(this->_DD_col[q], gamma); // Remove column gamma from this->_DD_col[q] + + //--------------------------------------------- Update matrices ------------------------------------------------------- + + // Update this->_H_col + // Subtract the product of (H11 * F11_inv) and F12 from this->_H_col[q] + // Note: * operator is used for matrix column results + this->_H_col[q] -= (H11 * F11_inv) * F12; + + // Set the column gamma of this->_H_col[q] to (-1 * H11) * F11_inv + OSM::set_column(this->_H_col[q], gamma, (-1 * H11) * (F11_inv)); + + // Update this->_F_row + // Add the product of (F21 * F11_inv) and F12 to this->_F_row[q] + this->_F_row[q] -= (F21 * F11_inv) * F12; + + // Set the row pi of this->_F_row[q] to F11_inv * F12 + OSM::set_row(this->_F_row[q], pi, F11_inv * F12); + + // Set the column gamma of this->_F_row[q] to F21 * (-F11_inv) + OSM::set_column(this->_F_row[q], gamma, F21 * (-F11_inv)); + + // Set the coefficient at (pi, gamma) in this->_F_row[q] to F11_inv + set_coefficient(this->_F_row[q], pi, gamma, F11_inv); + + // Update this->_G_col + // Add the product of (H11 * F11_inv) and D11 to this->_G_col[q+1] + this->_G_col[q + 1] += (H11 * F11_inv) * D11; + + // Update this->_DD_col[q + 1] + this->_DD_col[q + 1] -= (F21 * F11_inv) * D11; // Subtract the product from this->_DD_col[q + 1] + OSM::set_row(this->_DD_col[q + 1], pi, D11 * F11_inv); // Set the row pi in this->_DD_col[q + 1] + + // Update this->_G_col (for dimension q) and this->_DD_col (for dimension q) + if (q>0) + { + // Extract boundary chain and project it + Column_chain c = this->_K.d(pi, q); // Boundary of pi in dimension q + Column_chain projection_p(this->projection(c, PRIMARY, q-1)); // Project boundary chain to PRIMARY + Column_chain projection_c = this->projection(c, CRITICAL, q-1); // Project boundary chain to CRITICAL + + // Set the column pi of this->_G_col[q] to (-1 * this->_H_col[q-1]) * projection_p + OSM::set_column(this->_G_col[q], pi, (Coefficient_ring(-1) * this->_H_col[q - 1]) * projection_p); + + // Update this->_DD_col + // Extract projections and perform updates + Column_chain tmp(this->_F_row[q - 1] * projection_p + projection_c); // Compute the product of this->_F_row[q - 1] and projection_p + // Set the column pi of this->_DD_col[q] to cc + OSM::set_column(this->_DD_col[q], pi, tmp); + } + // else: if the dimension is 0, no change for this->_G_col[q] and this->_DD_col[q] + + // Update flags + this->_flag[q][pi] = CRITICAL; // Set the PSC_flag of pi in dimension q to PRIMARY + this->_flag[q][gamma] = PRIMARY; // Set the PSC_flag of gamma in dimension q to CRITICAL + } + else + std::cout << "!!! M impossible with partial reduction options" << std::endl; +} + +// Method to perform operation W +// gamma is in dimension q, sigma is in dimension q +template +void Hdvf::W(size_t sigma, size_t gamma, int q) { + //----------------------------------------------- Submatrices of G ---------------------------------------------------- + + if (this->_hdvf_opt & OPT_FULL) + { + // Output the operation details to the console + std::cout << "W_" << q << "(" << sigma << "," << gamma << ")" << std::endl; + + if (q == 0) + throw("W operation in dimension 0 !!!") ; + + // Extract row and column chains from this->_G_col + Row_chain G12(OSM::get_row(this->_G_col[q], sigma)); // G12 is the row chain from this->_G_col[q] at index sigma + Column_chain G21(OSM::get_column(this->_G_col[q], gamma)); // G21 is the column chain from this->_G_col[q] at index gamma + + // Get the coefficient at the intersection of G12 and G21 + Coefficient_ring G11(G12.get_coefficient(gamma)); // G11 is the coefficient at row sigma and column gamma + + // Assert that G11 is either 1 or -1 (for invertibility) + assert((G11 == 1) || (G11 == -1)); // !!!!! Test invertibility + Coefficient_ring G11_inv = G11; // Inverse of G11 (which is itself for 1 or -1) + + // Remove the contributions of gamma from G12 and sigma from G21 + G12 /= std::vector({gamma}); // Remove column gamma from G12 + G21 /= std::vector({sigma}); // Remove row sigma from G21 + + // Remove the corresponding row and column from this->_G_col + remove_row(this->_G_col[q], sigma); // Remove row sigma from this->_G_col[q] + remove_column(this->_G_col[q], gamma); // Remove column gamma from this->_G_col[q] + + //---------------------------------------------- Submatrices of F ----------------------------------------------------- + + // Extract the row chain from this->_F_row + + // Remove the row gamma from this->_F_row + remove_row(this->_F_row[q], gamma); + + //--------------------------------------------- Submatrices of H ------------------------------------------------------ + + // Extract the row chain from this->_H_col + Row_chain H11(OSM::get_row(this->_H_col[q-1], sigma)); // H11 is the row chain from this->_H_col[q] at index sigma + + // Remove the row sigma from this->_H_col + remove_row(this->_H_col[q-1], sigma); + + //--------------------------------------------- Submatrices of DD_q+1 ------------------------------------------------------ + + // Extract the row chain from this->_DD_col[q+1] + + // Remove the row gamma from this->_DD_col + if (q < this->_K.dimension()) + remove_row(this->_DD_col[q + 1], gamma); + + //--------------------------------------------- Submatrices of DD_q ------------------------------------------------------ + + // Extract the column chain from this->_DD_col[q] + Column_chain D11_q(OSM::get_column(this->_DD_col[q], gamma)); // D11_q is the column chain from this->_DD_col[q] at index gamma + + // Remove the column gamma from this->_DD_col + remove_column(this->_DD_col[q], gamma); + + //--------------------------------------------- Update matrices ------------------------------------------------------- + + // Update this->_H_col + // Subtract the product of (G21 * G11_inv) and H11 from this->_H_col[q] + // Note: % operator is used for matrix row results + this->_H_col[q-1] -= (G21 * G11_inv) * H11; + + // Set the row gamma of this->_H_col[q] to (-1 * H11) * G11_inv + OSM::set_row(this->_H_col[q-1], gamma, (-1 * H11) * (G11_inv)); + + // Update this->_G_col + // Subtract the product of G11_inv and (G21 * G12) from this->_G_col[q] + this->_G_col[q] -= (G21 * G11_inv) * G12; + + // Set the row gamma of this->_G_col[q] to G11_inv * G12 + OSM::set_row(this->_G_col[q], gamma, -G11_inv * G12); + + // Set the column sigma of this->_G_col[q] to G11_inv * G21 + OSM::set_column(this->_G_col[q], sigma, G21 * G11_inv); + + // Set the coefficient at (gamma, sigma) in this->_G_col[q] to G11_inv + set_coefficient(this->_G_col[q], gamma, sigma, G11_inv); + + // Update this->_F_row (for dimension q-1) + // Add the product of (D11_q * G11_inv) and H11 to this->_F_row[q - 1] + this->_F_row[q - 1] += (D11_q * G11_inv) * H11; + + // Update this->_DD_col (for dimension q) + this->_DD_col[q] -= (D11_q * G11_inv) * G12;// Subtract the product of (D11_q * G11_inv) and G12 + OSM::set_column(this->_DD_col[q], sigma, D11_q * G11_inv) ; + + // Update this->_F_row (for dimension q) and this->_DD_col (for dimension q+1) + if (q < this->_K.dimension()) + { + // Extract boundary chain and project it + Row_chain c = this->_K.cod(sigma, q); // Boundary of sigma in dimension q + Row_chain projection_s(this->projection(c, SECONDARY, q+1)); // Project boundary chain to SECONDARY + Row_chain projection_c(this->projection(c, CRITICAL, q+1)); // Project boundary chain to SECONDARY + + // Set the row sigma of this->_F_row[q] to (-1 * projection_s * this->_H_col[q]) + OSM::set_row(this->_F_row[q], sigma, (-1 * projection_s) * this->_H_col[q]); + + // Set the row sigma of this->_DD_col[q + 1] to projection_s * this->_G_col[q + 1] + projection_c + + Row_chain tmp(projection_s * this->_G_col[q + 1] + projection_c) ; + OSM::set_row(this->_DD_col[q + 1], sigma, tmp); + } + // else : if the dimension is maximal, no update of this->_DD_col[q+1] and this->_F_row[q] + + // Update flags + this->_flag[q][gamma] = SECONDARY; // Set the PSC_flag of gamma in dimension q to SECONDARY + this->_flag[q][sigma] = CRITICAL; // Set the PSC_flag of sigma in dimension q to CRITICAL + } + else + std::cout << "!!! W impossible with partial reduction options" << std::endl; +} + +// Method to perform operation MW +// gamma is in dimension q, sigma is in dimension q +template +void Hdvf::MW(size_t pi, size_t sigma, int q) { + //----------------------------------------------- Submatrices of G ---------------------------------------------------- + + if (this->_hdvf_opt & OPT_FULL) + { + // Output the operation details to the console + std::cout << "MW_" << q << "(" << pi << "," << sigma << ")" << std::endl; + + if (q <= 0) + throw("MW operation in dimension 0 !!!") ; + if (q >= this->_K.dimension()) + throw("MW operation in maximal dimension !!!") ; + + // In order to compute xi and xi', extract sub-matrices of H_q, H_q-1 and compute d(pi) and cod(sigma) + + // H_q extractions + + Column_chain H11(OSM::get_column(this->_H_col.at(q), pi)) ; + // H21 -> delete H11 + this->_H_col.at(q) /= std::vector({pi}) ; + + // H_q-1 extractions + + Row_chain H11q1(OSM::get_row(this->_H_col.at(q-1), sigma)) ; + // H21_q-1 -> delete H11q1 + remove_row(this->_H_col.at(q-1), sigma) ; + + // d(pi) + + Column_chain d_pi = this->_K.d(pi, q) ; + Column_chain projP_d_pi = this->projection(d_pi, PRIMARY, q-1) ; + Column_chain projC_d_pi = this->projection(d_pi, CRITICAL, q-1) ; + + // cod(sigma) + + Row_chain cod_sigma = this->_K.cod(sigma, q) ; + Row_chain projS_cod_sigma = this->projection(cod_sigma, SECONDARY, q+1) ; + Row_chain projC_cod_sigma = this->projection(cod_sigma, CRITICAL, q+1) ; + + // Compute xi and xi' to test the validity of MW + + Coefficient_ring xi = projS_cod_sigma * H11 ; + Coefficient_ring xip = H11q1 * projP_d_pi ; + + if (abs(xi) != 1) + throw "MW impossible, xi non invertible" ; + if (abs(xip) != 1) + throw "MW impossible, xi' non invertible" ; + + // F_q extraction + + Column_chain F11(OSM::get_column(this->_F_row.at(q), pi)) ; + // F12 -> delete col F11 + remove_column(this->_F_row.at(q), pi) ; + + // G_q extractions + + Row_chain G11(OSM::get_row(this->_G_col.at(q), sigma)) ; + // G21 -> dele row G11 + remove_row(this->_G_col.at(q), sigma) ; + + // ----------- Update of the reduction + + // H_q + + Row_chain tmp1 = projS_cod_sigma * this->_H_col.at(q) ; + + this->_H_col.at(q) -= (H11 * xi) * tmp1 ; + OSM::set_column(this->_H_col.at(q), sigma, H11 * xi) ; + + // F_q + + this->_F_row.at(q) += (F11 * xi) * tmp1 ; + OSM::set_column(this->_F_row.at(q), sigma, F11 * (-xi)) ; + + // G_q+1 // note: G_q+1 is not be modified if the Hdvf is perfect + + Row_chain tmp2(projS_cod_sigma * this->_G_col.at(q+1)) ; + tmp2 += projC_cod_sigma ; + this->_G_col.at(q+1) -= (H11 * xi) * tmp2 ; + + // DD_col_q+1 / DD_row_q + + this->_DD_col.at(q+1) -= (F11 * xi) * tmp2 ; + + // H_q-1 + + Column_chain tmp3(this->_H_col.at(q-1) * projP_d_pi) ; + + this->_H_col.at(q-1) -= (tmp3 * xip) * H11q1 ; + OSM::set_row(this->_H_col.at(q-1), pi, xip * H11q1) ; + + // G_q + + this->_G_col.at(q) -= (tmp3 * xip) * G11 ; + OSM::set_row(this->_G_col.at(q), pi, xip * G11) ; + + // F_q-1 // note: F_q-1 is not be modified if the Hdvf is perfect + + Column_chain tmp4(this->_F_row.at(q-1) * projP_d_pi) ; + tmp4 += projC_d_pi ; + this->_F_row.at(q-1) -= (tmp4 * xip) * H11q1 ; + + // DD_col_q + + this->_DD_col.at(q) += (tmp4 * xip) * G11 ; + + // Update flags + this->_flag[q][pi] = SECONDARY; // Set the PSC_flag of gamma in dimension q to SECONDARY + this->_flag[q][sigma] = PRIMARY; // Set the PSC_flag of sigma in dimension q to PRIMARY + } + else + std::cout << "!!! MW impossible with partial reduction options" << std::endl; +} + +} /* end namespace Homological_discrete_vector_field */ +} /* end namespace CGAL */ + +#endif // CGAL_HDVF_HDVF_H diff --git a/HDVF/include/CGAL/HDVF/Hdvf_core.h b/HDVF/include/CGAL/HDVF/Hdvf_core.h new file mode 100644 index 00000000000..fa4722d0e7d --- /dev/null +++ b/HDVF/include/CGAL/HDVF/Hdvf_core.h @@ -0,0 +1,1296 @@ +// Copyright (c) 2025 LIS Marseille (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Alexandra Bac + +#ifndef CGAL_HDVF_HDVF_CORE_H +#define CGAL_HDVF_HDVF_CORE_H + +#include + +#include +#include +#include +#include +#include +#include + +namespace CGAL { +namespace Homological_discrete_vector_field { + +/** \brief HDVF Enum for the label of cells. */ +static enum PSC_flag { + PRIMARY, + SECONDARY, + CRITICAL, + NONE // For duality and persistence +}; + +/** \brief HDVF option (compute only reduced boundary). */ +const int OPT_BND = 0b0001; +/** \brief HDVF option (compute only reduced boundary and f). */ +const int OPT_F = 0b0010; +/** \brief HDVF option (compute only reduced boundary and g). */ +const int OPT_G = 0b0100; +/** \brief HDVF option (compute full reduction). */ +const int OPT_FULL = 0b1000; + + +/** \brief Structure to represent data for HDVF operations (pairs of cells). + * + * Cells are always sorted so that the dimension of `sigma` is lesser than the dimension of `tau`. + */ +struct Cell_pair { + /// Index of the first cell + size_t sigma; + /// Index of the second cell + size_t tau; + /// Dimension of cells: `dim`/`dim`+1 for A and R, `dim`/`dim` for other operations + int dim; +}; + +/** \brief Overload of operator<< for Cell_pair type. */ +inline std::ostream& operator<<(std::ostream &out, const std::vector& pairs) { + for (const auto& pair : pairs) { + out << "Sigma: " << pair.sigma << ", Tau: " << pair.tau << ", Dim: " << pair.dim << std::endl; + } + return out ; +} + +/*! + \ingroup PkgHDVFRef + + The class `Hdvf_core` is the core implementation of homological discrete vector fields (HDVF for short). The ring of coefficients for homology computation must be a model of `IntegralDomainWithoutDivision`. + + An enumeration `PSC_flag` is defined in the `Homological_discrete_vector_field` namespace and the `Hdvf_core` class maps each cell to one of the flags (namely `PRIMARY`, `SECONDARY`, `CRITICAL`). The NONE `PSC_flag` is used in child classes (such as `Hdvf_duality`) when computing relative homology on a sub-complex. + The `PSC_flag` of each cell is stored in an appropriate structure and getters are provided to access to this information. + + The `Hdvf_core` class stores the associated reduction in sparse matrices: row-major for \f$f\f$, and column-major for \f$g\f$, \f$h\f$ and \f$\partial'\f$. Getters are provided to access this information. However, according to the chosen HDVF computation option (`OPT_BND`, `OPT_F`, `OPT_G`, `OPT_FULL`) the reduction can be computed only partially (and thus faster). + + The class provides perfect HDVF construction operations: `compute_perfect_hdvf()` and `compute_rand_perfect_hdvf()`, which build perfect HDVFs by pairing iteratively critical cells through the `A()` operation. + + If the user wishes to build an HDVF using other criteria, several `find_pair_A()` functions are provided (searching for valid pairs of cells for `A`respecting various constraints). The `A` operation can be applied to any pair returned by these functions. + + Homology/cohomology generators are actually algebraic objects, namely chains. Methods `homology_chain()` and `cohomology_chain()` return the homology and cohomology generator chain associated to a given critical cell. VTK export functions output all the cells of such chains with non zero coefficients. + + + \cgalModels{HDVF} + + \tparam ChainComplex a model of the `AbstractChainComplex` concept, providing the type of abstract chain complex used. + \tparam ChainType a model of the `SparseChain` concept (by default, `OSM::Sparse_chain`), providing the type of sparse chains used (should be coherent with `SparseMatrixType`). + \tparam SparseMatrixType a model of the `SparseMatrix` concept (by default, `OSM::Sparse_matrix`), providing the type of sparse matrices used. + */ + + +template typename ChainType = OSM::Sparse_chain, template typename SparseMatrixType = OSM::Sparse_matrix> +class Hdvf_core { +public: + /*! \brief Type of coefficients used to compute homology. */ + typedef typename ChainComplex::Coefficient_ring Coefficient_ring; + + /*! + Type of column-major chains + */ + typedef ChainType Column_chain; + + /*! + Type of row-major chains + */ + typedef ChainType Row_chain; + + /*! + Type of column-major sparse matrices + */ + typedef SparseMatrixType Column_matrix; + + /*! + Type of row-major sparse matrices + */ + typedef SparseMatrixType Row_matrix; + +protected: + /* \brief Flags of the cells. + * _flag.at(q) contains the flags of cells of dimension q + */ + std::vector> _flag; + /* \brief Number of `PRIMARY` cells. */ + std::vector _nb_P; + /* \brief Number of `SECONDARY` cells. */ + std::vector _nb_S; + /* \brief Number of `CRITICAL` cells. */ + std::vector _nb_C; + /* \brief Row matrices for f. */ + std::vector _F_row; + /* \brief Column matrices for g. */ + std::vector _G_col; + /* \brief Column matrices for h. */ + std::vector _H_col; + /* \brief Column matrices for reduced boundary. */ + std::vector _DD_col; + + /* \brief Reference to the underlying complex. */ + const ChainComplex& _K; + + /* \brief Hdvf_core options for computation (computation of partial reduction). */ + int _hdvf_opt; + +public: + /** + * \brief Default constructor. + * + * Builds an "empty" HDVF_core associated to K (with all cells critical). By default, the HDVF option is set to OPT_FULL (full reduction computed). + * + * \param K A chain complex (a model of `AbstractChainComplex`) + * \param hdvf_opt Option for HDVF computation (`OPT_BND`, `OPT_F`, `OPT_G` or `OPT_FULL`) + */ + Hdvf_core(const ChainComplex& K, int hdvf_opt = OPT_FULL) ; + + /* + * \brief Constructor by copy. + * + * Builds a HDVF by copy from another, including options. + * + * \param hdvf An initial HDVF. + */ + Hdvf_core(const Hdvf_core& hdvf) : _flag(hdvf._flag), _nb_P(hdvf._nb_P), _nb_S(hdvf._nb_S), _nb_C(hdvf._nb_C), _F_row(hdvf._F_row), _G_col(hdvf._G_col), _H_col(hdvf._H_col), _DD_col(hdvf._DD_col), _K(hdvf._K), _hdvf_opt(hdvf._hdvf_opt) { } + + /* + * \brief HDVF_core destructor. */ + ~Hdvf_core() { } + + /** + * \brief Finds a valid Cell_pair of dimension q / q+1 for A. + * + * The function searches a pair of critical cells \f$(\gamma_1, \gamma2)\f$ of dimension q / q+1, valid for A (ie.\ such that \f$\langle \partial_{q+1}(\gamma_2), \gamma_1 \rangle\f$ invertible). It returns the first valid pair found by iterators. + * + * \param q Lower dimension of the pair. + * \param found Reference to a %Boolean variable. The method sets `found` to `true` if a valid pair is found, `false` otherwise. + */ + virtual Cell_pair find_pair_A(int q, bool &found) const; + + /** + * \brief Finds a valid Cell_pair for A containing `gamma` (a cell of dimension `q`) + * + * The function searches a cell \f$\gamma'\f$ such that one of the following conditions holds: + * - \f$\gamma'\f$ has dimension q+1 and \f$(\gamma, \gamma')\f$ is valid for A (ie.\ such that \f$\langle \partial_{q+1}(\gamma'), \gamma \rangle\f$ invertible), + * - \f$\gamma'\f$ has dimension q-1 and \f$(\gamma', \gamma)\f$ is valid for A (ie.\ such that \f$\langle \partial_{q}(\gamma), \gamma' \rangle\f$ invertible). + * + * \param q Dimension of the cell `gamma`. + * \param found Reference to a %Boolean variable. The method sets `found` to `true` if a valid pair is found, `false` otherwise. + * \param gamma Index of a cell to pair. + */ + virtual Cell_pair find_pair_A(int q, bool &found, size_t gamma) const; + + /** + * \brief Finds *all* valid Cell_pair of dimension q / q+1 for A. + * + * The function searches all pairs of critical cells \f$(\gamma_1, \gamma2)\f$ of dimension q / q+1, valid for A (ie.\ such that \f$\langle \partial_{q+1}(\gamma_2), \gamma_1 \rangle\f$ invertible). + * It returns a vector of such pairs. + * + * \param q Lower dimension of the pair. + * \param found Reference to a %Boolean variable. The method sets `found` to `true` if a valid pair is found, `false` otherwise. + */ + virtual std::vector find_pairs_A(int q, bool &found) const; + + /** + * \brief Finds *all* valid Cell_pair for A containing `gamma` (a cell of dimension `q`) + * + * The function searches all `CRITICAL` cells \f$\gamma'\f$ such that one of the following conditions holds: + * - \f$\gamma'\f$ has dimension q+1 and \f$(\gamma, \gamma')\f$ is valid for A (ie.\ such that \f$\langle \partial_{q+1}(\gamma'), \gamma \rangle\f$ invertible), + * - \f$\gamma'\f$ has dimension q-1 and \f$(\gamma', \gamma)\f$ is valid for A (ie.\ such that \f$\langle \partial_{q}(\gamma), \gamma' \rangle\f$ invertible). + * It returns a vector of such pairs. + * + * \param q Dimension of the cell `gamma`. + * \param found Reference to a %Boolean variable. The method sets `found` to `true` if a valid pair is found, `false` otherwise. + * \param gamma Index of a cell to pair. + */ + virtual std::vector find_pairs_A(int q, bool &found, size_t gamma) const; + + /** + * \brief A operation: pairs critical cells. + * + * A pair of critical cells \f$(\gamma_1, \gamma_2)\f$ of respective dimension q and q+1 is valid for A if \f$\langle \partial_{q+1}(\gamma_2), \gamma_1 \rangle\f$ is invertible. After the `A()` operation, \f$\gamma_1\f$ becomes `PRIMARY`, \f$\gamma_2\f$ becomes `SECONDARY`. The A method updates the reduction accordingly (in time \f$\mathcal O(n^2)\f$). + * + * \param gamma1 First cell of the pair (dimension `q`) + * \param gamma2 Second cell of the pair (dimension `q+1`) + * \param q Dimension of the pair + */ + void A(size_t gamma1, size_t gamma2, int q); + + /** + * \brief Computes a perfect HDVF. + * + * As long as valid pairs for A exist, the function selects the first available pair (returned by `find_pair_A`()) and applies the corresponding `A()` operation. + * If the `IntegralDomainWithoutDivision` of coefficients is a field, this operation always produces a perfect HDVF (ie.\ the reduced boundary is null and the reduction provides homology and cohomology information). + * Otherwise the operation produces a maximal HDVF with a residual boundary matrix over critical cells. + * + * If the HDVF is initially not trivial (some cells have already been paired), the function completes it into a perfect HDVF. + * + * \param verbose If this parameter is `true`, all intermediate reductions are printed out. + * + * \return The vector of all `Cell_pair` paired with A. + */ + std::vector compute_perfect_hdvf(bool verbose = false); + + /** + * \brief Computes a random perfect HDVF. + * + * As long as valid pairs for A exist, the function selects a random pair (among pairs returned by `find_pairs_A()`) and applies the corresponding `A()` operation. + * If the `IntegralDomainWithoutDivision` of coefficients is a field, this operation always produces a perfect HDVF (that is the reduced boundary is null and the reduction provides homology and cohomology information). + * + * If the HDVF is initially not trivial (some cells have already been paired), the function randomly completes it into a perfect HDVF. + * + * \warning This method is slower that `compute_perfect_hdvf()` (finding out all possible valid pairs requires additional time). + * + * \param verbose If this parameter is `true`, all intermediate reductions are printed out. + * + * \return The vector of all pairs of cells used for apply A. + */ + std::vector compute_rand_perfect_hdvf(bool verbose = false); + + /** + * \brief Tests if a HDVF is perfect. + * + * The function returns `true` is the reduced boundary matrix is null and `false` otherwise. + */ + bool is_perfect_hdvf() + { + bool res = true ; + int q = 0 ; + while ((q<=_K.dimension()) && res) + { + res = res && _DD_col.at(q).is_null() ; + ++q; + } + return res ; + } + + // Hdvf_core getters + + /** + * \brief Gets cells with a given `PSC_flag` in any dimension. + * + * The function returns a vector containing, for each dimension, the vector of cells with a given `PSC_flag`. + * + * \param flag PSC_flag to select. + */ + + // !!! Why should it be virtual for duality????? + + virtual std::vector > psc_flags (PSC_flag flag) const ; + + /** + * \brief Gets cells with a given `PSC_flag` in dimension `q`. + * + * The function returns the vector of cells of dimension `q` with a given `PSC_flag`. + * + * \param flag PSC_flag to select. + * \param q Dimension visited. + */ + virtual std::vector psc_flags (PSC_flag flag, int q) const ; + + /*! + * \brief Gets the PSC_flag of the cell `tau` in dimension `q`. + * + * \param tau Index of the cell. + * \param q Dimension of the cell. + */ + PSC_flag psc_flag (int q, size_t tau) const { return _flag.at(q).at(tau); } + + /** + * \brief Gets HDVF computation option. + */ + int hdvf_opts () const { return _hdvf_opt ; } + + /** + * \brief Gets the row-major matrix of \f$f\f$ (from the reduction associated to the HDVF). + */ + const Row_matrix& matrix_f (int q) const { return _F_row.at(q); } + + /** + * \brief Gets the column-major matrix of \f$g\f$ (from the reduction associated to the HDVF). + */ + const Column_matrix& matrix_g (int q) const { return _G_col.at(q); } + + /** + * \brief Gets the column-major matrix of \f$h\f$ (from the reduction associated to the HDVF). + */ + const Column_matrix& matrix_h (int q) const { return _H_col.at(q); } + + /** + * \brief Gets the column-major matrix of \f$\partial'\f$, reduced boundary operator (from the reduction associated to the HDVF). + */ + const Column_matrix& matrix_dd (int q) const { return _DD_col.at(q); } + + /** + * \brief Prints the matrices of the reduction. + * + * Writes the matrices of the reduction (that is \f$f\f$, \f$g\f$, \f$h\f$, \f$\partial'\f$ the reduced boundary). + * By default, writes the complex to `std::cout`. + */ + std::ostream& write_matrices(std::ostream &out = std::cout) const; + + /** + * \brief Writes the homology and cohomology reduction information. + * + * Writes \f$f^*\f$, \f$g\f$ \f$\partial'\f$ the reduced boundary over each critical cell. + * + * By default, writes the complex to `std::cout`. + */ + std::ostream& write_reduction(std::ostream &out = std::cout) const; + + + /** + * \brief Exports primary/secondary/critical labels (in particular for vtk export) + * + * The method exports the labels of every cells in each dimension. + * + * \return A vector containing, for each dimension, the vector of labels by cell index. + */ + virtual std::vector > psc_labels () const + { + std::vector > labels(_K.dimension()+1) ; + for (int q=0; q<=_K.dimension(); ++q) + { + for (size_t i = 0; i<_K.number_of_cells(q); ++i) + { + if (_flag.at(q).at(i) == PRIMARY) + labels.at(q).push_back(-1) ; + else if (_flag.at(q).at(i) == SECONDARY) + labels.at(q).push_back(1) ; + else if (_flag.at(q).at(i) == CRITICAL) + labels.at(q).push_back(0) ; + else // NONE + labels.at(q).push_back(2) ; + } + } + return labels ; + } + + /** + * \brief Gets homology generators associated to `cell` (critical cell) of dimension `q` (used by vtk export). + * + * The method exports the chain \f$g(\sigma)\f$ for \f$\sigma\f$ the cell of index `cell_index` and dimension `q`. + * + * \return A column-major chain. + */ + virtual Column_chain homology_chain (size_t cell_index, int q) const + { + if ((q<0) || (q>_K.dimension())) + throw "Error : homology_chain with dim out of range" ; + if (_hdvf_opt & (OPT_FULL | OPT_G)) + { + Column_chain g_cell(OSM::get_column(_G_col.at(q), cell_index)) ; + // Add 1 to the cell + g_cell.set_coefficient(cell_index, 1) ; + return g_cell ; + } + else + throw "Error : trying to export g_chain without proper Hdvf_core option" ; + } + + /** + * \brief Gets cohomology generators associated to `cell_index` (critical cell) of dimension `q` (used by vtk export). + * + * The method exports the chain \f$f^\star(\sigma)\f$ for \f$\sigma\f$ the cell of index `cell_index` and dimension `q`. + * + * \param cell_index Index of the (critical) cell. + * \param dim Dimension of the (critical) cell. + * + * \return A column-major chain. + */ + virtual Column_chain cohomology_chain (size_t cell_index, int dim) const + { + if ((dim<0) || (dim>_K.dimension())) + throw "Error : cohomology_chain with dim out of range" ; + if (_hdvf_opt & (OPT_FULL | OPT_F)) + { + Row_chain fstar_cell(OSM::get_row(_F_row.at(dim), cell_index)) ; + // Add 1 to the cell + fstar_cell.set_coefficient(cell_index, 1) ; + + return fstar_cell.transpose() ; + + } + else + throw "Error : trying to export fstar_chain without proper Hdvf_core option" ; + } + + /** + * \brief Writes a HDVF together with the associated reduction (f, g, h, d matrices) + * + * Writes a HDVF to a stream in `hdvf` file format (a simple text file format, see for a specification). + */ + std::ostream& write_hdvf_reduction(std::ostream& out) ; + + /** + * \brief Writes a HDVF together with the associated reduction to a file (f, g, h, d matrices). + * + * Writes a HDVF to a file in `hdvf` file format (a simple text file format, see for a specification). + */ + void write_hdvf_reduction(std::string filename) + { + std::ofstream out_file ( filename, std::ios::out | std::ios::trunc); + if ( ! out_file . good () ) { + std::cerr << "Out fatal Error:\n " << filename << " not found.\n"; + throw std::runtime_error("File Parsing Error: File not found"); + } + + write_hdvf_reduction(out_file) ; + + out_file.close(); + } + + /** + * \brief Loads a HDVF together with the associated reduction (f, g, h, d matrices) + * + * Load a HDVF and its reduction from a stream in `hdvf` file format, a simple text file format (see for a specification). + * \warning The underlying complex is not stored in the file! + */ + std::istream& read_hdvf_reduction(std::istream& in_stream) ; + + /** + * \brief Loads a HDVF together with the associated reduction from a file (f, g, h, d matrices) + * + * Load a HDVF and its reduction from a file in `hdvf` file format, a simple text file format (see for a specification). + * \warning The underlying complex is not stored in the file! + */ + void read_hdvf_reduction(std::string filename) + { + std::ifstream in_file (filename); + if ( ! in_file . good () ) { + std::cerr << "Out fatal Error:\n " << filename << " not found.\n"; + throw std::runtime_error("File Parsing Error: File not found"); + } + + read_hdvf_reduction(in_file); + + in_file.close(); + } + +/** \brief Compares the HDVF with another HDVF over the same underlying complex. + * + * \param other Other HDVF to compare. + * \param full_compare Turns on "in depth" HDVF comparison (reduction matrices). + */ + + bool compare(const Hdvf_core& other, bool full_compare = false) + { + bool res; + int d = _flag.size(), d_other = other._flag.size(); + res = (d == d_other); + if (!res) { + std::cerr <<"HDVF compare: dimensions differ" << std::endl; + return false; + } + // Compare flags in each dimension + for (int q=0; res && (q<_K.dimension()); ++q) + res = res && (_flag.at(q) == other._flag.at(q)) ; + if (!res) { + std::cerr <<"HDVF compare: flags differ" << std::endl; + return false; + } + + // Check if full_compare is required + if (!full_compare) + return res; + + // ----- Only for full_compare + // F + if (_hdvf_opt & (OPT_FULL | OPT_F)) { + for (int q=0; res && (q<_K.dimension()); ++q) + res = res && (_F_row.at(q) == other._F_row.at(q)); + if (!res) { + std::cerr <<"HDVF compare: F matrices differ" << std::endl; + return false; + } + } + // G + if (_hdvf_opt & (OPT_FULL | OPT_G)) { + for (int q=0; res && (q<_K.dimension()); ++q) + res = res && (_G_col.at(q) == other._G_col.at(q)); + if (!res) { + std::cerr <<"HDVF compare: G matrices differ" << std::endl; + return false; + } + } + // H + for (int q=0; res && (q<_K.dimension()); ++q) + res = res && (_H_col.at(q) == other._H_col.at(q)); + if (!res) { + std::cerr <<"HDVF compare: H matrices differ" << std::endl; + return false; + } + // DD + for (int q=0; res && (q<_K.dimension()); ++q) + res = res && (_DD_col.at(q) == other._DD_col.at(q)); + if (!res) { + std::cerr <<"HDVF compare: DD matrices differ" << std::endl; + return false; + } + return res; + } + + /** \brief Comparison operator. + * + * Compares the HDVF with `other` HDVF (fast comparison testing only HDVF flags). + */ + bool operator==(const Hdvf_core& other) + { + return compare(other, true); + } + +protected: + /* \brief Project a chain onto a given PSC_flag + * The methods cancels all the coefficients of the chain of dimension `q` that do not correspond to`PSC_flag`. This is actually an implementation of the projection operator onto the sub A-module generated by cells of PSC_flag `flag`. + * + * \param chain The chain projected. + * \param flag The PSC_flag onto which the chain is projected. + * \param q Dimension of the chain + * + * \result Returns a copy of `chain` where only coefficients of cells of PSC_flag `flag` are kept (all other coefficients are cancelled). + */ + template + ChainType projection(const ChainType& chain, PSC_flag flag, int q) const { + // Create a new chain to store the result + // Better to initialize 'result' directly with the correct size and iterate over it + ChainType result(chain); + + // Iterate over each element of the chain + std::vector tmp ; + for (typename ChainType::const_iterator it = result.cbegin(); it != result.cend(); ++it) + { + size_t cell_index = it->first; + Coefficient_ring value = it->second; + + // Check the PSC_flag of the corresponding cell + if (_flag[q][cell_index] != flag) { + // Mark for cancellation + tmp.push_back(cell_index) ; + + } + } + // Cancel all coefficients of tmp + result /= tmp ; + return result; + } + + /* \brief Display a text progress bar. */ + void progress_bar(size_t i, size_t n) + { + const size_t step(n/20) ; + if ((i%step)==0) + { + const float percentage(float(i)/(n-1)) ; + const char PBSTR[] = "||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||" ; + const size_t PBWIDTH(60) ; + size_t val = (size_t) (percentage * 100); + size_t lpad = (size_t) (percentage * PBWIDTH); + size_t rpad = PBWIDTH - lpad; + printf("\r%3d%% [%.*s%*s]", val, lpad, PBSTR, rpad, ""); + fflush(stdout); + } + if (i==(n-1)) + std::cout << std::endl ; + } + +}; + +// Constructor for the Hdvf_core class +template typename ChainType, template typename SparseMatrixType> +Hdvf_core::Hdvf_core(const ChainComplex& K, int hdvf_opt) : _K(K) { + // Get the dimension of the simplicial complex + int dim = _K.dimension(); + std::cout << "----> Starting Hdvf_core creation / dim " << dim << std::endl ; + // Hdvf_core options + _hdvf_opt = hdvf_opt ; + + // Resize the _DD_col vector to hold dim+1 elements + _DD_col.resize(dim + 1); + + // Resize the _F_row vector to hold dim+1 elements + if (_hdvf_opt & (OPT_FULL | OPT_F)) + _F_row.resize(dim + 1); + + // Resize the _G_col vector to hold dim+1 elements + if (_hdvf_opt & (OPT_FULL | OPT_G)) + _G_col.resize(dim + 1); + + // Resize the _H_col vector to hold dim+1 elements + if (_hdvf_opt & OPT_FULL) + _H_col.resize(dim + 1); + + // Resize _flag and count vectors to hold dim+1 elements + _flag.resize(dim + 1); + _nb_P.resize(dim + 1); + _nb_S.resize(dim + 1); + _nb_C.resize(dim + 1); + + // Initialize matrices and counters + + for (int q = 0; q <= dim; q++) { + // Initialize _F_row[q] as a row matrix with dimensions (dim(q) x dim(q)) + if (_hdvf_opt & (OPT_FULL | OPT_F)) + _F_row[q] = Row_matrix(_K.number_of_cells(q), _K.number_of_cells(q)); + + // Initialize _G_col[q] as a column matrix with dimensions (dim(q) x dim(q)) + if (_hdvf_opt & (OPT_FULL | OPT_G)) + _G_col[q] = Column_matrix(_K.number_of_cells(q), _K.number_of_cells(q)); + + // Initialize _H_col[q] as a column matrix with dimensions (dim(q+1) x dim(q)) + if (_hdvf_opt & OPT_FULL) + _H_col[q] = Column_matrix(_K.number_of_cells(q + 1), _K.number_of_cells(q)); + + // Initialize the counters for PRIMARY, SECONDARY, and CRITICAL cells + _nb_P[q] = 0; + _nb_S[q] = 0; + _nb_C[q] = _K.number_of_cells(q); + } + + // Initialize the flags for each dimension to CRITICAL + for (int q = 0; q <= dim; q++) { + _flag[q] = std::vector(_K.number_of_cells(q), CRITICAL); + } + + // Populate the DD matrices + _DD_col.resize(_K.dimension()+1) ; + for (int q=0; q<=_K.dimension(); ++q) + _DD_col.at(q) = _K.boundary_matrix(q) ; + std::cout << "------> End Hdvf_core creation" << std::endl ; +} + +// Method to print the matrices +template typename ChainType, template typename SparseMatrixType> +std::ostream& Hdvf_core::write_matrices(std::ostream& out) const { + // Iterate through each dimension and print the corresponding matrices + for (int q = 0; q <= _K.dimension(); ++q) { + out << "------- Dimension " << q << std::endl; + + out << "Matrices _DD_col:" << std::endl; + out << _DD_col[q] << std::endl; + + if (_hdvf_opt & (OPT_FULL | OPT_F)) + { + out << "Matrices _F_row:" << std::endl; + out << _F_row[q] << std::endl; + } + + if (_hdvf_opt & (OPT_FULL | OPT_G)) + { + out << "Matrices _G_col:" << std::endl; + out << _G_col[q] << std::endl; + } + + if (_hdvf_opt & OPT_FULL) + { + out << "Matrices _H_col:" << std::endl; + out << _H_col[q] << std::endl; + } + } + return out ; +} + + +// Methods to find a pair of cells for A in dimension q +// -> = +- 1 with cell1, cell2 critical +// First version: returns a pair of dimensions q / q+1 +// Second version: returns all the pairs containing sigma + +// find a valid Cell_pair for A in dimension q +template typename ChainType, template typename SparseMatrixType> +Cell_pair Hdvf_core::find_pair_A(int q, bool &found) const +{ + found = false; + Cell_pair p; + + // Iterate through columns of _DD_col[q+1] + for (OSM::Bitboard::iterator it_col = _DD_col[q+1].begin(); (it_col != _DD_col[q+1].end() && !found); ++it_col) + { + const Column_chain& col(OSM::cget_column(_DD_col[q+1], *it_col)) ; + + // Iterate through the entries of the column + for (typename Column_chain::const_iterator it = col.begin(); (it != col.end() && !found); ++it) { + if ((it->second == 1) || (it->second == -1)) { + // If an entry with coefficient 1 or -1 is found, set the pair and mark as found + p.sigma = it->first; + p.tau = *it_col; + p.dim = q; + found = true; + } + } + } + return p; +} + +// find a valid Cell_pair containing tau for A in dimension q +template typename ChainType, template typename SparseMatrixType> +Cell_pair Hdvf_core::find_pair_A(int q, bool &found, size_t gamma) const +{ + found = false; + Cell_pair p ; + + // Search for a q-1 cell tau' such that <_d(tau),tau'> invertible + const Column_chain& tmp2(OSM::cget_column(_DD_col.at(q), gamma)) ; + for (typename Column_chain::const_iterator it = tmp2.cbegin(); (it != tmp2.cend() && !found); ++it) + { +// if (abs(it->second) == 1) + if ((it->second == 1) || (it->second == -1)) + { + found = true ; + p.sigma = it->first ; + p.tau = gamma ; + p.dim = q-1 ; + } + } + + // Search for a q+1 cell tau' such that <_d(tau'),tau> invertible, ie <_cod(tau),tau'> invertible + Row_chain tmp(OSM::get_row(_DD_col.at(q+1), gamma)) ; + for (typename Row_chain::const_iterator it = tmp.cbegin(); (it != tmp.cend() && !found); ++it) + { +// if (abs(it->second) == 1) + if ((it->second == 1) || (it->second == -1)) + { + found = true ; + p.sigma = gamma ; + p.tau = it->first ; + p.dim = q ; + } + } + return p; +} + +// find all the valid PairCells for A in dimension q +template typename ChainType, template typename SparseMatrixType> +std::vector Hdvf_core::find_pairs_A(int q, bool &found) const +{ + std::vector pairs; + found = false ; + + // Iterate through columns of _DD_col[q+1] + for (OSM::Bitboard::iterator it_col = this->_DD_col[q+1].begin(); it_col != this->_DD_col[q+1].end(); ++it_col) + { + const Column_chain& col(OSM::cget_column(this->_DD_col[q+1], *it_col)) ; + + // Iterate through the entries of the column + for (typename Column_chain::const_iterator it = col.begin(); it != col.end(); ++it) { + if ((it->second == 1) || (it->second == -1)) { + // If an entry with coefficient 1 or -1 is found, set the pair and mark as found + Cell_pair p; + p.sigma = it->first; + p.tau = *it_col; + p.dim = q; + pairs.push_back(p) ; + found = true; + } + } + } + return pairs; +} + +// find all the valid Cell_pair containing gamma for A in dimension q +template typename ChainType, template typename SparseMatrixType> +std::vector Hdvf_core::find_pairs_A(int q, bool &found, size_t gamma) const +{ + found = false; + std::vector pairs; + + // Search for a q+1 cell tau' such that <_d(tau'),tau> invertible, ie <_cod(tau),tau'> invertible + Row_chain tmp(OSM::get_row(_DD_col.at(q+1), gamma)) ; + for (typename Row_chain::const_iterator it = tmp.cbegin(); it != tmp.cend(); ++it) + { +// if (abs(it->second) == 1) + if ((it->second == 1) || (it->second == -1)) + { + found = true ; + Cell_pair p ; + p.sigma = gamma ; + p.tau = it->first ; + p.dim = q ; + pairs.push_back(p) ; + } + } + // Search for a q-1 cell tau' such that <_d(tau),tau'> invertible + const Column_chain& tmp2(OSM::cget_column(_DD_col.at(q), gamma)) ; + for (typename Column_chain::const_iterator it = tmp2.cbegin(); it != tmp2.cend(); ++it) + { +// if (abs(it->second) == 1) + if ((it->second == 1) || (it->second == -1)) + { + found = true ; + Cell_pair p ; + p.sigma = it->first ; + p.tau = gamma ; + p.dim = q-1 ; + pairs.push_back(p) ; + } + } + return pairs; +} + + + + +// Method to perform operation A +// tau1 is in dimension q, tau2 is in dimension q+1 +template typename ChainType, template typename SparseMatrixType> +void Hdvf_core::A(size_t tau1, size_t tau2, int q) { + //----------------------------------------------- Submatrices of D ---------------------------------------------------- + + // Output operation details to the console + // cout << "A of " << tau1 << "(dim " << q << ") / " << tau2 << "(dim " << q + 1 << ")" << endl; + + // Extract submatrices from _DD_col + Row_chain D12(OSM::get_row(_DD_col.at(q+1),tau1)); // D12 is a row chain from _DD_col[q+1] at index tau1 + Column_chain D21(OSM::get_column(_DD_col.at(q + 1),tau2)); // D21 is a column chain from _DD_col[q+1] at index tau2 + const Coefficient_ring D11 = D12.get_coefficient(tau2); // D11 is the coefficient at the intersection of tau2 in D12 + + // Assert that D11 is either 1 or -1 (check invertibility) + assert((D11 == 1) || (D11 == -1)); // !!!!! Test invertibility + + // Compute the inverse of D11 (which is itself, since D11 is 1 or -1) + Coefficient_ring D11_inv = D11; + + // Perform operations to remove the row and column contributions + D12 /= std::vector({tau2}); // Remove tau2 column from D12 + D21 /= std::vector({tau1}); // Remove tau1 row from D21 + + // Delete rows and columns from _DD_col + remove_row(_DD_col[q + 1], tau1); // Remove row tau1 from _DD_col[q+1] + remove_column(_DD_col[q + 1], tau2); // Remove column tau2 from _DD_col[q+1] + + //---------------------------------------------- Submatrices of F ----------------------------------------------------- + + Row_chain F11 ; + Column_chain G11 ; + if (_hdvf_opt & (OPT_FULL | OPT_F)) + { + // Extract the relevant submatrix from _F_row + F11 = OSM::get_row(_F_row.at(q),tau1); // F11 is a row chain from _F_row[q] at index tau1 + + // Delete the row tau1 from _F_row + remove_row(_F_row[q], tau1); + } + + //--------------------------------------------- Submatrices of G ------------------------------------------------------ + + if (_hdvf_opt & (OPT_FULL | OPT_G)) + { + // Extract the relevant submatrix from _G_col + G11 = OSM::get_column(_G_col.at(q + 1),tau2); // G11 is a column chain from _G_col[q+1] at index tau2 + + // Delete the column tau2 from _G_col + remove_column(_G_col[q + 1], tau2); + } + + //--------------------------------------------- Update matrices ------------------------------------------------------- + + // ---- Update _F_row + + if (_hdvf_opt & (OPT_FULL | OPT_F)) + { + // Update _F_row[q] + // Subtract the product of (D21 * D11_inv) and F11 from _F_row[q] + // Note: % operator returns a row matrix, so be careful with operations + _F_row[q] -= (D21 * D11_inv) % F11; + + // Set the column tau1 of _F_row[q] to (D21 * (-D11_inv)) + OSM::set_column(_F_row[q], tau1, D21 * (-D11_inv)); + + // Remove the row tau2 from _F_row[q+1] + remove_row(_F_row[q + 1], tau2); + } + + // ---- Update _G_col + + if (_hdvf_opt & (OPT_FULL | OPT_G)) + { + // Update _G_col[q + 1] + // Subtract the product of (G11 * D11_inv) and D12 from _G_col[q+1] + _G_col[q + 1] -= (G11 * D11_inv) * D12; + + // Set the row tau2 of _G_col[q + 1] to (D12 * (-D11_inv)) + OSM::set_row(_G_col[q + 1], tau2, D12 * (-D11_inv)); + + // Remove the column tau1 from _G_col[q] + remove_column(_G_col[q], tau1); + } + + // ---- Update _H_col + + if (_hdvf_opt & OPT_FULL) + { + // Update _H_col[q] + // Compute the temporary matrix product G11 * F11 + Column_matrix tmp = G11 * F11; + + // Add the product of tmp and D11_inv to _H_col[q] + _H_col[q] += (tmp) * D11_inv; + + // Set the row tau2 of _H_col[q] to (F11 * D11_inv) + OSM::set_row(_H_col[q], tau2, F11 * D11_inv); + + // Set the column tau1 of _H_col[q] to (G11 * D11_inv) + OSM::set_column(_H_col[q], tau1, G11 * D11_inv); + + // Set the coefficient at (tau2, tau1) in _H_col[q] to D11_inv + set_coefficient(_H_col[q], tau2, tau1, D11_inv); + } + + // ---- Update _DD_col + + // Update _DD_col + _DD_col[q + 1] -= (D21 * D12) * D11_inv; + + // Remove columns and rows from _DD_col as necessary + if (q > 0) { + remove_column(_DD_col[q], tau1); // Remove column tau1 from _DD_col[q] + } + if (q + 2 <= _K.dimension()) { + remove_row(_DD_col[q + 2], tau2); // Remove row tau2 from _DD_col[q+2] + } + + // Update flags + _flag[q][tau1] = PRIMARY; // Set the PSC_flag of tau1 in dimension q to PRIMARY + --_nb_C.at(q) ; + ++_nb_P.at(q) ; + _flag[q + 1][tau2] = SECONDARY; // Set the PSC_flag of tau2 in dimension q+1 to SECONDARY + --_nb_C.at(q+1) ; + ++_nb_S.at(q+1) ; + + // ----------------------------------------------------------------------------------------------------------------------- +} + + +// Method to compute a perfect Hdvf_core +template typename ChainType, template typename SparseMatrixType> +std::vector Hdvf_core::compute_perfect_hdvf(bool verbose) { + std::vector pair_list; // Vector to store the list of pairs + bool trouve = false; // Flag to indicate whether a pair was found + int dim = _K.dimension(); // Get the dimension of the complex K + + // Loop through dimensions from q-1 to 0 + for (int q = dim - 1; q >= 0; --q) { + std::cout << std::endl << "-> pairing cells of dimension " << q << " and " << q+1 << std::endl ; + + // Find a pair of cells in dimension q + Cell_pair pair = find_pair_A(q, trouve); + + // While a pair is found + while (trouve) { + progress_bar(_K.number_of_cells(q)-_nb_C.at(q), _K.number_of_cells(q)) ; + // Add the found pair to the list + pair_list.push_back(pair); + + // Perform operation A with the found pair + A(pair.sigma, pair.tau, q); + if (verbose) + { + std::cout << "A : " << pair.sigma << " - " << pair.tau << " (dim " << pair.dim << ")" << std::endl ; + write_matrices(std::cout) ; + } + + // Find another pair of cells in dimension q + pair = find_pair_A(q, trouve); + } + } + + // Return the list of pairs found + return pair_list; +} + +// Method to compute a random perfect Hdvf_core +// Returns a vector of Cell_pair objects representing the pairs found +template typename ChainType, template typename SparseMatrixType> +std::vector Hdvf_core::compute_rand_perfect_hdvf(bool verbose) { + std::vector pair_list; // Vector to store the list of pairs + bool trouve = false; // Flag to indicate whether a pair was found + int dim = _K.dimension(); // Get the dimension of the complex K + + // Init random generator + std::random_device dev; + std::mt19937 rng(dev()); // Random + + // Loop through dimensions from q-1 to 0 + for (int q = dim - 1; q >= 0; --q) { + std::cout << "-> pairing cells of dimension " << q << " and " << q+1 << std::endl ; + // Incorrect: the number of cells is the number of cols in _DD_col ... (duality) + + std::vector pairs = find_pairs_A(q, trouve); + + Cell_pair pair ; + + // While a pair is found + while (trouve) + { + // Add one of the pairs (randomly) to the list + { + // Pickup a random cell sigma + std::uniform_int_distribution rand_dist(0,pairs.size()-1); + size_t i(rand_dist(rng)) ; + pair = pairs.at(i) ; + } + pair_list.push_back(pair); + + // Perform operation A with the chosen pair + A(pair.sigma, pair.tau, pair.dim); + if (verbose) + { + std::cout << "A : " << pair.sigma << " - " << pair.tau << " (dim " << pair.dim << ")" << std::endl ; + write_matrices(std::cout) ; + } + + // Compute possible pairings + pairs = find_pairs_A(q, trouve); + } + } + // Return the list of pairs found + return pair_list; +} + +// Method to get cells if with a given psc_flags (P,S,C) for each dimension +template typename ChainType, template typename SparseMatrixType> +std::vector > Hdvf_core::psc_flags (PSC_flag flag) const +{ + std::vector > res(_K.dimension()+1) ; + for (int q=0; q<=_K.dimension(); ++q) + { + for (size_t i=0; i<_K.number_of_cells(q); ++i) + { + if (_flag.at(q).at(i) == flag) + res.at(q).push_back(i) ; + } + } + return res ; +} + +// Method to get cells with a given PSC_flag (P,S,C) for a given dimension +template typename ChainType, template typename SparseMatrixType> +std::vector Hdvf_core::psc_flags (PSC_flag flag, int q) const +{ + std::vector res ; + for (size_t i=0; i<_K.number_of_cells(q); ++i) + { + if (_flag.at(q).at(i) == flag) + res.push_back(i) ; + } + return res ; +} + +// Method to print the current state of the reduction +template typename ChainType, template typename SparseMatrixType> +std::ostream& Hdvf_core::write_reduction(std::ostream& out) const +{ + // Print PSC + out << "----- flags of cells:" << std::endl; + for (int q = 0; q <= _K.dimension(); ++q) { + out << "--- dim " << q << std::endl; + for (size_t i = 0; i < _K.number_of_cells(q); ++i) + { + const int flag(_flag.at(q).at(i)) ; + if (flag == PRIMARY) + out << i << " -> P" << std::endl ; + else if (flag == SECONDARY) + out << i << " -> S" << std::endl ; + else + out << i << " -> C" << std::endl ; + } + out << std::endl; + } + + // Print critical cells + out << "----- critical cells:" << std::endl; + std::vector > critical(psc_flags(CRITICAL)) ; + for (int q = 0; q <= _K.dimension(); ++q) { + out << "--- dim " << q << std::endl; + for (size_t i = 0; i < critical.at(q).size(); ++i) + { + out << critical.at(q).at(i) << " "; + } + out << std::endl; + } + + if (_hdvf_opt & (OPT_FULL | OPT_G)) + { + // Print matrices g + out << "----- g:" << std::endl; + for (int q = 0; q <= _K.dimension(); ++q) { + out << "--- dim " << q << std::endl; + for (size_t i = 0; i < critical.at(q).size(); ++i) + { + const size_t id(critical.at(q).at(i)) ; + out << "g(" << id << ") = (" << id << ")"; + // Iterate over the ith column of _G_col + Column_chain col(OSM::get_column(_G_col.at(q), id)) ; // TODO cget + for (typename Column_chain::const_iterator it_col = col.cbegin(); it_col != col.cend(); ++it_col) { + out << " + " << it_col->second << ".(" << it_col->first << ") + "; + } + out << std::endl; + } + } + } + + if (_hdvf_opt & (OPT_FULL | OPT_F)) + { + // Print matrices f* + out << "----- f*:" << std::endl; + for (int q = 0; q <= _K.dimension(); ++q) { + out << "--- dim " << q << std::endl; + for (size_t i = 0; i < critical.at(q).size(); ++i) + { + const size_t id(critical.at(q).at(i)) ; + out << "f*(" << id << ") = (" << id << ")"; + // Iterate over the ith row of _F_row + Row_chain row(OSM::get_row(_F_row.at(q), id)) ; // TODO cget + for (typename Row_chain::const_iterator it_row = row.cbegin(); it_row != row.cend(); ++it_row) { + out << " + " << it_row->second << ".(" << it_row->first << ") + "; + } + out << std::endl; + } + } + } + return out ; +} + +// Save HDVF and reduction +template typename ChainType, template typename SparseMatrixType> +std::ostream& Hdvf_core::write_hdvf_reduction(std::ostream& out) +{ + // HDVF save type + // 0: HDVF and reduction + // 1: HDVF only + out << 0 << std::endl ; + // Dimension + out << _K.dimension() << std::endl ; + // Number of cells in each dimension + for (int q=0; q<=_K.dimension(); ++q) + out << _K.number_of_cells(q) << " " ; + out << std::endl ; + // HDVF option + // + out << _hdvf_opt << std::endl; + // Flags + // P : -1 / S : 1 / C : 0 + // Each dimension written on a row + for (int q=0; q<=_K.dimension(); ++q) + { + for (int i=0; i<_K.number_of_cells(q); ++i) + { + if (_flag.at(q).at(i) == PRIMARY) + out << -1 << " " ; + else if (_flag.at(q).at(i) == SECONDARY) + out << 1 << " " ; + else // CRITICAL + out << 0 << " " ; + } + out << std::endl ; + } + // F + if (_hdvf_opt & (OPT_FULL | OPT_F)) { + for (int q=0; q<=_K.dimension(); ++q) + OSM::write_matrix(_F_row.at(q), out) ; + } + // G + if (_hdvf_opt & (OPT_FULL | OPT_G)) { + for (int q=0; q<=_K.dimension(); ++q) + OSM::write_matrix(_G_col.at(q), out) ; + } + // H + for (int q=0; q<=_K.dimension(); ++q) + OSM::write_matrix(_H_col.at(q), out) ; + // DD + for (int q=0; q<=_K.dimension(); ++q) + OSM::write_matrix(_DD_col.at(q), out) ; + return out; +} + +// Save HDVF and reduction +template typename ChainType, template typename SparseMatrixType> +std::istream& Hdvf_core::read_hdvf_reduction(std::istream& in_stream) +{ + // Load and check HDVF save type + int type ; + in_stream >> type ; + if (type != 0) + { + std::cerr << "read_hdvf_reduction error: trying to load a pure HDVF file..." << std::endl ; + throw ("read_hdvf_reduction error: trying to load a pure HDVF file..."); + } + + // Load and check dimension + int d ; + in_stream >> d ; + if (d != _K.dimension()) + { + std::cerr << "read_hdvf_reduction error: dimension loaded incompatible with the dimension of the underlying complex" << std::endl ; + throw ("read_hdvf_reduction error: dimension loaded incompatible with the dimension of the underlying complex"); + } + // Load and check number of cells + int nb ; + for (int q=0; q<=_K.dimension(); ++q) + { + in_stream >> nb ; + if (nb != _K.number_of_cells(q)) + { + std::string mess("read_hdvf_reduction error: incoherent number of cells in dimension "); + mess += std::to_string(q); + std::cerr << mess << std::endl ; + throw (mess); + } + } + // Load HDVF opt + in_stream >> _hdvf_opt ; + // Load flags + int flag ; + for (int q=0; q<=_K.dimension(); ++q) + { + for (int i=0; i<_K.number_of_cells(q); ++i) + { + in_stream >> flag ; + if (flag == -1) { + _flag.at(q).at(i) = PRIMARY ; + ++_nb_P.at(q); + } + else if (flag == 1) { + _flag.at(q).at(i) = SECONDARY ; + ++_nb_S.at(q); + } + else { + _flag.at(q).at(i) = CRITICAL ; + ++_nb_C.at(q); + } + } + } + // Load reduction matrices + // F + if (_hdvf_opt & (OPT_FULL | OPT_F)) { + for (int q=0; q<=_K.dimension(); ++q) + OSM::read_matrix(_F_row.at(q), in_stream) ; + } + // G + if (_hdvf_opt & (OPT_FULL | OPT_G)) { + for (int q=0; q<=_K.dimension(); ++q) + OSM::read_matrix(_G_col.at(q), in_stream) ; + } + // H + for (int q=0; q<=_K.dimension(); ++q) + { + OSM::read_matrix(_H_col.at(q), in_stream) ; + } + // DD + for (int q=0; q<=_K.dimension(); ++q) + { + OSM::read_matrix(_DD_col.at(q), in_stream) ; + } + return in_stream ; +} + +} /* end namespace Homological_discrete_vector_field */ +} /* end namespace CGAL */ + +#endif // CGAL_HDVF_HDVF_CORE_H diff --git a/HDVF/include/CGAL/HDVF/Hdvf_duality.h b/HDVF/include/CGAL/HDVF/Hdvf_duality.h new file mode 100644 index 00000000000..5e813125991 --- /dev/null +++ b/HDVF/include/CGAL/HDVF/Hdvf_duality.h @@ -0,0 +1,768 @@ +// Copyright (c) 2025 LIS Marseille (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Alexandra Bac + +#ifndef CGAL_HDVF_HDVF_DUALITY_H +#define CGAL_HDVF_HDVF_DUALITY_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace CGAL { +namespace Homological_discrete_vector_field { + +/*! + \ingroup PkgHDVFRef + + The class `Hdvf_duality` is the implementation of homological discrete vector fields (HDVF for short) for Alexander duality computation. + + \warning The ring of coefficients provided should be a **field**. + + In dimension \f$n\f$, given a complex \f$L\f$ homeomorphic to \f$\mathcal S^n\f$ and a sub-complex \f$K\subseteq L\f$, Alexander duality states that for all \f$q\leqslant n\f$: + \f\[\tilde H_q(K) \simeq \tilde H^{n-q-1}(L-K)\f\] + where \f$\tilde H_q\f$ and \f$\tilde H^q\f$ denote reduced homology and cohomology groups. + + In [Gonzalez and al. 2025], the authors prove that, even if \f$L-K\f$ is not a sub-complex, it produces a valid chain complex (we call "co-complex" such a complementary of sub-complex). Hence, its homology/cohomology can be computed and for all \f$q\leqslant n\f$: + \f\[\tilde H_q(K) \simeq \tilde H^{q+1}(L-K)\f\] + + HDVFs provide a fast and convenient mean to compute this isomorphism. In order to work with convenient finite complexes, the complex \f$L\f$ must be homeomorphic to a ball of dimension \f$n\f$ (thus \f$\mathcal S^n\f$ is actually homeomorphic to \f$L\f$ plus an infinite \f$n\f$-cell closing its boundary). + + Perfect HDVFs are first computed over \f$K\f$ and \f$L-K\f$ (providing corresponding relative homology) respectively and Alexander isomorphism gives rise to a pairing between critical cells in \f$K\f$ and \f$L-K\f$, that is a pairing between homology/cohomology generators in \f$K\f$ and \f$L-K\f$. + + The class provides HDVF constuction operations: `compute_perfect_hdvf()` and `compute_rand_perfect_hdvf()`, which build perfect HDVFs over \f$K\f$ and \f$L-K\f$ respectively. + Then, `compute_alexander_pairing()` computes Alexander isomorphism (and provides a pairing between homology/cohomology generators in \f$K\f$ and \f$L-K\f$). + + + + + + Example of Alexander duality isomorphism. The twirl mesh is a subcomplex `K` of a larger complex `L` depicted in yellow, homeomorphic to the ball of dimension 3 (right - sectional view). + + \cgalFigureBegin{Duality_quartet,HDVF_twirl_quartet.png} + Example of "homological quartet for the twirl model". 1: Homology generators of the twirl \f$H_1(K)\f$, 2: Cohomology generators of the twirl \f$H^1(K)\f$, 3: Homology generators of the complementary of the twirl \f$H_1(L-K)\f$, 4: Cohomology generators of the complementary of the twirl \f$H^1(L-K)\f$. Alexander isomorphism is represented through colours (paired generators have similar colours). + \cgalFigureEnd + + Hence, each hole in \f$K\f$ gives rise to four generators (called its "homological quarted": its homology and cohomology generators in \f$K_q\f$ and the homology and cohomology generators paired with them in \f$(L-K)_{q+1}\f$). + + In order to compute relative homology, a sub chain complex mask is used to partially screen the complex `L` and thus restrict HDVF computation. This mask is called "current mask" (and can be set over `K` or `L-K`). + + \cgalModels{HDVF} + + \tparam ChainComplex a model of the `AbstractChainComplex` concept, providing the type of abstract chain complex used. + + [Gonzalez and al. 2025] Gonzalez-Lorenzo, A., Bac, A. & Gazull, YS. A constructive approach of Alexander duality. J Appl. and Comput. Topology 9, 2 (2025). + */ + +template +class Hdvf_duality : public Hdvf_core { +public: + /*! \brief Chain complex type. */ + typedef ChainComplex Chain_complex; + + /*! \brief Type of coefficients used to compute homology. */ + typedef typename Chain_complex::Coefficient_ring Coefficient_ring; + + /*! + Type of parent Hdvf_core class. + */ + typedef Hdvf_core Base ; + + // Inherited types + using Column_chain = Base::Column_chain; + using Row_chain = Base::Row_chain; + +private: + // Complex L + const ChainComplex& _L ; + const int _hdvf_opt ; + // Subcomplex K + // _KCC is the Sub_chain_complex_mask describing the subcomplex K + // _subCC is the Sub_chain_complex_mask describing the current subcomplex (K, L-K or remaining critical cells for pairing) + Sub_chain_complex_mask _KCC, _subCC ; + + // Critical cells of perfect HDVFs (over K / L-K respectively) + std::vector > _critical_K, _critical_L_K ; + +public: + /** + * \brief Hdvf_duality constructor ( from a complex `L` and a sub-complex `K`) + * + * `L` is a complex of a given dimension \f$n\f$ homeomorphic to \f$\mathcal B^n\f$ and `K` is a sub-complex of `L` described by a bitboard (cells of `K` have a bit set to 1, cells of `K` have a bit set to 0). + * + * Initially, the sub chain complex mask is set to `K`. + * + * \param L A complex of a given dimension \f$n\f$ homeomorphic to \f$\mathcal B^n\f$. + * \param K A sub complex of `L` encoded through a bitboard. + * \param hdvf_opt Option for HDVF computation (`OPT_BND`, `OPT_F`, `OPT_G` or `OPT_FULL`). + */ + Hdvf_duality(const Chain_complex& L, Sub_chain_complex_mask& K, int hdvf_opt = OPT_FULL) ; + + /** + * \brief Finds a valid cell pair of dimension q / q+1 for A *in the current sub chain complex*. + * + * The function searches a pair of critical cells, *in the current sub chain complex*, \f$(\gamma_1, \gamma2)\f$ of dimension q / q+1, valid for A (ie.\ such that \f$\langle \partial_{q+1}(\gamma_2), \gamma_1 \rangle\f$ invertible). It returns the first valid pair found by iterators. + * + * \param q Lower dimension of the pair. + * \param found Reference to a %Boolean variable. The method sets `found` to `true` if a valid pair is found, `false` otherwise. + */ + Cell_pair find_pair_A(int q, bool &found) const; + + /** + * \brief Finds a valid cell pair for A containing `gamma` *in the current sub chain complex* (a cell of dimension `q`) + * + * The function searches a cell \f$\gamma'\f$ *in the current sub chain complex* such that one of the following conditions holds: + * - \f$\gamma'\f$ has dimension q+1 and \f$(\gamma, \gamma')\f$ is valid for A (ie.\ such that \f$\langle \partial_{q+1}(\gamma'), \gamma \rangle\f$ invertible), + * - \f$\gamma'\f$ has dimension q-1 and \f$(\gamma', \gamma)\f$ is valid for A (ie.\ such that \f$\langle \partial_{q}(\gamma), \gamma' \rangle\f$ invertible). + * + * \param q Dimension of the cell `gamma`. + * \param found Reference to a %Boolean variable. The method sets `found` to `true` if a valid pair is found, `false` otherwise. + * \param gamma Index of a cell to pair. + */ + Cell_pair find_pair_A(int q, bool &found, size_t gamma) const; + + /** + * \brief Finds *all* valid cell pairs of dimension q / q+1 *in the current sub chain complex* for A. + * + * The function searches all pairs of critical cells \f$(\gamma_1, \gamma2)\f$ *in the current sub chain complex* of dimension q / q+1, valid for A (ie.\ such that \f$\langle \partial_{q+1}(\gamma_2), \gamma_1 \rangle\f$ invertible). + * It returns a vector of such pairs. + * + * \param q Lower dimension of the pairs. + * \param found Reference to a %Boolean variable. The method sets `found` to `true` if a valid pair is found, `false` otherwise. + */ + std::vector find_pairs_A(int q, bool &found) const; + + /** + * \brief Finds *all* valid cell pairs for `A`containing `gamma` *in the current sub chain complex* (a cell of dimension `q`) + * + * The function searches all `CRITICAL` cells \f$\gamma'\f$ *in the current sub chain complex* such that one of the following conditions holds: + * - \f$\gamma'\f$ has dimension q+1 and \f$(\gamma, \gamma')\f$ is valid for A (ie.\ such that \f$\langle \partial_{q+1}(\gamma'), \gamma \rangle\f$ invertible), + * - \f$\gamma'\f$ has dimension q-1 and \f$(\gamma', \gamma)\f$ is valid for A (ie.\ such that \f$\langle \partial_{q}(\gamma), \gamma' \rangle\f$ invertible). + * It returns a vector of such pairs. + * + * \param q Dimension of the cell `gamma`. + * \param found Reference to a %Boolean variable. The method sets `found` to `true` if a valid pair is found, `false` otherwise. + * \param gamma Index of a cell to pair. + */ + std::vector find_pairs_A(int q, bool &found, size_t gamma) const; + + /** \brief Sets the current sub chain complex masks over `K`. + * + * Further HDVF computations will be restricted to `K` (ie.\ computation of reduced homology). + */ + inline void set_mask_K () + { + _subCC = _KCC ; + _subCC.screen_matrices(this->_DD_col); + } + + /** \brief Sets the current sub chain complex masks over `L-K`. + * + * Further HDVF computations will be restricted to `L-K` (ie.\ computation of reduced homology). + */ + inline void set_mask_L_K () + { + _subCC = _KCC.complement() ; + _subCC.screen_matrices(this->_DD_col); + } + + /** \brief Returns the value of the current sub chain complex mask. + */ + Sub_chain_complex_mask get_current_mask() + { + return _subCC; + } + + /** + * \brief Computes a perfect HDVF over the current sub chain complex. + * + * As long as valid pairs for `A` exist in the current sub chain complex, the function selects the first available pair (returned by `find_pair_A()`) and applies the corresponding `A()` operation. + * If the coefficient ring of coefficients is a field, this operation always produces a perfect HDVF (ie.\ the reduced boundary is null and the reduction provides homology and cohomology information). + * Otherwise the operation produces a maximal HDVF with a residual boundary matrix over critical cells. + * + * If the HDVF is initially not trivial (some cells have already been paired), the function completes it into a perfect HDVF. + * + * \param verbose If this parameter is `true`, all intermediate reductions are printed out. + * + * \return The vector of all `Cell_pair` paired with A. + */ + std::vector compute_perfect_hdvf(bool verbose = false); + + /** + * \brief Computes a random perfect HDVF over the current sub chain complex. + * + * As long as valid pairs for A exist in the current sub chain complex, the function selects a random pair (among pairs returned by `find_pairs_A()`) and applies the corresponding `A()` operation. + * If the coefficient ring is a field, this operation always produces a perfect HDVF (that is the reduced boundary is null and the reduction provides homology and cohomology information). + * + * If the HDVF is initially not trivial (some cells have already been paired), the function randomly completes it into a perfect HDVF. + * + * \warning This method is slower that `compute_perfect_hdvf()` (finding out all possible valid pairs requires additional time). + * + * \param verbose If this parameter is `true`, all intermediate reductions are printed out. + * + * \return The vector of all pairs of cells used for apply A. + */ + std::vector compute_rand_perfect_hdvf(bool verbose = false); + + /** + * \brief Computes a "pairing" HDVF between K and L-K + * + * \warning Run `compute_perfect_hdvf()` first (to build perfect HDVFs over `K` and `L-K` respectively). + * + * The function computes a perfect HDVF over remaining critical cells. Each pair of cells inserted with the `A()` operation maps corresponding homology/cohomology generators in the Alexander isomorphism. + * + * \return The vector of paired critical cells (encoding Alexander isomorphism). + */ + std::vector compute_pairing_hdvf() ; + + /** + * \brief Computes a random "pairing" HDVF between K and L-K + * + * \warning Run `compute_perfect_hdvf()` first (to build perfect HDVFs over `K` and `L-K` respectively). + * + * The function computes a random perfect HDVF over remaining critical cells. Each pair of cells inserted with the `A() operation maps corresponding homology/cohomology generators in the Alexander isomorphism. + * + * \return The vector of paired critical cells (encoding Alexander isomorphism). + */ + std::vector compute_rand_pairing_hdvf() ; + + // Hdvf_duality getters + /** + * \brief Gets cells with a given `PSC_flag` in any dimension *in the current sub chain complex*. + * + * The function returns a vector containing, for each dimension, the vector of cells with a given `PSC_flag`. + * + * \param flag PSC_flag to select. + */ + std::vector > psc_flags (PSC_flag flag) const ; + + /** + * \brief Gets cells with a given `PSC_flag` in dimension `q` *in the current sub chain complex*. + * + * The function returns the vector of cells of dimension `q` with a given `PSC_flag`. + * + * \param flag PSC_flag to select. + * \param q Dimension visited. + */ + std::vector psc_flags (PSC_flag flag, int q) const ; + + // Hdvf_duality I/O + + /** + * \brief Prints the homology and cohomology reduction information for `K` and `L-K`. + * + * Prints \f$f^*\f$, \f$g\f$ \f$\partial'\f$ the reduced boundary over each critical cell. + * + * By default, outputs the complex to `std::cout`. + */ + std::ostream& insert_reduction(std::ostream& out = std::cout) + { + // Print K + out << "----> K" << std::endl ; + _subCC = _KCC ; + _subCC.screen_matrices(this->_DD_col); + print_reduction_sub(out) ; + + // Print L-K + _subCC = _KCC.complement() ; + _subCC.screen_matrices(this->_DD_col); + print_reduction_sub(out) ; + + // Set back _subCC to K + _subCC = _KCC ; + + return out ; + } + + /** + * \brief Prints the homology and cohomology reduction information for the current such chain complex. + * + * Prints \f$f^*\f$, \f$g\f$ \f$\partial'\f$ the reduced boundary over each critical cell. + * + * By default, outputs the complex to `std::cout`. + */ + std::ostream& print_reduction_sub(std::ostream& out = std::cout) // const; + { + // Print critical cells + out << "----- critical cells:" << std::endl; + for (int q = 0; q <= _L.dimension(); ++q) { + out << "--- dim " << q << std::endl; + for (size_t i = 0; i < _L.number_of_cells(q); ++i) + { + if ((this->_flag[q][i] == CRITICAL) && (_subCC.get_bit(q, i))) { + out << i << " "; + } + } + out << std::endl; + } + + if (_hdvf_opt & (OPT_FULL | OPT_G)) + { + // Print matrices g + out << "----- g:" << std::endl; + for (int q = 0; q <= _L.dimension(); ++q) { + out << "--- dim " << q << std::endl; + for (size_t i = 0; i < _L.number_of_cells(q); ++i) { + if ((this->_flag[q][i] == CRITICAL) && (_subCC.get_bit(q, i))) { + out << "g(" << i << ") = (" << i << ")"; + // Iterate over the ith column of _G_col + typename Base::Column_chain col(OSM::get_column(this->_G_col.at(q), i)) ; // TODO cget + for (typename Base::Column_chain::const_iterator it_col = col.cbegin(); it_col != col.cend(); ++it_col) { + out << " + " << it_col->second << ".(" << it_col->first << ") + "; + } + out << std::endl; + } + } + } + } + + if (_hdvf_opt & (OPT_FULL | OPT_F)) + { + // Print matrices f* + out << "----- f*:" << std::endl; + for (int q = 0; q <= _L.dimension(); ++q) { + out << "--- dim " << q << std::endl; + for (size_t i = 0; i < _L.number_of_cells(q); ++i) { + if ((this->_flag[q][i] == CRITICAL) && (_subCC.get_bit(q, i))) { + out << "f*(" << i << ") = (" << i << ")"; + // Iterate over the ith row of _F_row + typename Base::Row_chain row(OSM::get_row(this->_F_row.at(q), i)) ; // TODO cget + for (typename Base::Row_chain::const_iterator it_row = row.cbegin(); it_row != row.cend(); ++it_row) { + out << " + " << it_row->second << ".(" << it_row->first << ") + "; + } + out << std::endl; + } + } + } + } + return out ; + } + + /** + * \brief Prints the reduced boundary over critical cells of `K` and `L-K`. + * + * The method prints out the reduced boundary matrix in each dimension, restricted to critical cells of `K` and `L-K` (ie.\ the matrix used to compute Alexander pairing). + * + * \warning Call this method after `compute_perfect_hdvf()`. + * + * By default, outputs the complex to `std::cout`. + */ + std::ostream& print_bnd_pairing(std::ostream& out = std::cout) + { + Sub_chain_complex_mask subPair(_L, false) ; + for (int q=0; q<=_L.dimension(); ++q) + { + for (size_t i=0; i<_critical_K.at(q).size(); ++i) + subPair.set_bit_on(q, _critical_K.at(q).at(i)) ; + for (size_t i=0; i<_critical_L_K.at(q).size(); ++i) + subPair.set_bit_on(q, _critical_L_K.at(q).at(i)) ; + } + // Print corresponding submatrices _DD_col + for (int q=1; q<=_L.dimension(); ++q) + { + out << "--> dim " << q << " : q / q-1 cells" << std::endl ; + out << "id " << q << " : " ; + for (typename OSM::Bitboard::iterator it = subPair.get_bitboard(q).begin(); it != subPair.get_bitboard(q).end(); ++it) + out << *it << " " ; + out << std::endl ; + out << "id " << q-1 << " : " ; + for (typename OSM::Bitboard::iterator it = subPair.get_bitboard(q-1).begin(); it != subPair.get_bitboard(q-1).end(); ++it) + out << *it << " " ; + out << std::endl ; + for (typename OSM::Bitboard::iterator it = subPair.get_bitboard(q).begin(); it != subPair.get_bitboard(q).end(); ++it) + { + for (typename OSM::Bitboard::iterator it2 = subPair.get_bitboard(q-1).begin(); it2 != subPair.get_bitboard(q-1).end(); ++it2) + { + if (this->_DD_col.at(q).get_coefficient(*it2, *it) == 0) + out << ".\t" ; + else + out << this->_DD_col.at(q).get_coefficient(*it2, *it) << "\t" ; + } + out << std::endl ; + } + } + return out ; + } + + /** + * \brief Exports primary/secondary/critical labels *of the current sub chain complex* for vtk export. + * + * The method exports the labels of every cells in each dimension. + * + * \return A vector containing, for each dimension, the vector of labels by cell index. + */ + std::vector > psc_labels () const + { + std::vector > labels(this->_K.dimension()+1) ; + for (int q=0; q<=this->_K.dimension(); ++q) + { + for (size_t i = 0; i_K.number_of_cells(q); ++i) + { + if (_subCC.get_bit(q, i)) // i belongs to _subCC + { + if (this->_flag.at(q).at(i) == PRIMARY) + labels.at(q).push_back(-1) ; + else if (this->_flag.at(q).at(i) == SECONDARY) + labels.at(q).push_back(1) ; + else + labels.at(q).push_back(0) ; + } + else // i does not belongs to _subCC + labels.at(q).push_back(2) ; + } + } + return labels ; + } + + /** + * \brief Exports homology generators *of the current sub chain complex* associated to `cell_index` (critical cell) of dimension `q` (used by vtk export). + * + * The method exports the chain \f$g(\sigma)\f$ for \f$\sigma\f$ the cell of index `cell_index` and dimension `q`. + * + * \return A column-major chain. + */ + Column_chain homology_chain (size_t cell_index, int dim) const + { + if ((dim<0) || (dim>this->_K.dimension())) + throw "Error : homology_chain with dim out of range" ; + if (!_subCC.get_bit(dim, cell_index)) + throw "Error : homology_chain for a cell out of current sub chain complex" ; + + if (this->_hdvf_opt & (OPT_FULL | OPT_G)) + { + // Get g(cell, dim) with per indices + Column_chain g_cell(OSM::get_column(this->_G_col.at(dim), cell_index)) ; + // Add 1 to the cell + g_cell.set_coefficient(cell_index, 1) ; + // Keep cells of the chain belonging to _subCC + Column_chain g_cell_sub(g_cell.dimension()) ; + for (typename Column_chain::const_iterator it = g_cell.begin(); it != g_cell.end(); ++it) + { + + if (_subCC.get_bit(dim, it->first)) + { + g_cell_sub.set_coefficient(it->first, it->second) ; + } + } + return g_cell_sub ; + } + else + throw "Error : trying to export g_chain without proper HDVF option" ; + } + + /** + * \brief Exports cohomology generators *of the current sub chain complex* associated to `cell_index` (critical cell) of dimension `q` (used by vtk export). + * + * The method exports the chain \f$f^\star(\sigma)\f$ for \f$\sigma\f$ the cell of index `cell_index` and dimension `q`. + * + * \return A column-major chain. + */ + Column_chain cohomology_chain (size_t cell_index, int dim) const + { + if ((dim<0) || (dim>this->_K.dimension())) + throw "Error : cohomology_chain with dim out of range" ; + if (!_subCC.get_bit(dim, cell_index)) + throw "Error : cohomology_chain for a cell out of current sub chain complex" ; + + if (this->_hdvf_opt & (OPT_FULL | OPT_F)) + { + Row_chain fstar_cell(OSM::get_row(this->_F_row.at(dim), cell_index)) ; + // Add 1 to the cell + fstar_cell.set_coefficient(cell_index, 1) ; + + // Keep cells of the chain belonging to _subCC + Column_chain fstar_cell_sub(fstar_cell.dimension()) ; + for (typename Column_chain::const_iterator it = fstar_cell.begin(); it != fstar_cell.end(); ++it) + { + + if (_subCC.get_bit(dim, it->first)) + { + fstar_cell_sub.set_coefficient(it->first, it->second) ; + } + } + return fstar_cell_sub ; + } + } +} ; + +// Constructor +template +Hdvf_duality::Hdvf_duality(const ChainComplex& L, Sub_chain_complex_mask& K, int hdvf_opt) : +Hdvf_core(L,hdvf_opt), _L(L), _hdvf_opt(hdvf_opt), _KCC(K), _subCC(K) {} + +// find a valid Cell_pair for A in dimension q +template +Cell_pair Hdvf_duality::find_pair_A(int q, bool &found) const +{ + found = false; + Cell_pair p; + + // Iterate through columns of _DD_col[q+1] + for (OSM::Bitboard::iterator it_col = this->_DD_col[q+1].begin(); (it_col != this->_DD_col[q+1].end() && !found); ++it_col) + { + const typename Base::Column_chain& col(OSM::cget_column(this->_DD_col[q+1], *it_col)) ; + + // Iterate through the entries of the column + // Check that the row belongs to the subchaincomplex + for (typename Base::Column_chain::const_iterator it = col.begin(); (it != col.end() && !found); ++it) { + if (_subCC.get_bit(q, it->first) && (abs(it->second) == 1)) { + // If an entry with coefficient 1 or -1 is found, set the pair and mark as found + p.sigma = it->first; + p.tau = *it_col; + p.dim = q; + found = true; + } + } + } + return p; +} + +// find a valid Cell_pair containing tau for A in dimension q +template +Cell_pair Hdvf_duality::find_pair_A(int q, bool &found, size_t tau) const +{ + found = false; + Cell_pair p ; + // Check tau belongs to _subCC + if (!_subCC.get_bit(q, tau)) + throw("Hdvf_duality: searching for a cell tau outside _subCC") ; + + // Search for a q-1 cell tau' such that <_d(tau),tau'> invertible + // and tau' belongs to _subCC + const typename Base::Column_chain& tmp2(OSM::cget_column(this->_DD_col.at(q), tau)) ; + for (typename Base::Column_chain::const_iterator it = tmp2.cbegin(); (it != tmp2.cend() && !found); ++it) + { + if (_subCC.get_bit(q-1, it->first) && abs(it->second) == 1) + { + found = true ; + p.sigma = it->first ; + p.tau = tau ; + p.dim = q-1 ; + } + } + + // Search for a q+1 cell tau' such that <_d(tau'),tau> invertible, ie <_cod(tau),tau'> invertible + // and tau' belongs to _subCC + typename Base::Row_chain tmp(OSM::get_row(this->_DD_col.at(q+1), tau)) ; + for (typename Base::Row_chain::const_iterator it = tmp.cbegin(); (it != tmp.cend() && !found); ++it) + { + if (_subCC.get_bit(q+1, it->first) && (abs(it->second) == 1)) + { + found = true ; + Cell_pair p ; + p.sigma = tau ; + p.tau = it->first ; + p.dim = q ; + } + } + return p; +} + +// find all the valid Cell_pair for A in dimension q +template +std::vector Hdvf_duality::find_pairs_A(int q, bool &found) const +{ + std::vector pairs; + found = false ; + + // Iterate through columns of _DD_col[q+1] + for (OSM::Bitboard::iterator it_col = this->_DD_col[q+1].begin(); it_col != this->_DD_col[q+1].end(); ++it_col) + { + const typename Base::Column_chain& col(OSM::cget_column(this->_DD_col[q+1], *it_col)) ; + + // Iterate through the entries of the column + for (typename Base::Column_chain::const_iterator it = col.begin(); it != col.end(); ++it) { + if (_subCC.get_bit(q, it->first) && ((it->second == 1) || (it->second == -1))) { + // If an entry of _subCC with coefficient 1 or -1 is found, set the pair and mark as found + Cell_pair p; + p.sigma = it->first; + p.tau = *it_col; + p.dim = q; + pairs.push_back(p) ; + found = true; + } + } + } + return pairs; +} + +// find all the valid Cell_pair containing tau for A in dimension q +template +std::vector Hdvf_duality::find_pairs_A(int q, bool &found, size_t tau) const +{ + found = false; + std::vector pairs; + // Check if tau belongs to _subCC + if (!_subCC.get_bit(q, tau)) + throw("Hdvf_duality: searching for a cell tau outside _subCC") ; + + // Search for a q+1 cell tau' such that <_d(tau'),tau> invertible, ie <_cod(tau),tau'> invertible + // and tau' belongs to _subCC + typename Base::Row_chain tmp(OSM::get_row(this->_DD_col.at(q+1), tau)) ; + for (typename Base::Row_chain::const_iterator it = tmp.cbegin(); it != tmp.cend(); ++it) + { + if (_subCC.get_bit(q+1, it->first) && (abs(it->second) == 1)) + { + found = true ; + Cell_pair p ; + p.sigma = tau ; + p.tau = it->first ; + p.dim = q ; + pairs.push_back(p) ; + } + } + // Search for a q-1 cell tau' such that <_d(tau),tau'> invertible + // and tau' belongs to _subCC + const typename Base::Column_chain& tmp2(OSM::cget_column(this->_DD_col.at(q), tau)) ; + for (typename Base::Column_chain::const_iterator it = tmp2.cbegin(); it != tmp2.cend(); ++it) + { + if (_subCC.get_bit(q-1, it->first) && (abs(it->second) == 1)) + { + found = true ; + Cell_pair p ; + p.sigma = it->first ; + p.tau = tau ; + p.dim = q-1 ; + pairs.push_back(p) ; + } + } + return pairs; +} + +// Compute dual perfect HDVFs (over K and L-K) +template +std::vector Hdvf_duality::compute_perfect_hdvf(bool verbose) +{ + std::cout << std::endl << "==== Compute perfect HDVF over K" << std::endl ; + // Set _subCC to K + _subCC = _KCC ; + // Restrict _DD_col accordingly + _subCC.screen_matrices(this->_DD_col); + // Compute perfect HDVF over K + std::vector tmp = Base::compute_perfect_hdvf(verbose) ; + std::cout << tmp.size() << " cells paired" << std::endl ; + _critical_K = psc_flags(CRITICAL) ; + + std::cout << std::endl << "==== Compute perfect HDVF over L-K" << std::endl ; + // set _subCC to L-K + _subCC = _KCC.complement() ; + // Restrict _DD_col accordingly + _subCC.screen_matrices(this->_DD_col); + // Compute perfect HDVF over L-K + std::vector tmp2 = Base::compute_perfect_hdvf(verbose) ; + std::cout << tmp2.size() << " cells paired" << std::endl ; + _critical_L_K = psc_flags(CRITICAL) ; + + // Return the vector of paired cells + tmp.insert(tmp.end(), tmp2.begin(), tmp2.end()); + return tmp; +} + +// Compute random dual perfect HDVFs (over K and L-K) +template +std::vector Hdvf_duality::compute_rand_perfect_hdvf(bool verbose) +{ + std::cout << std::endl << "==== Compute perfect HDVF over K" << std::endl ; + // Set _subCC to K + _subCC = _KCC ; + // Restrict _DD_col accordingly + _subCC.screen_matrices(this->_DD_col); + // Compute perfect HDVF over K + std::vector tmp = Base::compute_rand_perfect_hdvf(verbose) ; + std::cout << tmp.size() << " cells paired" << std::endl ; + _critical_K = psc_flags(CRITICAL) ; + + std::cout << std::endl << "==== Compute perfect HDVF over L-K" << std::endl ; + // set _subCC to L-K + _subCC = _KCC.complement() ; + // Restrict _DD_col accordingly + _subCC.screen_matrices(this->_DD_col); + // Compute perfect HDVF over L-K + std::vector tmp2 = Base::compute_rand_perfect_hdvf(verbose) ; + std::cout << tmp2.size() << " cells paired" << std::endl ; + _critical_L_K = psc_flags(CRITICAL) ; + + // Return the vector of paired cells + tmp.insert(tmp.end(), tmp2.begin(), tmp2.end()); + return tmp; +} + +// Compute Alexander isomorphism (A pairing between critical cells of K / L-K) +template +std::vector Hdvf_duality::compute_pairing_hdvf() +{ + // TODO : check both HDVFs are perfect + + std::cout << std::endl << "==== Compute pairing" << std::endl ; + + // Create a full Sub_chain_complex_mask + _subCC = Sub_chain_complex_mask(_L) ; + _subCC.screen_matrices(this->_DD_col); + // If necessary, copy the HDVF before computing the pairing -> otherwise we loose it... + std::vector pairing = Base::compute_perfect_hdvf() ; + return pairing ; +} + +// Compute random Alexander isomorphism (A pairing between critical cells of K / L-K) +template +std::vector Hdvf_duality::compute_rand_pairing_hdvf() +{ + // TODO : check both HDVFs are perfect + + std::cout << std::endl << "==== Compute pairing" << std::endl ; + + // Create a full Sub_chain_complex_mask + _subCC = Sub_chain_complex_mask(_L) ; + _subCC.screen_matrices(this->_DD_col); + // If necessary, copy the HDVF before computing the pairing -> otherwise we loose it... + std::vector pairing = Base::compute_rand_perfect_hdvf() ; + return pairing ; +} + +// Method to get cells of _subCC with a given PSC_flag for each dimension +template +std::vector > Hdvf_duality::psc_flags (PSC_flag flag) const +{ + std::vector > res(_L.dimension()+1) ; + for (int q=0; q<=_L.dimension(); ++q) + { + for (size_t i=0; i<_L.number_of_cells(q); ++i) + { + if (_subCC.get_bit(q, i) && (this->_flag.at(q).at(i) == flag)) + res.at(q).push_back(i) ; + } + } + return res ; +} + +// Method to get cells of _subCC with a given PSC_flag for a given dimension +template +std::vector Hdvf_duality::psc_flags (PSC_flag flag, int q) const +{ + std::vector res ; + for (size_t i=0; i_K.number_of_cells(q); ++i) + { + if (_subCC.get_bit(q, i) && (this->_flag.at(q).at(i) == flag)) + res.push_back(i) ; + } + return res ; +} + +} /* end namespace Homological_discrete_vector_field */ +} /* end namespace CGAL */ + +#endif // CGAL_HDVF_HDVF_DUALITY_H diff --git a/HDVF/include/CGAL/HDVF/Hdvf_persistence.h b/HDVF/include/CGAL/HDVF/Hdvf_persistence.h new file mode 100644 index 00000000000..6c4e736d210 --- /dev/null +++ b/HDVF/include/CGAL/HDVF/Hdvf_persistence.h @@ -0,0 +1,829 @@ +// Copyright (c) 2025 LIS Marseille (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Alexandra Bac + +#ifndef CGAL_HDVF_HDVF_PERSISTENCE_H +#define CGAL_HDVF_HDVF_PERSISTENCE_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace CGAL { +namespace Homological_discrete_vector_field { + +// Types for persistent homology / cohomology + + + +/*! + \ingroup PkgHDVFRef + + The class `Hdvf_persistence` computes persistent homology using HDVFs (over a ring of coefficients which should actually be a **field**). Hence, unlike other persistence algorithms, beside standard persistent intervals informations (birth/death indices, degrees, associated cells), `Hdvf_persistence` also provides **homology and cohomology generators for persistent pairs**. Intuitively, holes die when they are "filled" by a cell: associated homology and cohomology generators provide a representation of the hole and of the cells filling the hole. + + Given a `Filtration`, the `Hdvf_persistence` constructor basically builds a HDVF where indices of cells in the bases follow the filtration order (permutations between initial indices of cells in the chain complex and new indices given by the filtration are stored). + Besides, `Hdvf_persistence` derives from `Hdvf_core` with the SparseMatrix parameter type set to `OSM::Sub_sparse_matrix`. Hence, `Hdvf_persistence` computes homology over a larger and larger sub-complex of `K` (encoded through a Bitboard mask of `OSM::Sub_sparse_matrix`) following the filtration `f`. At each step of the filtration, the new cell is paired (A operation) with the youngest cell valid for A. + + A few words about persistent generators. At a given time \f$t_2\f$ along the filtration, let us denote by \f$\tau\f$ the \f$q+1\f$-cell inserted and let us assume that persistent homology computation pairs \f$A(\sigma, \tau)\f$ (let \f$t_1\f$ be the index of \f$\sigma\f$ along the filtration). A new persistent interval \f$(t_1, t_2)\f$is created, generated by \f$\sigma\f$ and filled by \f$\tau\f$. + Reduction at time \f$t_2-1\f$ (just before the insertion of \f$\tau\f$) provides persistent generators: \f$g(\sigma)\f$ is a cycle and \f$g(\tau)\f$ is a chain "filling" this cycle (before the pairing \f$A(\sigma, \tau)\f$, the HDVF over \f$K_{t_2}\f$ is not perfect, thus \f$g(\tau)\f$ is not a cycle but a chain bounded by \f$q\f$-cycles). + + Next figures illustrate such informations for a filtration of a dented double torus. + + + + Filtration `Filtration_lower_star` along the z axis of a dented double torus. + + + + + + The first persistent hole is a dimension 1 hole with a duration of 0.359 along z. + - (Left) the complex at time 622 along the filtration (in blue, the 2-cell \f$\tau\f$ added at time 623). This 1-homology of this complex contains two cycles depicted in white and yellow respectively. Yellow curve: a 1-cycle born at time 319 (with cell \f$\sigma\f$) along the filtration. White curve: the second 1-cycle of this complex (born at time 156 along the filtration). + - (Middle) the filtration adds the (blue) 2-cell \f$\tau\f$ at time 623: this cell fills one the 1-cycles (actually the "youngest" cycle, thus the yellow one). Middle image illustrates as a yellow patch the "filling 2-chain", a chain a 2-cells filling the hole when the cell \f$\tau\f$ is added. As stated earlier, this filling chain is not a cycle and it equals \f$g(\tau)\f$ in the (non perfect) reduction before the pairing `A(319, 623)` between \f$\sigma\f$ and \f$\tau\f$. Middle figure illustrates that in the (non perfect) HDVF before A operation, \f$g(\tau)\f$ is not a cycle but a chain bounded by \f$q\f$-cycles. + - (Right) the complex after insertion of \f$\tau\f$ and the pairing `A(319, 623)` between \f$\sigma\f$ and \f$\tau\f$. Only the white 1-cycle remains (actually its persistent lifetime is infinite). + + + + The figure above illustrates next persistent hole (dimension 0 hole) which duration is 0.684 along z. The cell \f$\tau\f$ is added at time 1237 and fills the 0-hole (connected component) introduced at time 756 by the cell \f$\sigma\f$. The persistent generator associated to \f$\sigma\f$ is simply the cell itself identifying its connected component. The persistent generator associated to \f$\tau\f$ is a 1-chain (depicted in yellow) connecting \f$\sigma\f$ to the critical cell identifying the second connected component. + + + + + + Figures above illustrate next finite persistent intervals and (right) infinite dimension 1 persistent intervals. + + \cgalModels{HDVF} + + \tparam ChainComplex a model of the `AbstractChainComplex` concept, providing the type of abstract chain complex used (the underlying ring of coefficients must be a model of `Field`). + \tparam Degree a scalar data type used for the degrees of the filtration (a model of `RealEmbeddable` concept). + \tparam Filtration_ a model of the `Filtration` concept, providing the filtration used to compute persistence. + + */ + +template +class Hdvf_persistence : public Hdvf_core +{ +public: + /*! \brief Chain complex type */ + typedef ChainComplex Chain_complex; + + /*! \brief Type of coefficients used to compute homology. */ + typedef typename Chain_complex::Coefficient_ring Coefficient_ring; + + /*! \brief Structure storing (full) persistent interval data: + * - `time_birth`, `time_death`: persistent interval filtration indices + * - `cell_birth`, `cell_death`: persistent interval cells + * - `degree_birth`, `degree_death`: persistent interval degrees + * + * Infinite intervals are encoded by setting birth data = death data (time, degree and cell). + */ + struct Persistence_interval { + size_t time_birth, time_death; + Degree degree_birth, degree_death; + Cell cell_birth, cell_death; + + /** \brief Computes the (degree) duration of a persistent interval (ie.\ persistent hole) + * + * By definitions of "default" values, infinite intervals have a duration of -1. + */ + inline Degree duration () const + { + // Test if the interval is infinite + if (time_birth == time_death) + return -1 ; + else + return degree_death-degree_birth; + } + + /** + * \brief Output the persistence interval to a stream. + */ + inline std::ostream& insert (std::ostream& out_stream) const + { + if (time_death != time_birth) // finite interval + { + out_stream << "[" << time_birth << " (" << cell_birth.first << ", " << cell_birth.second << ") -> " ; + out_stream << time_death << " (" << cell_death.first << ", " << cell_death.second << ") / duration: " << duration() << "]" << std::endl ; + } + else + { + out_stream << "[" << time_birth << " (" << cell_birth.first << ", " << cell_birth.second << ") -> " ; + out_stream << "inf]" << std::endl ; + } + return out_stream ; + } + }; + + // Matrices types + /*! + Type of column-major chains + */ + typedef CGAL::OSM::Sparse_chain Column_chain; + + /*! + Type of row-major chains + */ + typedef CGAL::OSM::Sparse_chain Row_chain; + + /*! + Type of column-major sparse matrices + */ + typedef CGAL::OSM::Sub_sparse_matrix Column_matrix; + + /*! + Type of row-major sparse matrices + */ + typedef CGAL::OSM::Sub_sparse_matrix Row_matrix; + + /*! Type of parent HDVF class (`Hdvf_core` with appropriate template parameters) + * The `SparseMatrix` model is set to `Sub_sparse_matrix` to activate (co)homology computation over a subcomplex. + */ + typedef Hdvf_core Base ; + + /*! Type of filtrations used to compute persistence. + */ + typedef Filtration_ Filtration; + + /*! \brief Hole information returned by the persistent diagram iterator. + * + * Information comprises: + * - `Persistence_interval` providing informations related to the "times" of the persistent interval (index, degree and cell of birth/death). + * - `labelsPSC` storing HDVF flags at death time of the hole + */ + typedef struct { + Persistence_interval hole ; + std::vector > labelsPSC ; + Column_chain homology_chain_birth, homology_chain_death, cohomology_chain_birth, cohomology_chain_death ; + } Persistent_hole ; + +protected: + /* \brief Reference to the filtration used for persistence */ + const Filtration &_f ; + + /* \brief Permutation between indices in the chain complex `K` and indices along the filtration + * Indices along the filtration provide new indices for cells in each dimension. + */ + std::vector > _K_to_per, _per_to_K ; + + /* \brief Vector of persistent pairs computed */ + std::vector _persist ; + + /* \brief Boolean determining whether or not export homology/cohomology generators associated to persistent pairs + * - If `_with_export` is `true`, PSC labels and homology/cohomology generators are stored for each persistent pair of duration (that is, such as the difference between degrees of birth/death) strictly positive. + * - If `_with_export` is `false`, only persistent intervals are stored. + */ + bool _with_export ; + + /* \brief Vector of exported PSC labels */ + std::vector >> _export_labels ; + + /* \brief Vector of exported homology/cohomology generators */ + std::vector > _export_g, _export_fstar ; + + /* Warning: + * In order to encode "time" as an unsigned int while the initial value is -1, + * we store current times + 1 + */ + + /* Current time + 1 (that is, current index + 1) in the filtration */ + size_t _t ; + /* Current time +1 (that is, current index + 1) along each dimension */ + std::vector _t_dim ; + + /* Bitboard masks in each dimension + * At a given filtration time, only cells already met have a bit set to 1 + */ + std::vector _masks ; + + +private: + // Hide find_pair_A methods of the Hdvf_core class + // Hide A operation of the Hdvf_core class + // This operation is redefined with a different prototype in Hdvf_persistence and set as private since persistence lets no choice for A pairing (rule of the "youngest") + using Base::find_pair_A; + using Base::find_pairs_A; + using Base::A; +public: + /** + * \brief Hdvf_persistence default constructor + * + * Builds an "empty" HDVF_persistence (with all cells critical) associated to the chain complex `K` and the filtration `f`. + * By default, the HDVF option is set to OPT_FULL (full reduction computed) + * + * \param K A chain complex (a model of `AbstractChainComplex`). + * \param f A filtration (a model of `Filtration`). + * \param hdvf_opt Option for HDVF computation (`OPT_BND`, `OPT_F`, `OPT_G` or `OPT_FULL`) + * \param with_export Boolean option to activate or not the export of PSC labels and homology/cohomology generators for of persistent intervals of positive duration. This information is used by vtk exporters. + */ + Hdvf_persistence(const Chain_complex& K, const Filtration& f, int hdvf_opt = OPT_BND, bool with_export = false) ; + + /** + * \brief Computes a perfect persistent HDVF. + * + * This method follows the filtration and considers cells one by one. For each of them, it searches the youngest possible cell valid for A (returned by `find_pair_A()`), and applies the corresponding `A()` operation. + * By definition of persistent homology, the `IntegralDomainWithoutDivision` of coefficients *must be* a field. + * + * \param verbose If this parameter is `true`, all intermediate reductions are printed out. + * + * \returns The vector of all `Cell_pair` paired with A. + */ + std::vector compute_perfect_hdvf(bool verbose = false) + { + bool found; + Cell_pair pair; + std::vector res ; + for (size_t i=0; i < _f._filtration.size(); ++i) + { + this->progress_bar(i, _f._filtration.size()) ; + found = false ; + pair = step_persist(found, verbose) ; + if (found) + res.push_back(pair) ; + } + + // Compute "infinite" holes + std::vector > criticals(this->psc_flags(CRITICAL)) ; + for (int q=0; q < criticals.size(); ++q) + { + for (size_t i : criticals.at(q)) + { + // i : persistence index + // encoding of the second "infinite" cell: + // -> equals the first cell (ie. birth = death) + const Cell_pair p = {i, this->_K.number_of_cells(q+1), q} ; + const size_t ki(_per_to_K.at(q).at(i)) ; // K index + const Cell c(ki,q) ; + const size_t ti(_f._cell_to_t.at(c)) ; + const Degree di(_f._deg.at(i)) ; + + Persistence_interval hole; + hole.time_birth = ti; + hole.time_death = ti; + hole.degree_birth = di; + hole.degree_death = di; + hole.cell_birth = c; + hole.cell_death = c; + + _persist.push_back(hole) ; + + // If export is on, store export data + if (_with_export) + export_hdvf_persistence_pair(p) ; + } + } + return res; + } + + /** \brief Gets the "with_export" %Boolean flag. + * If the flag is `true`, homology/cohomology generators and corresponding PSC labels are exported for each persistent interval of positive duration. + */ + bool with_export () { return _with_export ; } + + /** \brief Get a constant reference on the filtration + */ + const Filtration& filtration() { return _f; } + + /** \brief Overload of `operator<<()` for `Hdvf_persistence`. + * + * Prints out finite and infinite persistence intervals. + * + * \param out_stream Reference to an output stream. + * \param per_hdvf Constant reference on the `Hdvf_persistence` to print. + */ + friend std::ostream& operator<< (std::ostream& out_stream, const Hdvf_persistence& per_hdvf) + { + size_t i = 0 ; + for (Persistence_interval hole : per_hdvf._persist) + { + if (std::abs(hole.duration()) > 0) + { + out_stream << i << " --- duration : " << hole.duration() << " -- " ; + hole.insert(out_stream); + out_stream << std::endl ; + } + ++i ; + } + return out_stream ; + } + + /** \brief Prints informations related to the filtration. + * + * Prints out the filtration and associated permutations (`_K_to_per` and `_per_to_K`) between indices of cells in each dimension in the basis of `K` and indices along the filtration. + * + * \param out Reference to an output stream. + */ + std::ostream& print_hdvf_persistence_info (std::ostream& out) + { + out << "Filtration: " << _f << std::endl ; + out << "_K_to_per and _per_to_K" << std::endl ; + for (int q=0; q<=this->_K.dimension(); ++q) + { + out << "-> dim " << q << std::endl ; + out << "index_per -(_per_to_K)-> index_K -(_K_to_per)-> index_per" << std::endl ; + for (size_t i=0; i_K.number_of_cells(q); ++i) + { + const size_t id_K(_per_to_K.at(q).at(i)) ; + out << i << " -> " << id_K << " -> " << _K_to_per.at(q).at(id_K) << std::endl ; + } + } + return out ; + } + + + /** + * \brief Exports primary/secondary/critical labels (e.g.\ for vtk export). + * + * The method exports the labels of every cell in each dimension. + * Encoding used: + * - `PRIMARY`: -1 + * - `SECONDARY`: +1 + * - `CRITICAL`: 0 + * + * \returns A vector containing, for each dimension, the vector of labels by cell index. + */ + std::vector > psc_labels () const + { + std::vector > labels(this->_K.dimension()+1) ; + for (int q=0; q<=this->_K.dimension(); ++q) + { + for (size_t i = 0; i_K.number_of_cells(q); ++i) + { + const size_t id_per(_K_to_per.at(q).at(i)) ; + if (id_per < _t_dim.at(q)) + { + if (this->_flag.at(q).at(id_per) == PRIMARY) + labels.at(q).push_back(-1) ; + else if (this->_flag.at(q).at(id_per) == SECONDARY) + labels.at(q).push_back(1) ; + else if (this->_flag.at(q).at(id_per) == CRITICAL) + labels.at(q).push_back(0) ; + else // NONE + labels.at(q).push_back(2) ; + } + else + labels.at(q).push_back(2) ; + } + } + return labels ; + } + + /** + * \brief Exports homology generators associated to `cell_index` (critical cell) of dimension `q` (e.g.\ for vtk export). + * + * The method exports the chain \f$g(\sigma)\f$ for \f$\sigma\f$ the cell of index `cell_index` and dimension `q`. + * + * \returns A column-major chain. + */ + Column_chain homology_chain (size_t cell_index, int q) const + { + if ((q<0) || (q>this->_K.dimension())) + throw "Error : homology_chain with dim out of range" ; + + if (this->_hdvf_opt & (OPT_FULL | OPT_G)) + { + // Get g(cell, dim) with per indices + Column_chain g_cell(OSM::get_column(this->_G_col.at(q), cell_index)) ; + // Add 1 to the cell + g_cell.set_coefficient(cell_index, 1) ; + // Compute the chain with _K indices + Column_chain g_cell_K(g_cell.dimension()) ; + for (typename Column_chain::const_iterator it = g_cell.begin(); it != g_cell.end(); ++it) + { + const size_t i(_per_to_K.at(q).at(it->first)) ; + g_cell_K.set_coefficient(i, it->second) ; + } + return g_cell_K ; + } + else + throw "Error : trying to export g_chain without proper HDVF option" ; + } + + /** + * \brief Exports cohomology generators associated to `cell` (critical cell) of dimension `q` (used by vtk export). + * + * The method exports the chain \f$f^\star(\sigma)\f$ for \f$\sigma\f$ the cell of index `cell` and dimension `q`. + * + * \returns A column-major chain. + */ + Column_chain cohomology_chain (size_t cell_index, int q) const + { + if ((q<0) || (q>this->_K.dimension())) + throw "Error : homology_chain with dim out of range" ; + + if (this->_hdvf_opt & (OPT_FULL | OPT_F)) + { + // Get fstar(cell, dim) with per indices + Row_chain fstar_cell(OSM::get_row(this->_F_row.at(q), cell_index)) ; + // Add 1 to the cell + fstar_cell.set_coefficient(cell_index, 1) ; + + // Compute the chain with _K indices + Column_chain fstar_cell_K(fstar_cell.dimension()) ; + for (typename Column_chain::const_iterator it = fstar_cell.begin(); it != fstar_cell.end(); ++it) + { + const size_t i(_per_to_K.at(q).at(it->first)) ; + fstar_cell_K.set_coefficient(i, it->second) ; + } + return fstar_cell_K ; + } + else + throw "Error : trying to export f_star_chain without proper HDVF option" ; + } + + /*! \brief Iterator over (finite) persistent intervals. + * + * Iterate over persistent intervals of finite degree duration. + * If `discard_small` is true (which is the default), the iterator discards persistent intervals with a null degree duration (that is, small persistent holes). + */ + struct iterator + { + // Iterator tags + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = Persistent_hole; + + /*! \brief Iterator constructor + * + * \param per_hdvf Constant reference over the Hdvf_persistence iterated. + * \param i The initial index. + * \param discard_small If `true` (default), only persistent intervals of (strictly) positive degree duration are iterated. Otherwise, all persistent intervals are iterated. + */ + iterator(const Hdvf_persistence& per_hdvf, size_t i=0, bool discard_small = true) : _i(i), _per_hdvf(per_hdvf), _discard_small(discard_small) + { + if(_discard_small) + { + // Iterate only over holes of duration > 0 + while ((_i<_per_hdvf._persist.size()) && ((_per_hdvf._persist.at(_i)).duration() == 0)) + { + ++_i ; + } + } + } + + /** \brief Returns the `discard_small` flag. */ + bool has_discard_small() { return _discard_small; } + + // Operators + /*! \brief Iterator dereference + * + * \returns A `Persistent_hole` is a structure containing all information related to the current persistence hole. + */ + value_type operator*() const + { + Persistent_hole res ; + res.hole = _per_hdvf._persist.at(_i) ; + res.labelsPSC = _per_hdvf._export_labels.at(_i) ; + if (_per_hdvf._hdvf_opt & (OPT_G | OPT_FULL)) + { + res.homology_chain_birth = _per_hdvf._export_g.at(_i).first ; + res.homology_chain_death = _per_hdvf._export_g.at(_i).second ; + } + if (_per_hdvf._hdvf_opt & (OPT_F | OPT_FULL)) + { + res.cohomology_chain_birth = _per_hdvf._export_fstar.at(_i).first ; + res.cohomology_chain_death = _per_hdvf._export_fstar.at(_i).second ; + } + return res ; + } + + /** + * \brief Prefix increment. Finds next persistent interval. + * + * If `discard_small` is `true`, the iterator searches next persistent interval with a (strictly) positive degree duration, otherwise, the iterator returns next persistent interval. + * + * \returns The reference to the current iterator. + */ + iterator& operator++() + { + ++_i; + if (_discard_small) + { + // Iterate only over holes of duration > 0 + while ((_i<_per_hdvf._persist.size()) && ((_per_hdvf._persist.at(_i)).duration() == 0)) + { + ++_i ; + } + } + return *this; + } + + /** + * \brief Postfix increment. Finds the next non-null index. + * \returns The pre-increment iterator. + */ + iterator operator++(int) { iterator tmp = *this; ++(*this); return tmp; } + + /** + * \brief Equality check. + * \returns `true` if the indices are equal. + */ + friend bool operator== (const iterator& a, const iterator& b) { return a._i == b._i; }; + + /** + * \brief Inequality check. + * \returns `true` if the indices are different. + */ + friend bool operator!= (const iterator& a, const iterator& b) { return a._i != b._i; }; + + private: + size_t _i ; // Index along _persist + const Hdvf_persistence& _per_hdvf ; // per_hdvf iterated + const bool _discard_small ; + }; + + /** + * \brief Iterator to the beginning of persistent intervals. + * + * \param discard_small If `true`, the iterator visits only persistent intervals of (strictly) positive degree duration. Otherwise, visit all persistent intervals. + * + * \returns The iterator to the beginning of the chain indices. + */ + iterator begin(bool discard_small = true) { return iterator(*this, 0, discard_small) ; } + + /** + * \brief Iterator past-the-end of the chain indices. + * + * \returns The past-the-end iterator of the chain indices. + */ + iterator end() { return iterator(*this, _persist.size()) ; } + +private: + /* \brief Export current persistent pair informations. + * + * This method exports and saves PSC labels, homology and cohomology generators of the current persistent pair `p` to corresponding private vectors (`_export_label`, `_export_g`, `_export_fstar`). + * The method is invoqued on the result of `find_pair_A()` before pairing cells with the A operation. + */ + void export_hdvf_persistence_pair(Cell_pair p) + { + // Export labels + std::vector > labels(this->psc_labels()) ; + _export_labels.push_back(labels) ; + // Export g (according to options) + if (this->_hdvf_opt & (OPT_FULL | OPT_G)) + { + Column_chain chain_sigma(homology_chain(p.sigma, p.dim)) ; + Column_chain chain_tau ; + if (p.tau != this->_K.number_of_cells(p.dim+1)) // Check if the second cell is "finite" + chain_tau = homology_chain(p.tau, p.dim+1) ; + _export_g.push_back(std::pair(chain_sigma, chain_tau)) ; + } + // Export fstar (according to options) + if (this->_hdvf_opt & (OPT_FULL | OPT_F)) + { + Column_chain chain_sigma(cohomology_chain(p.sigma, p.dim)) ; + Column_chain chain_tau ; + if (p.tau != this->_K.number_of_cells(p.dim+1)) // Check if the second cell is "finite" + chain_tau = cohomology_chain(p.tau, p.dim+1) ; + _export_fstar.push_back(std::pair(chain_sigma, chain_tau)) ; + } + } + + /* \brief Export empty persistent pair. + * Export empty persistence information (for "small" discarded pairs). + */ + void export_hdvf_persistence_pair() + { + // Export labels + std::vector > labels ; + _export_labels.push_back(labels) ; + // Export g (according to options) + if (this->_hdvf_opt & (OPT_FULL | OPT_G)) + { + Column_chain chain_sigma, chain_tau ; + _export_g.push_back(std::pair(chain_sigma, chain_tau)) ; + } + // Export fstar (according to options) + if (this->_hdvf_opt & (OPT_FULL | OPT_F)) + { + Column_chain chain_sigma, chain_tau ; + _export_fstar.push_back(std::pair(chain_sigma, chain_tau)) ; + } + } + + /* + * \brief Find a valid `Cell_pair` for `A()` for persistent homology. + * + * The function searches, at a given time \f$t\f$ in the filtration, the youngest critical cell \f$\gamma'\f$ forming a valid pair with the cell \f$\gamma\f$. Hence, \f$(\gamma', \gamma)\f$ valid pair is a valid pair + * (ie.\ such that \f$\langle \partial(\gamma), \gamma' \rangle\f$ invertible). + * + * \param found Reference to a %Boolean variable. The method sets `found` to `true` if a valid pair is found, `false` otherwise. + */ + Cell_pair find_pair_A(bool &found) ; + + /* + * \brief Step forward along the filtration. + * + * Searches a possible persistent pair for `A()` with `find_pair_A`, apply `A()` and update internal structures. + */ + Cell_pair step_persist(bool& found, bool verbose = false) ; +} ; + + +template +Hdvf_persistence::Hdvf_persistence(const ChainComplex& K, const Filtration_& f, int hdvf_opt, bool with_export) : Hdvf_core(K,hdvf_opt), _f(f), _with_export(with_export), _t(0) +{ + // Initialisation of _t_dim, _K_to_per and _per_to_K + _t_dim.resize(this->_K.dimension()+1, 0) ; + _K_to_per.resize(this->_K.dimension()+1) ; + _per_to_K.resize(this->_K.dimension()+1) ; + for (int q=0; q<=this->_K.dimension(); ++q) + { + _K_to_per.at(q).resize(this->_K.number_of_cells(q)) ; + _t_dim.at(q) = 0 ; + } + + for (size_t i = 0; i<_f._filtration.size(); ++i) + { + const Cell c(_f._filtration.at(i)); + const int q(c.second) ; + const size_t ind_K_i(c.first) ; + const size_t ind_per_i(_per_to_K.at(q).size()) ; + _per_to_K.at(q).push_back(ind_K_i) ; + _K_to_per.at(q).at(ind_K_i) = ind_per_i ; + } + + // Init _masks + _masks.resize(this->_K.dimension()+1) ; + for (int q=0; q_K.dimension()+1; ++q) + { + _masks.at(q) = OSM::Bitboard(this->_K.number_of_cells(q)) ; + } + + // Init boundary matrices + std::vector _DD_per(this->_K.dimension()+1) ; + + // Copy _DD_col with filtration order (for dimensions q>0) + for (int q = 0 ; q <= this->_K.dimension(); ++q) + { + const std::pair s(this->_DD_col.at(q).dimensions()) ; + _DD_per.at(q) = Column_matrix(s.first, s.second) ; + } + // Set empty mask for _DD_per[0] + _DD_per.at(0).complement(); + + for (int q = 1 ; q <= this->_K.dimension(); ++q) + { + // Cross _DD_col.at(q) and set _DD_per.at(q) coefficients on the fly + for (OSM::Bitboard::iterator it_col = this->_DD_col.at(q).begin(); it_col != this->_DD_col.at(q).end(); ++it_col) + { + const size_t j(*it_col) ; + const Column_chain& col(OSM::cget_column(this->_DD_col.at(q), j)) ; + for (typename Column_chain::const_iterator it = col.begin(); it != col.end(); ++it) + { + const size_t i(it->first) ; + const Coefficient_ring v(it->second) ; + // Cells in the _K basis : i(dim q-1) / j(dim q) + // Convert to indices in the persistent order + const size_t pi(_K_to_per.at(q-1).at(i)), pj(_K_to_per.at(q).at(j)) ; + set_coefficient(_DD_per.at(q), pi, pj, v) ; + } + } + } + this->_DD_col = _DD_per ; + + // Init _DD_col mask (empty for all cells) + for (int q = 1 ; q <= this->_K.dimension(); ++q) + this->_DD_col.at(q).set_sub(_masks.at(q)) ; +} + +template +Cell_pair Hdvf_persistence::find_pair_A(bool &found) +{ + Cell_pair p ; + // Get current cell (in the basis K) + const size_t current_time(_t-1); + Cell c(_f._filtration.at(current_time)) ; + const int q(c.second), sigma(_K_to_per.at(q).at(c.first)) ; + // Search for pairing + found = false; + + if (q >= 1) + { + // Compute bounded max while iterating over the Sparse_chain + const Column_chain& tmp2(OSM::cget_column(this->_DD_col.at(q), sigma)) ; + std::size_t tmax = _t_dim.at(q-1) ; + std::size_t i ; + for (typename Column_chain::const_iterator it = tmp2.cbegin(); it != tmp2.cend(); ++it) + { +// if ((it->first < tmax) && (abs(it->second) == 1)) // possible pairing + if ((it->first < tmax) && ((it->second == 1) || (it->second == -1))) + { + if (!found) // for first cell met + { + found = true ; + i = it->first ; + } + else // update the i with the maximum cell index (persistence) + { + if (it->first > i) + i = it->first ; + } + } + } + if (found) + { + p.sigma = i ; + p.tau = sigma ; + p.dim = q-1 ; + } + } + return p; +} + +template +Cell_pair Hdvf_persistence::step_persist(bool& found, bool verbose) +{ + // Compute next persistent pair + + ++_t ; // Step forward in the filtration + const size_t t_current(_t-1); // Current time + const size_t q_current(_f._filtration.at(t_current).second) ; // Get the dimension of the new current cell + ++_t_dim.at(q_current) ; // Update time in the dimension of the current cell + const size_t t_dim_current(_t_dim.at(q_current)-1); // Time of the current cell in its dimension + _masks.at(q_current).set_on(t_dim_current) ; // Update mask accordingly + this->_DD_col.at(q_current).set_bit_on(t_dim_current) ; // Update _DD_col mask + + // Search for pairing + Cell_pair p(find_pair_A(found)) ; + if (found) + { + // Corresponding persistent interval + const int q(p.dim) ; + // indices of both cells in the _K basis + const size_t ki(_per_to_K.at(q).at(p.sigma)), kj(_per_to_K.at(q+1).at(p.tau)) ; + Cell ci(ki, q), cj(kj, q+1) ; // cells of the interval - in the K basis + size_t ti(_f._cell_to_t.at(ci)), tj(_f._cell_to_t.at(cj)) ; // times of the interval +// FiltrIndexPerInterval interval(ti, tj) ; +// CellsPerInterval interval_cells(ci, cj) ; +// DegreePerInterval interval_deg(_f._deg.at(ti), _f._deg.at(tj)) ; +// Persistence_interval hole(interval, interval_cells, interval_deg) ; + Persistence_interval hole; + hole.time_birth = ti; + hole.time_death = tj; + hole.degree_birth = _f._deg.at(ti); + hole.degree_death = _f._deg.at(tj); + hole.cell_birth = ci; + hole.cell_death = cj; + // Add this interval + _persist.push_back(hole) ; + + // If export is on, store export data for significant persistent intervals + if (_with_export) + { + if (hole.duration() > 0) /*((interval_deg.second-interval_deg.first)>0)*/ + export_hdvf_persistence_pair(p) ; + else + export_hdvf_persistence_pair() ; + } + + + // Prepare for next step + this->A(p.sigma, p.tau, p.dim) ; // Update the reduction + if (verbose) + { + std::cout << "A : " << p.sigma << " - " << p.tau << " (dim " << p.dim << ")" << std::endl ; + this->write_matrices(std::cout) ; + } + } + return p; +} + +// HELP !!!!! +template +std::ostream& operator<< (std::ostream& out_stream, const typename Hdvf_persistence::Persistence_interval& hole) +{ + typedef Hdvf_persistence Base; + // time (cell, dim) -> time (cell, dim) / degree duration + + if (hole.time_death != hole.time_birth) // finite interval + { + out_stream << "[" << hole.time_birth << " (" << hole.cell_birth.first << ", " << hole.cell_birth.second << ") -> " ; + out_stream << hole.time_death << " (" << hole.cell_death.first << ", " << hole.cell_death.second << ") / duration: " << hole.duration() << "]" << std::endl ; + } + else + { + out_stream << "[" << hole.time_birth << " (" << hole.cell_birth.first << ", " << hole.cell_birth.second << ") -> " ; + out_stream << "inf]" << std::endl ; + } + return out_stream ; +} + +} /* end namespace Homological_discrete_vector_field */ +} /* end namespace CGAL */ + +#endif // CGAL_HDVF_HDVF_PERSISTENCE_H diff --git a/HDVF/include/CGAL/HDVF/Hdvf_tools.h b/HDVF/include/CGAL/HDVF/Hdvf_tools.h new file mode 100644 index 00000000000..0a8bcfaf2c0 --- /dev/null +++ b/HDVF/include/CGAL/HDVF/Hdvf_tools.h @@ -0,0 +1,133 @@ +// Copyright (c) 2025 LIS Marseille (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Alexandra Bac + +#ifndef CGAL_HDVF_HDVF_TOOLS_H +#define CGAL_HDVF_HDVF_TOOLS_H + +#include + +#include + +#include +#include + +// IO tools + +namespace CGAL { +namespace Homological_discrete_vector_field { + +inline std::ostream & operator<< (std::ostream &out, const Cell_pair& p) +{ + out << "(" << p.sigma << "/" << p.tau << " - dim " << p.dim << ")" ; + return out ; +} + +/** + * \brief Runs an interaction loop to iterated M, W or MW operations and export the results to vtk. + * + * The loop runs until the key `Q` is pressed. Otherwise, the loop asks for an operation (M, W or MW) and a cell (index and dimension). + * Then all possible paired cells are listed and the user can chose one of them (or none). + */ + +template +void interaction_loop(Hdvf &hdvf, + ChainComplex &complex, + const std::function &hdvf, ChainComplex &complex)> &output_vtk) +{ + bool over = false ; + output_vtk(hdvf, complex) ; + while (!over) + { + std::string instr ; + int ipair ; + std::cout << "Next instruction : M, W, MW or Q (to quit)" << std::endl ; + std::cin >> instr ; + if (instr == std::string("M")) + { + int sigma, q ; + std::cout << "Provide sigma / q (separate with space):" << std::endl ; + std::cin >> sigma >> q ; + bool found = false ; + std::vector possM(hdvf.find_pairs_M(q, found, sigma)) ; + std::cout << "-> possible pairings for M:" << std::endl ; + for (int i = 0; i 0) + { + std::cout << "chose indice of pairing (out the range = quit): " ; + std::cin >> ipair ; + if ((ipair >= 0) && (ipair < possM.size())) + { + Cell_pair p(possM.at(ipair)) ; + std::cout << "M(" << p << ")" << std::endl ; + hdvf.M(p.sigma, p.tau, p.dim) ; + // hdvf.write_matrices() ; + output_vtk(hdvf, complex) ; + } + } + } + else if (instr == std::string("W")) + { + int sigma, q ; + std::cout << "Provide sigma / q (separate with space):" << std::endl ; + std::cin >> sigma >> q ; + bool found = false ; + std::vector possW(hdvf.find_pairs_W(q, found, sigma)) ; + std::cout << "-> possible pairings for W:" << std::endl ; + for (int i = 0; i 0) + { + std::cout << "chose indice of pairing (out the range = quit): " ; + std::cin >> ipair ; + if ((ipair >= 0) && (ipair < possW.size())) + { + Cell_pair p(possW.at(ipair)) ; + std::cout << "W(" << p << ")" << std::endl ; + hdvf.W(p.sigma, p.tau, p.dim) ; + // hdvf.write_matrices() ; + output_vtk(hdvf, complex) ; + } + } + } + else if (instr == std::string("MW")) + { + int sigma, q ; + std::cout << "Provide sigma / q (separate with space):" << std::endl ; + std::cin >> sigma >> q ; + bool found = false ; + std::vector possMW(hdvf.find_pairs_MW(q, found, sigma)) ; + std::cout << "-> possible pairings for MW:" << std::endl ; + for (int i = 0; i 0) + { + std::cout << "chose indice of pairing (out the range = quit): " ; + std::cin >> ipair ; + if ((ipair >= 0) && (ipair < possMW.size())) + { + Cell_pair p(possMW.at(ipair)) ; + std::cout << "MW(" << p << ")" << std::endl ; + hdvf.MW(p.sigma, p.tau, p.dim) ; + // hdvf.write_matrices() ; + output_vtk(hdvf, complex) ; + } + } + } + else if (instr == std::string("Q")) + over = true ; + } +} + +} /* end namespace Homological_discrete_vector_field */ +} /* end namespace CGAL */ + +#endif // CGAL_HDVF_HDVF_TOOLS_H diff --git a/HDVF/include/CGAL/HDVF/Hdvf_traits_2.h b/HDVF/include/CGAL/HDVF/Hdvf_traits_2.h new file mode 100644 index 00000000000..8206d79f9c0 --- /dev/null +++ b/HDVF/include/CGAL/HDVF/Hdvf_traits_2.h @@ -0,0 +1,53 @@ +// Copyright (c) 2025 LIS Marseille (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Alexandra Bac + +#ifndef CGAL_HDVF_TRAITS_2_H +#define CGAL_HDVF_TRAITS_2_H + +#include +#include +#include +#include +#include + +namespace CGAL { +namespace Homological_discrete_vector_field { + + /*! + \ingroup PkgHDVFTraitsClasses + + The class `Hdvf_traits_2` implements the `HDVFTraits` concept for 2D data, using a geometric kernel `K`. + + @tparam K a geometric kernel model of the `Kernel` concept. + + \cgalModels{HDVFTraits} + + */ + +template +struct Hdvf_traits_2 { + using Dimension = Dimension_tag< 2 >; + typedef K Kernel; + typedef typename K::Point_2 Point; + typedef typename K::Vector_2 Vector; + typedef typename K::FT FT; + typedef CGAL::Bbox_2 Bbox; + typedef typename K::Point_3 Point3; + static std::function to_point3; +}; + +template +std::function Hdvf_traits_2::to_point3 = [](const K::Point_2& p) { return typename K::Point_3(p[0], p[1], 0); }; + +} /* end namespace Homological_discrete_vector_field */ +} /* end namespace CGAL */ + +#endif // CGAL_HDVF_TRAITS_2_H diff --git a/HDVF/include/CGAL/HDVF/Hdvf_traits_3.h b/HDVF/include/CGAL/HDVF/Hdvf_traits_3.h new file mode 100644 index 00000000000..4fab7ba82f1 --- /dev/null +++ b/HDVF/include/CGAL/HDVF/Hdvf_traits_3.h @@ -0,0 +1,53 @@ +// Copyright (c) 2025 LIS Marseille (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Alexandra Bac + +#ifndef CGAL_HDVF_TRAITS_3_H +#define CGAL_HDVF_TRAITS_3_H + +#include +#include +#include +#include +#include + +namespace CGAL { +namespace Homological_discrete_vector_field { + + /*! + \ingroup PkgHDVFTraitsClasses + + The class `Hdvf_traits_3` implements the `HDVFTraits` concept for 3D data, using a geometric kernel `K`. + + @tparam K a geometric kernel model of the `Kernel` concept. + + \cgalModels{HDVFTraits} + + */ + +template +struct Hdvf_traits_3 { + using Dimension = Dimension_tag< 3 >; + typedef K Kernel; + typedef typename K::Point_3 Point; + typedef typename K::Vector_3 Vector; + typedef typename K::FT FT; + typedef CGAL::Bbox_3 Bbox; + typedef typename K::Point_3 Point3; + static std::function to_point3; +}; + +template +std::function Hdvf_traits_3::to_point3 = [](const K::Point_3& p) { return typename K::Point_3(p); }; + +} /* end namespace Homological_discrete_vector_field */ +} /* end namespace CGAL */ + +#endif // CGAL_HDVF_TRAITS_3_H diff --git a/HDVF/include/CGAL/HDVF/Hdvf_traits_d.h b/HDVF/include/CGAL/HDVF/Hdvf_traits_d.h new file mode 100644 index 00000000000..b0277c5eb81 --- /dev/null +++ b/HDVF/include/CGAL/HDVF/Hdvf_traits_d.h @@ -0,0 +1,129 @@ +// Copyright (c) 2025 LIS Marseille (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Alexandra Bac + +#ifndef CGAL_HDVF_TRAITS_D_H +#define CGAL_HDVF_TRAITS_D_H + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace CGAL { +namespace Homological_discrete_vector_field { + + /*! + \ingroup PkgHDVFTraitsClasses + + The class `Hdvf_traits_d` implements the `HDVFTraits` concept for dD data, using a geometric kernel `K`. + + @tparam K a geometric traits class. Must be either `Epick_d` or `Epeck_d` with a fixed dimension. + + \cgalModels{HDVFTraits} + + */ + +template +struct Hdvf_traits_d { + using Dimension = Dimension_tag< K::Dimension::value >; + typedef K Kernel; + typedef typename K::Point_d Point; + typedef typename K::Vector_d Vector; + typedef typename K::FT FT; + typedef CGAL::Bbox_d Bbox; + typedef typename Exact_predicates_inexact_constructions_kernel::Point_3 Point3; + static std::function to_point3; + + // Set of standard projection operators + // R^d -> R^3 projection on first coordinates (x,y,z) + static std::function default_projection ; + + // Projection on a given 3D affine plane (defined by three unit orthogonal vectors + point) + static std::function plane_projection_builder(typename K::Point_d p0, typename K::Vector_d d1, typename K::Vector_d d2, typename K::Vector_d d3) { + typedef Exact_predicates_inexact_constructions_kernel::Point_3 Point_3; + typedef typename K::Point_d Point_d; + typedef typename K::Vector_d Vector_d; + // See NewKernel_d\include\CGAL\NewKernel_d\Kernel_d_interface.h for what is available in K + std::function project = [=](const Point_d& p) { + Vector_d tmp(K().difference_of_points_d_object()(p,p0)); + return Point_3(K().scalar_product_d_object()(tmp,d1), K().scalar_product_d_object()(tmp,d2), K().scalar_product_d_object()(tmp,d3)); + }; + return project; + } + // Define a method computing the PCA projection frame + static std::function pca_frame_builder(const std::vector& pts) { + typedef Exact_predicates_inexact_constructions_kernel::Point_3 Point_3; + typedef typename K::Point_d Point_d; + typedef typename K::Vector_d Vector_d; + typedef double FT; + typedef Eigen_vector Vector; + typedef Eigen_matrix Matrix; + + // Barycenter of points + Vector_d bary; + for (Point p : pts){ + Vector_d v = K().point_to_vector_d_object()(p) ; + bary = bary + v; + } + Vector_d v = K().scaled_vector_d_object()(bary, FT(1)/FT(pts.size())) ; + Point_d barycenter = K().vector_to_point_d_object()(v) ; + + // Define the PCA matrix by SVD + Matrix A(pts.size(), K::Dimension::value); + // Fill the matrix (p[i]-barycenter along line i) + for (int i=0; i jacobiSvd(A.eigen_object()); +#else + Eigen::JacobiSVD jacobiSvd(A.eigen_object(), ::Eigen::ComputeFullV); +#endif + + std::vector> d(3); + std::vector dirs(3); + // Fill d[i] vector + std::cerr << "Directions:" << std::endl; + for (int i=0; i<3; ++i) { + d.at(i).resize(K::Dimension::value); + for (int j=0; j +std::function Hdvf_traits_d::to_point3 = Hdvf_traits_d::default_projection; + +template +std::function Hdvf_traits_d::default_projection = + [](const K::Point_d& p) { return typename Exact_predicates_inexact_constructions_kernel::Point_3(p[0], p[1], p[2]); }; + + + + +} /* end namespace Homological_discrete_vector_field */ +} /* end namespace CGAL */ + +#endif // CGAL_HDVF_TRAITS_D_H diff --git a/HDVF/include/CGAL/HDVF/Icosphere_object_io.h b/HDVF/include/CGAL/HDVF/Icosphere_object_io.h new file mode 100644 index 00000000000..0a9e6dbadf9 --- /dev/null +++ b/HDVF/include/CGAL/HDVF/Icosphere_object_io.h @@ -0,0 +1,133 @@ +// Copyright (c) 2025 LIS Marseille (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Alexandra Bac + +#ifndef CGAL_HDVF_ICOSPHERE_OBJECT_H +#define CGAL_HDVF_ICOSPHERE_OBJECT_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace CGAL { +namespace Homological_discrete_vector_field { + +/* Class used to build an icosphere embedding a triangular mesh (for Alexander Duality) + + \tparam Traits a geometric traits class model of the `HDVFTraits` concept. +*/ + +template +class Icosphere_object_io : public Mesh_object_io +{ +public: + using Point = typename Traits::Point; + using Vector = typename Traits::Vector; + using Index=size_t ; + using Lookup=std::map, Index>; +// using Point = typename Traits::Point; + Icosphere_object_io(size_t subdivisions, const Point &c = Point({0, 0, 0}), double r=1.) : Mesh_object_io(2, vertices_ico, triangles_ico) + { + for (size_t i=0; i vertices_ico = + { + Point(-X,N,Z), Point(X,N,Z), Point(-X,N,-Z), Point(X,N,-Z), + Point(N,Z,X), Point(N,Z,-X), Point(N,-Z,X), Point(N,-Z,-X), + Point(Z,X,N), Point(-Z,X, N), Point(Z,-X,N), Point(-Z,-X, N) + }; + + inline static const std::vector triangles_ico = + { + {0,4,1},{0,9,4},{9,5,4},{4,5,8},{4,8,1}, + {8,10,1},{8,3,10},{5,3,8},{5,2,3},{2,7,3}, + {7,10,3},{7,6,10},{7,11,6},{11,0,6},{0,1,6}, + {6,1,10},{9,0,11},{9,11,2},{9,2,5},{7,2,11} + }; + + // Methods + Index vertex_for_edge(Lookup& lookup, Index first, Index second) + { + Lookup::key_type key(first, second); + if (key.first>key.second) + std::swap(key.first, key.second); + + auto inserted=lookup.insert({key, this->nodes.size()}); + if (inserted.second) + { + Point& edge0(this->nodes[first]); + Point& edge1(this->nodes[second]); + Vector v = (edge1 - ORIGIN) + (edge0 - ORIGIN); + v = v / std::sqrt(v.squared_length()); + this->add_node(ORIGIN + v); + } + + return inserted.first->second; + } + + void subdivide() + { + Lookup lookup; + std::vector result ; + + for (Io_cell_type & each:this->cells) + { + std::array mid; + std::vector each_vertex ; + for (size_t c : each) + each_vertex.push_back(c) ; + for (size_t edge=0; edge<3; ++edge) + { + mid[edge]=vertex_for_edge(lookup, + each_vertex[edge], each_vertex[(edge+1)%3]); + } + result.push_back({each_vertex[0], mid[0], mid[2]}); + result.push_back({each_vertex[1], mid[1], mid[0]}); + result.push_back({each_vertex[2], mid[2], mid[1]}); + result.push_back({mid[0], mid[1], mid[2]}); + } + this->clear_cells() ; + for (Io_cell_type c : result) + this->add_cell(c) ; + } + + void rigid_transformation(const Point &c, double r) + { + for (size_t i=0; invertices; ++i) + { + this->nodes[i] = ORIGIN + (r * (this->nodes[i] - ORIGIN)) ; + this->nodes[i] += (c - ORIGIN) ; + } + } +}; + +} /* end namespace Homological_discrete_vector_field */ +} /* end namespace CGAL */ + + +#endif // CGAL_HDVF_ICOSPHERE_OBJECT_H diff --git a/HDVF/include/CGAL/HDVF/Mesh_object_io.h b/HDVF/include/CGAL/HDVF/Mesh_object_io.h new file mode 100644 index 00000000000..9d0a3ef0f94 --- /dev/null +++ b/HDVF/include/CGAL/HDVF/Mesh_object_io.h @@ -0,0 +1,619 @@ +// Copyright (c) 2025 LIS Marseille (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Alexandra Bac + +#ifndef CGAL_HDVF_MESH_OBJECT_H +#define CGAL_HDVF_MESH_OBJECT_H + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace CGAL { +namespace Homological_discrete_vector_field { + +/** \brief Type of cells of Mesh_object_io. + * + * *Sorted* vector of the vertex indices. + */ +typedef std::vector Io_cell_type ; +/** \brief Type of pre-chains in Mesh_object_io (list of cells without coefficients). */ +typedef std::vector Io_chain_type ; + + +// ----- VTK format ----- + +// For triangular meshes +// Associated to any dimension the type number of associated VTK cells +static std::vector VTK_types_IO = {1, 3, 5, 10} ; + + +// ----- Generic ----- + +inline bool check_sanity_line(const std::string &line, const std::string &file) +{ + // Check that line is sanitized. If not, throw. + for ( size_t i = 0; i < line.size(); ++ i ) { + if ( ! ( std::isspace(line[i]) || std::isdigit(line[i]) || (line[i] == '-') || (line[i] == '.') || (line[i] == 'e')) ) { + std::cerr << "Error:\n Cannot parse file " << file << std::endl ; + std::cerr << "--- " << line[i] << std::endl ; + return false; + } + } + return true ; +} + +inline bool get_next_uncommented_line(std::ifstream &infile, std::string &result) { + while(getline(infile,result)) { + if(result.length() > 1 && result[0] != '#') { + return true; + } + } + return false; +} + +/** \brief Load nodes from a .nodes file. + * + * Load vertices coordinates from a .nodes file. + * + * \param filename Name of the input file. + * \param nodes Pointer to a vector of points into which nodes are outputed. + * \param adapt If `fill` is false, nodes must have the same dimension as the traits Point, if true, nodes dimension can be lower (and missing coordinates are filled with zeros) or higher (and coordinates are truncated to the traits dimension). + **/ + +template +inline size_t read_nodes(const std::string &filename, std::vector *nodes, bool adapt = false) +{ + typedef typename Traits::Point Point; + std::ifstream in_file (filename) ; + if ( ! in_file . good () ) { + std::cerr << "SimplicialComplex::loadFromFile. Fatal Error:\n " << filename << " not found.\n"; + throw std::runtime_error("File Parsing Error: File not found"); + } + + // First line is the number of nodes + size_t nnodes, nnodes_tmp, nodes_dim, padding, d ; + bool fill; + if ( ! in_file.eof()) + { + std::string line; + getline( in_file, line ); + check_sanity_line(line, filename) ; + // First number is the number of nodes, then dimension of nodes + std::istringstream is (line); + is >> nnodes ; + is >> nodes_dim ; + if (!adapt) { + if (nodes_dim != Traits::Dimension::value){ + std::cerr << "read_nodes error: dimension of nodes incompatible with Traits::Dimension" << std::endl; + throw("read_nodes error: dimension of nodes incompatible with Traits::Dimension"); + } + } + else { + if (nodes_dim < Traits::Dimension::value){ + fill = true; + padding = Traits::Dimension::value - nodes_dim; + } + else + fill = false; + } + d = (nodes_dim <= Traits::Dimension::value)?nodes_dim:Traits::Dimension::value; + } + nnodes_tmp = nnodes ; + while ( !(in_file.eof()) && (nnodes_tmp>0)) + { + size_t trash ; + double x ; + std::vector node ; + --nnodes_tmp ; + std::string line; + getline( in_file, line ); + check_sanity_line(line, filename) ; + std::istringstream is (line); + is >> trash ; + for (int i = 0; i> x ; + node.push_back(x) ; + } + + if constexpr (Traits::Dimension::value == 2){ + nodes->push_back(Point(typename Traits::FT(node[0]), typename Traits::FT(node[1]))) ; + }else if constexpr (Traits::Dimension::value == 3){ + if ((padding <= 0) || (!adapt)) + nodes->push_back(Point(typename Traits::FT(node[0]), typename Traits::FT(node[1]), typename Traits::FT(node[2]))) ; + else + nodes->push_back(Point(typename Traits::FT(node[0]), typename Traits::FT(node[1]), 0.)) ; + }else{ + std::vector res(Traits::Dimension::value) ; + for (int i=0; ipush_back(Point(res.begin(), res.end())); + } + } + in_file.close() ; + return nnodes; +} + +/*! + \ingroup PkgHDVFRef + + The class `Mesh_object_io` is an intermediate IO class, used to load triangular/tetraedral meshes and produce simplicial complexes. + + \tparam Traits a geometric traits class model of the `HDVFTraits` concept. + */ + +// Generic Mesh_object_io class - for 3D triangular meshes +template +class Mesh_object_io +{ + public: + typedef typename Traits::Point Point ; + typedef typename Traits::Bbox Bbox ; +private: + // Write vtk file + template + void write_vtk(const std::string &filename, const std::vector &nodes, const std::vector &chains, const std::vector *labels=NULL, const std::string scalar_type="none") + { + bool with_scalars = (labels != NULL) ; + // Load ... + std::ofstream out ( filename, std::ios::out | std::ios::trunc); + + if ( !out . good () ) { + std::cerr << "SimplicialComplex::loadFromFile. Fatal Error:\n " << filename << " not found.\n"; + throw std::runtime_error("File Parsing Error: File not found"); + } + + // Header + out << "# vtk DataFile Version 2.0" << std::endl ; + out << "generators" << std::endl ; + out << "ASCII" << std::endl ; + out << "DATASET UNSTRUCTURED_GRID" << std::endl ; + + // Points + size_t nnodes = nodes.size() ; + out << "POINTS " << nnodes << " double" << std::endl ; + for (Point n : nodes) { + out << Traits::to_point3(n); + out << std::endl ; + } + + // Cells + // Number of cells : for each chain, each number of vertices for each cell + // Size : size of a cell defined by d vertices : d+1 + size_t ncells_tot = 0, size_cells_tot = 0 ; + for (size_t i = 0; i types ; + std::vector scalars ; + for (size_t i = 0; iat(i)) ; + } + } + + // CELL_TYPES + out << "CELL_TYPES " << ncells_tot << std::endl ; + for (size_t t : types) + out << t << " " ; + out << std::endl ; + + if (with_scalars) + { + // CELL_TYPES + out << "CELL_DATA " << ncells_tot << std::endl ; + out << "SCALARS CriticalCellsId " << scalar_type << " 1" << std::endl ; + out << "LOOKUP_TABLE default" << std::endl ; + for (CoefficientRing s : scalars) + out << s << " " ; + out << std::endl ; + } + out.close() ; + } + +public: + // The variable `dim` is used to encode both the dimension of the object loaded and whether it encodes a complex (with cells of various dimensions) or a mesh (a collection of triangles) + // - if `dim` > 0 : `Mesh_object_io` encodes a mesh and all cells have dimension `d` + // - if `dim` < 0 : `Mesh_object_io` encodes a complex (possibly incomplete) of dimension `d` + int dim = 0 ; + size_t nvertices, ncells, nedges ; + std::vector nodes ; // Coordinates of vertices (optional) + std::vector cells ; + + /* \brief Default constructor. + * + * Create an empty Mesh_object_io. + */ + Mesh_object_io(int d = 0) : dim(d), nvertices(0), ncells(0), nedges(0) {} + + /** \brief Constructor from a vector of Point (vertex coordinates) and a vector of simplices. + * + * Simplices are described by the list of vertex indices. + * + * \param d The dimension `d` can be positive or negative: + * - if positive: the set of simplicial cells loaded is a "mesh" and all cells have the same dimension + * - if negative: the set of simplicial cells loaded have various dimensions and `d` must be the maximum of these dimensions. + * \param vnodes Vector of vertex coordinates. + * \param vcells Vector of cells (described by a sorted vector of indices) + * \param sort_data If `true` the vectors of vertex indices are sorted, if `false` they are assumed to be sorted (faster). + */ + Mesh_object_io(int d, const std::vector &vnodes, const std::vector &vcells, bool sort_data = false) : dim(d), nvertices(vnodes.size()), ncells(vcells.size()), nedges(0), nodes(vnodes), cells(vcells) { + check_dimension() ; + if (sort_data) { + // Sort the vector encoding each cell + for (int i=0; i get_nodes () const + { + return nodes ; + } + + // Mesh operations + // Add a Mesh_object_io to the current Mesh_object_io + void push_back(const Mesh_object_io &mesh) + { + size_t off = nvertices ; // The index of all the cells of mesh has to be incremented by off + nvertices += mesh.nvertices ; + ncells += mesh.ncells ; + // Append all the vertices + for (size_t i=0; i> nvertices >> ncells >> nedges; + + nodes.resize(nvertices) ; + for(auto i=0; i < nvertices; ++i) { + if (!get_next_uncommented_line(infile,info)) { + return false; + } + std::istringstream info_stream(info); + std::array p; + info_stream >> p[0] >> p[1] >> p[2] ; + nodes[i] = Point(p[0], p[1], p[2]) ; + } + + // 4 - the actual faces + cells.resize(ncells); + for(auto i=0; i < ncells; ++i) { + if (!get_next_uncommented_line(infile,info)) { + return false; + } + std::istringstream info_stream(info); + unsigned long n; + unsigned long index; + info_stream >> n; + Io_cell_type c ; + // Read vertex indices + for (auto j = 0; j < n; ++j) { + info_stream >> index; + c.push_back(index) ; + } + // Sort the vector + std::sort(c.begin(), c.end()); + // Insert the cell + cells[i] = c ; + dim = (c.size()-1>dim)?c.size()-1:dim ; + } + + infile.close(); + return true; + } + + bool write_off(const std::string &filename) const + { + // 0 - open input file + std::ofstream outfile(filename); + if(!outfile.is_open()) { + std::cerr << "Warning: cannot open file " << filename << std::endl ; + // failed to open the file + return false; + } + // 1 - header + outfile << "OFF" << std::endl ; + outfile << nvertices << " " << cells_of_dim(2) << " " << nedges << std::endl ; + // 2 - nodes + for (size_t i=0; i chains{cells} ; + write_vtk(filename, nodes, chains) ; + } + + // SIMP + bool write_simp(const std::string &filename) + { + // 0 - open input file + std::ofstream outfile(filename); + if(!outfile.is_open()) { + std::cerr << "Warning: cannot open file " << filename << std::endl ; + // failed to open the file + return false; + } + // 1 - write cells + for (size_t i=0; i " << line << "\n"; + throw std::runtime_error("File Parsing Error: Invalid file"); + } + } + Io_cell_type cell ; + std::istringstream is( line ); + size_t v; + // Read vertex indices + while ( is >> v ) + cell.push_back(v); + // Sort the vector of indices + std::sort(cell.begin(), cell.end()); + // Add this simplex to cells + if (!(cell.empty())) + { + ++ncells ; + cells.push_back(cell) ; + const int dcell = cell.size()-1 ; + if (dcell > d) + d = dcell ; + } + } + dim = d ; + infile.close() ; + return true ; + } + + bool read_nodes_file(const std::string &filename) ; + + std::ostream& print_infos (std::ostream& out_stream = std::cout) const + { + out_stream << "Mesh_object_io infos - dim : "<< dim << ", nodes : " << nodes.size() << ", cells : " << cells.size() << std::endl ; + for (int q = 0; q <= dim; ++q) + out_stream << "cells of dim " << q << " : " << cells_of_dim(q) << std::endl ; + return out_stream; + } + + + friend std::ostream& operator<<(std::ostream& out_stream, const Mesh_object_io& mesh_io) { + return mesh_io.print_infos(out_stream); + } + + // Mesh computations + Point centroid() const + { + return CGAL::centroid(nodes.begin(), nodes.end()) ; + } + + double radius(const Point &bary) const + { + double r = 0 ; + for (Point v : nodes) + { + r = (std::max) (r, sqrt(squared_distance(v,bary))) ; + } + return r ; + } + + Bbox bbox(double ratio=1.) const + { + return bounding_box(nodes.begin(), nodes.end()) .bbox(); + // @todo deal with ratio + } +private: + void check_dimension() + { + if (dim > 0) // exact dimension + { + for (Io_cell_type cell : cells) + { + if (cell.size() != (dim+1)) + throw "Mesh has a cell of inconsistent dimension" ; + } + } + else // max dim + { + for (Io_cell_type cell : cells) + { + if ((cell.size() > (-dim+1))) + throw "Mesh has a cell of inconsistent dimension" ; + } + } + } +} ; + + + + +} /* end namespace Homological_discrete_vector_field */ +} /* end namespace CGAL */ + + +#endif // CGAL_HDVF_MESH_OBJECT_H diff --git a/HDVF/include/CGAL/HDVF/Simplex.h b/HDVF/include/CGAL/HDVF/Simplex.h new file mode 100644 index 00000000000..2bab2e2ff9d --- /dev/null +++ b/HDVF/include/CGAL/HDVF/Simplex.h @@ -0,0 +1,152 @@ + +// Copyright (c) 2025 LIS Marseille (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Alexandra Bac + + +#ifndef CGAL_HDVF_SIMPLEX_H +#define CGAL_HDVF_SIMPLEX_H + +#include + +#include +#include +#include +#include +#include + +namespace CGAL { +namespace Homological_discrete_vector_field { + +/*! + \ingroup PkgHDVFRef + + The class `Simplex` is used by the class `Abstract_simplicial_chain_complex` to represent a simplex (i.e.\ cells of a simplicial complex). + + Simplices are described by the *ordered vector* of the indices of their vertices (see the documentation of `Abstract_simplicial_chain_complex` for examples). + + */ + +class Simplex { + // Friend class: Abstract_simplicial_chain_complex + template + friend class Abstract_simplicial_chain_complex ; + +public: + /** \brief Type of simplices representation. + * + * Simplices are stored as a sorted vector of vertex indices (a simplex of dimension \f$q\f$ has \f$q+1\f$ vertices and the vector must be **sorted**). + **/ + typedef std::vector Simplex_data; +private: + // Indices of the simplex vertices - sorted vector + Simplex_data _vertices; + + public: + /** \brief Constructor from a vector of vertex indices. + * + * \param vertices Vector of the simplex vertex indices (a simplex of dimension \f$q\f$ has \f$q+1\f$ vertices and the vector must be **sorted**). + * \param sort_data If `true` vectors of vertex indices are sorted, if `false` indices are assumed to be sorted (faster). + */ + Simplex(const Simplex_data& vertices, bool sort_data = false) : _vertices(vertices) { + if (sort_data) + std::sort(_vertices.begin(), _vertices.end()); + } + + /** \brief Constant iterator over the vertices of a simplex. */ + typedef Simplex_data::const_iterator const_iterator ; + + /** \brief Returning a constant iterator on the first vertex of the simplex. */ + const_iterator cbegin () + { + return _vertices.cbegin() ; + } + /** \brief Returning a constant past-the-end iterator of the simplex. */ + const_iterator cend () { return _vertices.cend() ; } + + /** \brief Returns the dimension of a simplex. + * + * A simplex of dimension \f$q\f$ has \f$q+1\f$ vertices. + */ + int dimension() const + { + return _vertices.size() - 1; + } + + /** \brief Returns the boundary of a simplex. + * + * The method returns the vector of the simplices in the boundary of the object. + */ + + std::vector boundary() const + { + std::vector result; + result.reserve(_vertices.size()); + + auto it = _vertices.begin(); + for (size_t i = 0; i < _vertices.size(); ++i) { + Simplex_data simplex_vertices; + std::copy_if(_vertices.begin(), _vertices.end(), std::inserter(simplex_vertices, simplex_vertices.begin()), + [it](const size_t& vertex) { return vertex != *it; }); + result.emplace_back(simplex_vertices); + ++it; + } + + return result; + } + + /** \brief Gets the set of vertex indices of the simplex. */ + const Simplex_data& vertices() const + { + return _vertices ; + } + + /** \brief Comparison operator. + * + * Compare the object with another simplex according to the lexicographical order on vertex indices sets. + * + * \param other Compare `this` with `other` (returns `this < other`). + */ + bool operator<(const Simplex& other) const { + return _vertices < other._vertices; + } + + /** \brief Equality operator. + * + * As vertex indices must be store in increasing order, comparison just comes to compare the ordered vector of indices. + */ + bool operator==(const Simplex& other) const { + return _vertices == other._vertices; + } + + + /** \brief Outputs a simplex. + */ + friend std::ostream& operator<<(std::ostream& out, const Simplex& simplex) + { + out << "<"; + bool first = true; + for (size_t vertex : simplex._vertices) { + if (!first) { + + out << " "; + } + out << vertex; + first = false; + } + out << ">"; + return out; + } +}; + +} /* end namespace Homological_discrete_vector_field */ +} /* end namespace CGAL */ + +#endif // CGAL_HDVF_SIMPLEX_H diff --git a/HDVF/include/CGAL/HDVF/Simplicial_chain_complex.h b/HDVF/include/CGAL/HDVF/Simplicial_chain_complex.h new file mode 100644 index 00000000000..c01dbe7f88f --- /dev/null +++ b/HDVF/include/CGAL/HDVF/Simplicial_chain_complex.h @@ -0,0 +1,352 @@ +// Copyright (c) 2025 LIS Marseille (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Alexandra Bac + +#ifndef CGAL_HDVF_SIMPLICIAL_CHAIN_COMPLEX_H +#define CGAL_HDVF_SIMPLICIAL_CHAIN_COMPLEX_H + +#include + +#include +#include +#include +#include +#include +#include + +namespace CGAL { +namespace Homological_discrete_vector_field { + +// Forward declaration of SimpComplexTools +template class Duality_simplicial_complex_tools ; + +/*! + \ingroup PkgHDVFRef + + The class `Simplicial_chain_complex` refines the `Abstract_simplicial_chain_complex` class by assigning coordinates to vertices (i.e.\ 0-simplices). Hence, vtk output is available. + + \cgalModels{GeometricChainComplex} + + \tparam CoefficientRing a model of the `IntegralDomainWithoutDivision` concept. + + \tparam Traits a geometric traits class model of the `HDVFTraits` concept. + */ + +template +class Simplicial_chain_complex : public Abstract_simplicial_chain_complex { +public: + + /** \brief Type of vertex coordinates */ + typedef typename Traits::Point Point ; + /** \brief Type of vtk export vertex coordinates */ + typedef typename Traits::Point3 Point3 ; +// typedef CGAL::Simple_cartesian::Point_3 Point3; + +protected: + /** \brief Vector of vertex coordinates */ + std::vector _coords ; + +private: + /** \brief Vector of VTK types associated to cells in each dimension + e.g. {1, 3, 5, 10} */ + static const std::vector VTK_simptypes ; + +public: + + /** + * \brief Default constructor: builds an empty simplicial complex. + */ + Simplicial_chain_complex() {} ; + // Constructor from a simplicial complex stored in a .simp file and read into a MeshObject + /** + * \brief Constructor from a Mesh_object_io. + * + * Builds a simplicial complex from a Mesh_object_io describing maximal faces and vertex coordinates. + */ + Simplicial_chain_complex(const Mesh_object_io& mesh) : Abstract_simplicial_chain_complex(mesh), _coords(mesh.get_nodes()) {} ; + + /** + * \brief Assignment operator for simplicial complexes. + * + * Stores a copy of a simplicial complex in *this. + * + * \param complex The simplicial complex which will be copied. + */ + Simplicial_chain_complex& operator= (const Simplicial_chain_complex& complex) + { + this->Abstract_simplicial_chain_complex::operator=(complex) ; + _coords = complex._coords ; + return *this ; + } + + /** \brief Gets the vector of vertex coordinates */ + const std::vector& points() const + { + return _coords ; + } + + /** \brief Gets the coordinates of the ith dimension-0 simplex + + * \warning This does not come to return vertex indices, as dimension 0 simplices enumerate vertices in any order. For instance, if an abstract simplicial complex is build from 3 vertices {1,2,3} such that the enumeration of dimension 0 simplices is: + * id0: 3, id1 : 2, id2: 1 + * then the bottom_faces of the 1-simplex {1,2} are two 0-simplices with id 2 and 1. + */ + Point point(size_t i) const + { + const Simplex simpl(this->_ind2simp.at(0).at(i)) ; + const std::vector verts(simpl.vertices()) ; + const size_t id(*(verts.cbegin())) ; + return _coords.at(id); + } + + // VTK export + + /* + * \brief Exports a simplicial complex (plus, optionally, labels) to a VTK file. + * + * The method generates legacy text VTK files. Labels are exported as such in a VTK property, together with CellID property, containing the index of each cell. + * + * \tparam LabelType Type of labels provided (default: int). + * + * \param K Simplicial complex exported. + * \param filename Output file root (output filenames will be built from this root). + * \param labels Pointer to a vector of labels in each dimension. (*labels).at(q) is the set of integer labels of cells of dimension q. If labels is NULL, only CellID property is exported. + * \param label_type_name Typename used in vtk export (e.g. "int" or "unsigned_long", see VTK manual ). + */ + template + static void chain_complex_to_vtk(const Simplicial_chain_complex &K, const std::string &filename, const std::vector > *labels=NULL, std::string label_type_name = "int") + { + typedef Simplicial_chain_complex ChainComplex; + if (K._coords.size() != K.number_of_cells(0)) + { + std::cerr << "SimpComplex_to_vtk. Error, wrong number of points provided.\n"; + throw std::runtime_error("Geometry of points invalid."); + } + + bool with_scalars = (labels != NULL) ; + + // Load out file... + std::ofstream out ( filename, std::ios::out | std::ios::trunc); + + if ( ! out . good () ) { + std::cerr << "SimpComplex_to_vtk. Fatal Error:\n " << filename << " not found.\n"; + throw std::runtime_error("File Parsing Error: File not found"); + } + + // Header + out << "# vtk DataFile Version 2.0" << std::endl ; + out << "generators" << std::endl ; + out << "ASCII" << std::endl ; + out << "DATASET UNSTRUCTURED_GRID" << std::endl ; + + // Points + size_t nnodes = K._coords.size() ; + out << "POINTS " << nnodes << " double" << std::endl ; + const std::vector& coords(K.points()) ; + for (size_t n = 0; n < nnodes; ++n) + { + Point3 p(Traits::to_point3(coords.at(n))) ; + out << p ; + out << std::endl ; + } + + size_t ncells_tot = 0, size_cells_tot = 0 ; + std::vector types ; + std::vector scalars ; + std::vector ids ; + // all cells must be printed + { + // Cells up to dimension 3 + // Size : size of a cell of dimension q : q+1 + for (int q=0; q<=K._dim; ++q) + { + ncells_tot += K.number_of_cells(q) ; + const size_t size_cell = q+1 ; + size_cells_tot += (size_cell+1)*K.number_of_cells(q) ; + } + out << "CELLS " << ncells_tot << " " << size_cells_tot << std::endl ; + // Output cells by increasing dimension + for (int q=0; q<=K._dim; ++q) + { + const size_t size_cell = q+1 ; + for (size_t id =0; id < K.number_of_cells(q); ++id) + { + Simplex verts(K._ind2simp.at(q).at(id)) ; + out << size_cell << " " ; + for (typename Simplex::const_iterator it = verts.cbegin(); it != verts.cend(); ++it) + out << *it << " " ; + out << std::endl ; + types.push_back(ChainComplex::VTK_simptypes.at(q)) ; + if (with_scalars) + { + scalars.push_back((*labels).at(q).at(id)) ; + ids.push_back(id) ; + } + } + } + + // CELL_TYPES + out << "CELL_TYPES " << ncells_tot << std::endl ; + for (int t : types) + out << t << " " ; + out << std::endl ; + } + + if (with_scalars) + { + // CELL_LABEL + out << "CELL_DATA " << ncells_tot << std::endl ; + out << "SCALARS Label " << label_type_name << " 1" << std::endl ; + out << "LOOKUP_TABLE default" << std::endl ; + for (LabelType s : scalars) + out << s << " " ; + out << std::endl ; + // CELL_IDs + out << "SCALARS CellId " << "int" << " 1" << std::endl ; + out << "LOOKUP_TABLE default" << std::endl ; + for (size_t i : ids) + out << i << " " ; + out << std::endl ; + } + out.close() ; + } + + /* + * \brief Exports a chain over a simplicial complex to a VTK file. + * + * The method generates legacy text VTK files. All the cells of the chain with non zero coefficient are exported. If a cellId is provided, labels are exported in a VTK property (2 for all cells, 0 for cell of index cellId). The index of each cell is exported in a CellID property. + * + * \param K Simplicial complex exported. + * \param filename Output file root (output filenames will be built from this root). + * \param chain Sparse_chain exported (all the cells with non-zero coefficients in the chain are exported to vtk). + * \param q Dimension of the cells of the chain. + * \param cellId If cellID is not -1 (that is MAX_SIZE_T), labels are exported to distinguish cells of the chain (label 2) from cellId cell (label 0). + */ + static void chain_to_vtk(const Simplicial_chain_complex &K, const std::string &filename, const OSM::Sparse_chain& chain, int q, size_t cellId = -1) ; +}; + +// Initialization of static VTK_simptypes +template const +std::vector Simplicial_chain_complex::VTK_simptypes({1, 3, 5, 10}); + + +// chain_to_vtk +template +void Simplicial_chain_complex::chain_to_vtk(const Simplicial_chain_complex &K, const std::string &filename, const OSM::Sparse_chain& chain, int q, size_t cellId) +{ + typedef Simplicial_chain_complex ChainComplex ; + if (K._coords.size() != K.number_of_cells(0)) + { + std::cerr << "SimpComplex_chain_to_vtk. Error, wrong number of points provided.\n"; + throw std::runtime_error("Geometry of points invalid."); + } + + bool with_scalars = (cellId != -1) ; + + // Load out file... + std::ofstream out ( filename, std::ios::out | std::ios::trunc); + + if ( ! out . good () ) { + std::cerr << "SimpComplex_chain_to_vtk. Fatal Error:\n " << filename << " not found.\n"; + throw std::runtime_error("File Parsing Error: File not found"); + } + + // Header + out << "# vtk DataFile Version 2.0" << std::endl ; + out << "generators" << std::endl ; + out << "ASCII" << std::endl ; + out << "DATASET UNSTRUCTURED_GRID" << std::endl ; + + // Points + size_t nnodes = K._coords.size() ; + out << "POINTS " << nnodes << " double" << std::endl ; + const std::vector& coords(K.points()) ; + for (size_t n = 0; n < nnodes; ++n) + { + Point3 p(Traits::to_point3(coords.at(n))) ; + out << p ; + out << std::endl ; + } + + size_t ncells_tot = 0, size_cells_tot = 0 ; + std::vector types ; + std::vector scalars ; + std::vector ids ; + + // output only cells of the chain (dimension q) + { + // 1 - Compute the number of cells / size of encoding + { + const size_t size_cell = q+1 ; + for (size_t id =0; id < K.number_of_cells(q); ++id) + { + if (!chain.is_null(id)) + { + ++ncells_tot; + size_cells_tot += (size_cell+1) ; + } + } + } + // 2 - Output cells + out << "CELLS " << ncells_tot << " " << size_cells_tot << std::endl ; + + { + const size_t size_cell = q+1 ; + for (size_t id =0; id < K.number_of_cells(q); ++id) + { + if (!chain.is_null(id)) + { + Simplex verts(K._ind2simp.at(q).at(id)) ; + out << size_cell << " " ; + for (typename Simplex::const_iterator it = verts.cbegin(); it != verts.cend(); ++it) + out << *it << " " ; + out << std::endl ; + types.push_back(ChainComplex::VTK_simptypes.at(q)) ; + if (with_scalars) + { + ids.push_back(id) ; + if (id != cellId) + scalars.push_back(2) ; + else + scalars.push_back(0) ; + } + } + } + } + // CELL_TYPES + out << "CELL_TYPES " << ncells_tot << std::endl ; + for (int t : types) + out << t << " " ; + out << std::endl ; + } + + if (with_scalars) + { + // CELL_TYPES + out << "CELL_DATA " << ncells_tot << std::endl ; + out << "SCALARS Label " << "int" << " 1" << std::endl ; + out << "LOOKUP_TABLE default" << std::endl ; + for (int s : scalars) + out << s << " " ; + out << std::endl ; + // CELL_IDs + out << "SCALARS CellId " << "int" << " 1" << std::endl ; + out << "LOOKUP_TABLE default" << std::endl ; + for (size_t i : ids) + out << i << " " ; + out << std::endl ; + } + out.close() ; +} + +} /* end namespace Homological_discrete_vector_field */ +} /* end namespace CGAL */ + +#endif // CGAL_HDVF_SIMPLICIAL_CHAIN_COMPLEX_H diff --git a/HDVF/include/CGAL/HDVF/Sub_chain_complex_mask.h b/HDVF/include/CGAL/HDVF/Sub_chain_complex_mask.h new file mode 100644 index 00000000000..f867638fcf2 --- /dev/null +++ b/HDVF/include/CGAL/HDVF/Sub_chain_complex_mask.h @@ -0,0 +1,325 @@ +// Copyright (c) 2025 LIS Marseille (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Alexandra Bac + +#ifndef CGAL_HDVF_SUB_CHAIN_COMPLEX_MASK_H +#define CGAL_HDVF_SUB_CHAIN_COMPLEX_MASK_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace CGAL { +namespace Homological_discrete_vector_field { + +/*! + \ingroup PkgHDVFRef + + The class `Sub_chain_complex_mask` is a technical class implementing a sub chain complex. A sub chain complex \f$A\f$ of a chain complex \f$K\f$ is a subset \f$A\subseteq K\f$ such that the restricted boundary operator \f$\partial_A = \partial_K|_A\f$ still satisfies \f$\partial_A^2 = 0\f$. + + The `Sub_chain_complex_mask` class is used to compute reduced homology. This class is based on a set of bitboard masks (one in each dimension) used to define sub chain complexes and their associated reduction encoded in sub-sparse matrices (`OSM::Sub_sparse_matrix` class). Technically, `Sub_chain_complex_mask` are used to partially screen chain complexes and chains in associated boundary matrices, and hence compute homology "locally". + + \warning For efficiency reasons, when a `Sub_chain_complex_mask` is used to screen sparse matrices (with the `screen_matrices()` method), screening is **only** performed on the major direction of matrices (thus column-major matrices are restricted over columns and row-major matrices are restricted over rows). Iterators are restricted accordingly. But chains themselves are not restricted.
+ However, if `A` is a proper sub-complex of `K` (that is, closed with respect to faces), chains automatically comply with the screening. Indeed, for any \f$q\f$-cell \f$\sigma\in A\f$ (thus the corresponding bit is on in the mask), all the faces of \f$\sigma\f$ also belong to \f$A\f$. Thus for any \f$q-1\f$-cell \f$\tau\f$ in the boundary of \f$\sigma\f$ (that is, such that \f$\langle\partial_k(\sigma),\tau\rangle\neq 0\f$), \f$\tau\f$ belongs to \f$A\f$ (and thus the corresponding bit in the mask is also on). + + \tparam ChainComplex a model of the `AbstractChainComplex` concept (type of the chain complex screened by `Sub_chain_complex_mask`). + */ + +template +class Sub_chain_complex_mask +{ +public: + /*! \brief Chain complex type */ + typedef ChainComplex Chain_complex; + + /*! \brief Type of coefficients used to compute homology. */ + typedef typename Chain_complex::Coefficient_ring Coefficient_ring; + +protected: + /** \brief Dimension of the underlying complex. */ + int _dim ; + /** \brief Vector of bitboards coding masks in each dimension. */ + std::vector _sub ; + /** \brief Number of cells in the mask in each dimension. */ + std::vector _nb_cells ; + /** \brief Constant reference to the underlying complex. */ + const ChainComplex& _K ; + /** \brief Is full sub_complex. + * This boolean flag is true is all bits in the bitboards are on. */ + bool _full ; +private: + /** Load a set of cells and close the enumeration down. + * + * The method activates bits corresponding to the set of cells encoded by `cells` and recursively activates all bits corresponding to their faces. + * + * \param cells A vector containing, in each dimension, a vector of cell indices. + */ + void down_closure (const std::vector > &cells) + { + std::vector > faces(cells.size()) ; + bool rec_needed = false ; + for (int q = 0; q < cells.size(); ++q) + { + for (int i : cells.at(q)) + { + if (!_sub.at(q).is_on(i)) + { + _sub.at(q).set_on(i) ; + ++_nb_cells.at(q) ; + if (q>0) // Then faces must be considered + { + // Add all its faces to faces.at(q-1) + OSM::Sparse_chain bnd(_K.d(i,q)) ; + for (typename OSM::Sparse_chain::const_iterator it = bnd.cbegin(); it != bnd.cend(); ++it) + { + const int c(it->first) ; + if (!_sub.at(q-1).is_on(c)) + { + faces.at(q-1).push_back(c) ; + rec_needed = true ; + } + } + } + } + } + } + // Call recursively + if (rec_needed) + down_closure(faces, _K); + } + + /** Load a set of cells (without closure). + * + * The method activates bits corresponding to the set of cells encoded by `cells`. + * + * \param cells A vector giving, for each dimension, a vector of cell indices. + */ + void set_cells (const std::vector > &cells) + { + for (int q = 0; q < cells.size(); ++q) + { + for (int i : cells.at(q)) + { + _sub.at(q).set_on(i) ; + ++_nb_cells.at(q) ; + } + } + } + +public: + /** \brief Constructor from a complex. + * + * Build a `Sub_chain_complex_mask` associated to `K` with all bits set to 1 in the masks if `full` is true, and all bits set to 0 otherwise. + * + * \param K A constant reference to the underlying complex. + * \param full Build full / empty masks (default: full). + */ + Sub_chain_complex_mask(const Chain_complex& K, bool full=true) : _K(K) + { + _dim = K.dimension() ; + _sub.resize(_dim+1) ; + _nb_cells.resize(_dim+1) ; + // Create Bitboards + for (int q=0; q<=K.dimension(); ++q) + { + if (full) + { + _sub.at(q) = OSM::Bitboard(K.number_of_cells(q),false) ; // all cells to 1 + _full = true ; + } + else + _sub.at(q) = OSM::Bitboard(K.number_of_cells(q),true) ; // all cells to 0 + _nb_cells.at(q) = K.number_of_cells(q) ; + } + } + + + /** \brief Constructor from an enumeration of cells. + * + * Build masks associated to the underlying complex `K` with all bits corresponding to `cells` (and their faces if `close` is true) set to 1. + * + * \param K A constant reference to the underlying complex. + * \param cells A vector containing, in each dimension, a vector of cell indices. + * \param close If this boolean is true, the faces of `cells` are also set to 1. + */ + Sub_chain_complex_mask(const Chain_complex& K, const std::vector > &cells, bool close = true) : _K(K) + { + _dim = K.dimension() ; + _sub.resize(_dim+1) ; + _nb_cells.resize(_dim+1) ; + _full = true ; + // Create Bitboards + for (int q=0; q<=K.dimension(); ++q) + { + _sub.at(q) = OSM::Bitboard(K.number_of_cells(q)) ; + } + + // Set bits + if (close) + down_closure(cells, K) ; + else + set_cells(cells, K) ; + + for (int q=0; q<=K.dimension(); ++q) + { + _full = (_full && (_nb_cells.at(q) == _sub.at(q).size())) ; + } + } + + /** + * \brief Constructor by copy. + * + * Builds a `Sub_chain_complex_mask` by copy from another. + * + * \param otherToCopy An initial `Sub_chain_complex_mask`. + */ + Sub_chain_complex_mask(const Sub_chain_complex_mask& otherToCopy) : _K(otherToCopy._K) + { + _dim = otherToCopy._dim ; + _nb_cells = otherToCopy._nb_cells ; + _full = otherToCopy._full ; + _sub = otherToCopy._sub ; + } + + /** \brief Assignment operator + * + * \warning The operator argument must provide a sub chain complex mask over the *same underlying chain complex*. It not so, the assignment will throw an exception. + * + * \param otherToCopy A `Sub_chain_complex_mask` copied into `this` (`otherToCopy` and `this` must have the same underlying chain complex). + */ + Sub_chain_complex_mask& operator= (const Sub_chain_complex_mask& otherToCopy) + { + // Check that otherToCopy and current mask share the same chain complex + if (_K.get_id() != otherToCopy._K.get_id() ) + throw("Error, operator= can only copy mask over the same chain complex."); + + // Perform assignment + _dim = otherToCopy._dim ; + _sub = otherToCopy._sub ; + _nb_cells = otherToCopy._nb_cells ; + _full = otherToCopy._full ; + return *this; + } + + /** \brief Returns the complement of the mask. + * + * The method return a new `Sub_chain_complex_mask` containing the complement of the current mask (0 and 1 bits in the mask are exchanged). + */ + Sub_chain_complex_mask complement() + { + Sub_chain_complex_mask cSub(*this) ; + // Complement bitboards + for (int q=0; q<=_dim; ++q) + { + cSub._sub.at(q).bit_not() ; + cSub._nb_cells.at(q) = _sub.at(q).size() - _nb_cells.at(q) ; + cSub._full = ~_full ; + } + return cSub ; + } + + /** \brief Gets a bit of the mask (bit i in dimension q). */ + inline bool get_bit(int q, int i) const + { + return _sub.at(q).is_on(i) ; + } + + /** \brief Sets a bit to 1 (bit i in dimension q). */ + inline void set_bit_on(int q, int i) + { + if (!_sub.at(q).is_on(i)) + { + _nb_cells[q]++ ; + _sub.at(q).set_on(i) ; + } + } + + /** \brief Sets a bit to 0 (bit i in dimension q). */ + inline void set_bit_off(int q, int i) + { + if (_sub.at(q).is_on(i)) + { + _nb_cells[q]--; + _sub.at(q).set_off(i) ; + } + } + + /** \brief Gets the bitboards of the sub chain complex. + * + * Returns a constant reference to the vector of bitboards in each dimension. + */ + inline const std::vector& get_bitboard() const + { + return _sub ; + } + + /** \brief Gets the bitboard of the sub chain complex in dimension q. */ + inline const OSM::Bitboard& get_bitboard(int q) const + { + return _sub.at(q) ; + } + + /** \brief Screens a sequence of `OSM::Sub_sparse_matrix` (in each dimension). + * + * Given a sequence of matrices (vector of `OSM::Sub_sparse_matrices`) sets the masks of `Sub_sparse_matrices` in each dimension to the current `Sub_chain_complex_mask`. + * + * \warning For efficiency, screening is performed on the major direction of matrices (so along columns for column-major matrices and along row for row-major matrices). + * + * \param matrices A vector of `OSM::Sub_sparse_matrix` (in each dimension). + */ + template + void screen_matrices(std::vector >& matrices) + { + for (int q=0; q<=_K.dimension(); ++q) + { + matrices.at(q).set_sub(_sub.at(q)) ; + } + } + + /** + * \brief Restricts a chain in a given dimension to the sub chain complex mask. + * + * Nullify all coefficients out of the mask. + * + * \param chain The chain restricted. + * \param q Dimension of the chain. + */ + template + inline void screen_chain(OSM::Sparse_chain& chain, int q) + { + std::vector indices; + for (typename OSM::Sparse_chain::iterator it = chain.begin(); it != chain.end(); ++it) + { + if (!get_bit(q, it->first)) + indices.push_back(it->first); + } + chain/=indices; + } + + /*! \brief Overload of the `<<`operator for `Sub_chain_complex_mask`. + */ + friend std::ostream & operator << (std::ostream & out, const Sub_chain_complex_mask & sub) + { + for (int q = 0; q <= sub._dim; ++q) + out << "dim " << q << " : " << sub._sub.at(q) << std::endl ; + return out ; + } +}; + +} /* end namespace Homological_discrete_vector_field */ +} /* end namespace CGAL */ + +#endif // CGAL_HDVF_SUB_CHAIN_COMPLEX_MASK_H diff --git a/HDVF/include/CGAL/HDVF/Sub_sparse_matrix.h b/HDVF/include/CGAL/HDVF/Sub_sparse_matrix.h new file mode 100644 index 00000000000..0beebf2ad8c --- /dev/null +++ b/HDVF/include/CGAL/HDVF/Sub_sparse_matrix.h @@ -0,0 +1,197 @@ +// Copyright (c) 2025 LIS Marseille (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Alexandra Bac +// Kevin Fedyna + +#ifndef CGAL_OSM_SUB_SPARSE_MATRIX_H +#define CGAL_OSM_SUB_SPARSE_MATRIX_H + + +#include + +#include +#include + +namespace CGAL { +namespace OSM { + +/*! + \ingroup PkgHDVFAlgorithmClasses + + The class `Sub_sparse_matrix` is a technical class implementing the concept `SparseMatrix` together with a system of masks to partially screen matrices (and restrict computations to a subset of indices *along their major direction*). This class is used to compute reduced homology (and thus to compute persistent homology and Alexander duality). + + `Sub_sparse_matrix` inherits `Sparse_matrix` structure and basically adds two bitboards: + - one describing indices of cells belonging to the subset of indices considered (let us denote it by \f$A\f$) + - the second providing indices of non-empty chains in \f$A\f$ + + The class does not modify linear algebra operators, but focuses on adapted iterators, output operators and methods to adjust the "mask". + + \cgalModels{SparseMatrix} + + \tparam CoefficientRing a model of the `IntegralDomainWithoutDivision` concept, providing the ring used to compute homology. + \tparam StorageFormat an integer constant encoding the storage format of matrices (`OSM::COLUMN` or `OSM::ROW`). +*/ + + +template +class Sub_sparse_matrix : public Sparse_matrix { + +protected: + /** \brief A bitboard describing subchains restriction. */ + Bitboard _subChains; + + /** \brief A bitboard containing state of each chain (restricted to subchains). */ + Bitboard _subChainsStates; + +public: + /** + * \brief Default constructor of a new `Sub_sparse_matrix` (with given rows/columns sizes and mask set to `full`). + * + * Default constructor. Constructor with sizes, initialize an empty Sub_sparse_matrix of type `StorageFormat` with coefficients of type `CoefficientRing`, a given size along rows/columns. The constructor sets the mask to `full`. + * + * \param rowCount The number of rows to preallocate (default 0). + * \param columnCount The number of columns to preallocate (default 0). + */ + Sub_sparse_matrix(size_t rowCount=0, size_t columnCount=0) : Sparse_matrix(rowCount, columnCount) + { + if (StorageFormat == OSM::COLUMN) + _subChains = OSM::Bitboard(columnCount,false) ; + else + _subChains = OSM::Bitboard(rowCount,false) ; + _subChainsStates = this->_chainsStates & _subChains ; + } + + /** + * \brief Constructor with given rows/columns sizes and a mask. + * + * Create a new empty `Sub_sparse_matrix` of type `StorageFormat` with coefficients of type `CoefficientRing`, a given size along rows/columns and a given mask. + * + * \param rowCount The number of rows to preallocate. + * \param columnCount The number of columns to preallocate. + * \param subChain Bitboard describing the subset of indices considered as a mask. + */ + Sub_sparse_matrix(size_t rowCount, size_t columnCount, const Bitboard& subChain) : Sparse_matrix(rowCount, columnCount), _subChains(subChain), _subChainsStates(this->_chainsStates & subChain) + { + } + + /** \brief Copy constructor from another `Sub_sparse_matrix`. + * + * Create a new empty `Sub_sparse_matrix` from another of type `StorageFormat` with coefficients of type `CoefficientRing`, a given size along rows/columns and a given mask. + * + * \param otherToCopy `Sub_sparse_matrix` copied into `this`. + */ + Sub_sparse_matrix(const Sub_sparse_matrix& otherToCopy) : Sparse_matrix(otherToCopy), _subChains(otherToCopy._subChains), _subChainsStates(otherToCopy._subChainsStates) {} + + /** \brief Copy constructor from `Sparse_matrix`. + * + * Create a new `Sub_sparse_matrix` from a `Sparse_matrix` object (with the same `StorageFormat`). Create a "full" mask. + * + * \param otherToCopy The matrix copied. + */ + Sub_sparse_matrix(const Sparse_matrix& otherToCopy) : Sparse_matrix(otherToCopy) + { + if (StorageFormat == OSM::COLUMN) + _subChains = OSM::Bitboard(otherToCopy.dimensions().second,false) ; + else + _subChains = OSM::Bitboard(otherToCopy.dimensions().first,false) ; + _subChainsStates = this->_chainsStates & _subChains ; + } + + + /** \brief Iterator to the beginning of the indices of non empty chains inside the mask. */ + inline Bitboard::iterator begin() const noexcept + { + return _subChainsStates.begin() ; + } + + /** \brief Iterator past-the-end of the indices of non empty chains inside the mask. */ + inline Bitboard::iterator end() const noexcept + { + return _subChainsStates.end() ; + } + + /** \brief Changes the indices subset mask. + * + * Set a new mask encoding a new subset of indices along the major dimension. + */ + inline void set_sub (const Bitboard& new_subChains) + { + _subChains = new_subChains ; + _subChainsStates = this->_chainsStates & _subChains ; + } + + /** \brief Adds an index to the mask. + * + * Set the bit encoding a given index to 1 (ie.\ add the index in the mask). + * + * \param index Index to turn on in the mask. + */ + inline void set_bit_on (size_t index) + { + _subChains.set_on(index) ; + if (this->_chainsStates.is_on(index)) + _subChainsStates.set_on(index) ; + } + + /** \brief Removes an index from the mask. + * + * Set the bit encoding a given index to 0 (ie.\ remove the index from the mask). + * + * \param index Index to turn off in the mask. + */ + inline void set_bit_off (size_t index) + { + _subChains.set_off(index) ; + _subChainsStates.set_off(index) ; + } + + /** \brief Changes the mask to its complement. */ + inline void complement() { _subChains.bit_not() ; } + + /** + * \brief Assigns to other `Sub_sparse_matrix`. + * + * Assign to other matrix coefficient-wise, and copy the bitboard. + * + * \pre The matrices must have the same type. + * + * \param otherToCopy The matrix we want to copy. + * + * \return The reference to the modified matrix. + */ + inline Sub_sparse_matrix& operator=(const Sub_sparse_matrix &otherToCopy) + { + (dynamic_cast&>(*this)).operator=(otherToCopy) ; + _subChains = otherToCopy._subChains ; + _subChainsStates = otherToCopy._subChainsStates ; + return *this ; + } + + /** + * \brief Displays a `Sub_sparse_matrix` in the output stream. + * + * Displays the sparse matrix as well as its mask. + * + * \param stream The output stream. + * \param matrix The matrix to display. + * + * \return A reference to the modified stream. + */ + friend std::ostream& operator<<(std::ostream &stream, const Sub_sparse_matrix &matrix) { + stream << static_cast&>(matrix) ; + stream << matrix._subChains << std::endl ; + return stream ; + } +}; + +} /* end namespace OSM */ +} /* end namespace CGAL */ + +#endif // CGAL_OSM_SUB_SPARSE_MATRIX_H diff --git a/HDVF/include/CGAL/HDVF/Surface_mesh_io.h b/HDVF/include/CGAL/HDVF/Surface_mesh_io.h new file mode 100644 index 00000000000..179b63b178e --- /dev/null +++ b/HDVF/include/CGAL/HDVF/Surface_mesh_io.h @@ -0,0 +1,160 @@ +// Copyright (c) 2025 LIS Marseille (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Alexandra Bac + +#ifndef CGAL_HDVF_SURFACE_MESH_IO_H +#define CGAL_HDVF_SURFACE_MESH_IO_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace CGAL { +namespace Homological_discrete_vector_field { + + +/*! + \ingroup PkgHDVFIOClasses + + The class `Surface_mesh_io` is an intermediate %IO class, used to load a triangle mesh and produce simplicial complexes. +\tparam TriangleMesh a model of `FaceGraph` and `HalfedgeGraph` concepts, e.g., a `CGAL::Surface_mesh`. +\tparam Traits a geometric traits class model of the `HDVFTraits` concept. + */ + +template +class Surface_mesh_io : public Mesh_object_io +{ +public: + typedef TriangleMesh Surface_mesh; + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor ; + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor ; + typedef typename boost::graph_traits::edge_descriptor edge_descriptor ; + typedef typename boost::graph_traits::face_descriptor face_descriptor ; +private: + + /** \brief Storage of vertices Io_cell_type <-> Vertex_index permutation. + * + * `_io_cell_to_he_index` maps a Io_cell_type to its associated Halfedge_index in the mesh + * + * `_he_index_to_io_cell.at(q)` stores, for each dimension `q`, the map between Halfedge_index and Io_cell_type of cells of dimension `q` + * + * All cells, whatever their dimension, are identified by halfedges: + * - a vertex is the `target` of the halfedge + * - an edge is the `edge` of the halfedge + * - a face is the `face` of the halfedge + */ + std::map _io_cell_to_he_index; + std::vector > _he_index_to_io_cell; + +public: + /** \brief Constructor from a triangular mesh. + * + */ + Surface_mesh_io(const TriangleMesh& mesh) : Mesh_object_io(2) { + typedef typename Traits::Point Point; + + this->nvertices = num_vertices(mesh); + this->ncells = num_vertices(mesh) + num_edges(mesh) + num_faces(mesh) ; + _he_index_to_io_cell.resize(3); + + auto vpm = get(CGAL::vertex_point, mesh); + // Load nodes + for (vertex_descriptor v : vertices(mesh)) { + Point p(get(vpm,v)); + this->nodes.push_back(p); + } + // Load vertices + size_t tmp_index(0); + for (vertex_descriptor v : vertices(mesh)) { + // Corresponding Io_cell_type + Io_cell_type tmp_io_cell({tmp_index}); + // Add to cells + this->cells.push_back(tmp_io_cell); + // Associated he + halfedge_descriptor vertex_he(halfedge(v,mesh)); + // Store the permutation + _io_cell_to_he_index[tmp_io_cell] = vertex_he; + _he_index_to_io_cell.at(0)[vertex_he] = tmp_io_cell; + ++tmp_index; + } + // Load edges + for (edge_descriptor e : edges(mesh)) { + // Associated he + halfedge_descriptor edge_he(halfedge(e,mesh)); + // Compute corresponding Io_cell + std::vector tmp_cell; + // Vertex1 + { + // Get the halfedge "encoding" the target vertex + halfedge_descriptor vert_he_ind(halfedge(target(edge_he, mesh), mesh)); + // Get corresponding io_cell + Io_cell_type vert(_he_index_to_io_cell.at(0).at(vert_he_ind)); + assert(vert.size()==1); // vertex + // Push_back the index + tmp_cell.push_back(vert.at(0)); + } + // Vertex2 + { + // Get the halfedge "encoding" the source vertex + halfedge_descriptor vert_he_ind(halfedge(target(opposite(edge_he, mesh), mesh), mesh)); + // Get the corresponding io_cell + Io_cell_type vert(_he_index_to_io_cell.at(0).at(vert_he_ind)); + assert(vert.size()==1); // vertex + // Push_back the index + tmp_cell.push_back(vert.at(0)); + } + std::sort(tmp_cell.begin(), tmp_cell.end()); + // Add to cells + this->cells.push_back(tmp_cell); + // Set the permutation + _io_cell_to_he_index[tmp_cell] = edge_he; + _he_index_to_io_cell.at(1)[edge_he] = tmp_cell; + } + // Load faces + for (face_descriptor f : faces(mesh)) { + // Associated he + halfedge_descriptor face_he(halfedge(f,mesh)); + // Compute corresponding Io_cell + std::vector tmp_cell; + // Visit vertices around the face + size_t cpt_verts(0); + for(vertex_descriptor v : vertices_around_face(halfedge(f,mesh), mesh)){ + ++cpt_verts; + // Get the halfedge stored in the vertex + halfedge_descriptor vert_he_ind(halfedge(v,mesh)); + // Get the corresponding io_cell + Io_cell_type vert(_he_index_to_io_cell.at(0).at(vert_he_ind)); + assert(vert.size()==1); // vertex + // Push_back the index + tmp_cell.push_back(vert.at(0)); + } + // Faces must be triangles + assert(cpt_verts == 3); + std::sort(tmp_cell.begin(), tmp_cell.end()); + // Add to cells + this->cells.push_back(tmp_cell); + // Set the permutation + _io_cell_to_he_index[tmp_cell] = face_he; + _he_index_to_io_cell.at(2)[face_he] = tmp_cell; + } + } +} ; + +} /* end namespace Homological_discrete_vector_field */ +} /* end namespace CGAL */ + + +#endif // CGAL_HDVF_SURFACE_MESH_IO_H diff --git a/HDVF/include/CGAL/HDVF/Tet_object_io.h b/HDVF/include/CGAL/HDVF/Tet_object_io.h new file mode 100644 index 00000000000..7715e704773 --- /dev/null +++ b/HDVF/include/CGAL/HDVF/Tet_object_io.h @@ -0,0 +1,221 @@ +// Copyright (c) 2025 LIS Marseille (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Alexandra Bac + +#ifndef CGAL_HDVF_TET_OBJECT_H +#define CGAL_HDVF_TET_OBJECT_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace CGAL { +namespace Homological_discrete_vector_field { + + +// Tetgen related + + +template +inline bool Mesh_object_io::read_nodes_file(const std::string &filename) +{ + nvertices = read_nodes(filename, &nodes) ; + return true; // @todo error handling +} + +// Tetgen + +/* Class used to load tetgen outputs (for Alexander duality). + + \tparam Traits a geometric traits class model of the `HDVFTraits` concept. +*/ +template +class Tet_object_io : public Mesh_object_io +{ +public: + Tet_object_io(const std::string & prefix) : Mesh_object_io(), _prefix(prefix) + { + this->dim = -3 ; + add_nodes() ; + create_nodes() ; + // If the user wished to keep tetgen indices, insert edges and faces below. Otherwise, they will be created by the Abstract_simplicial_chain_complex constructor +// add_edges() ; +// add_faces() ; + add_tets() ; + this->ncells = this->cells.size() ; + } + + void add_nodes() + { + const std::string file_node = fnodes_from_prefix(_prefix) ; + std::cout << "file_node : " << file_node << std::endl ; + this->read_nodes_file(file_node) ; + } + + void create_nodes() + { + for (size_t i=0; invertices; ++i) + { + Io_cell_type cell({i}) ; + this->cells.push_back(cell) ; + } + std::cout << "--- " << this->nvertices << "vert" << std::endl ; + } + + void add_edges() + { + const std::string file_edge = fedges_from_prefix(_prefix) ; + std::ifstream input_file ( file_edge ); + size_t f_nedges ; + // Open the input file + if ( ! input_file . good () ) + { + std::cerr << "File " << file_edge << " not found.\n"; + throw std::runtime_error("File Parsing Error: File ! found"); + } + // First line is the number of edges + std::size_t line_number = 0; + if (! input_file.eof()) + { + ++line_number ; + std::string line; + getline( input_file, line ); + check_sanity_line(line, file_edge) ; + // First number is the number of nodes + std::istringstream is (line); + is >> f_nedges ; + } + while ( !(input_file.eof()) && (f_nedges>0)) + { + size_t trash, i, j ; + ++ line_number; + --f_nedges ; + std::string line; + getline( input_file, line ); + check_sanity_line(line, file_edge) ; + std::istringstream is (line); + is >> trash ; + is >> i ; + is >> j ; + Io_cell_type cell({i,j}) ; + this->add_cell(cell) ; + } + input_file.close() ; + std::cout << "--- " << f_nedges << " edges" << std::endl ; + } + + void add_faces() + { + const std::string file_face = ffaces_from_prefix(_prefix) ; + std::ifstream input_file ( file_face ); + size_t f_nfaces ; + // Open the input file + if ( ! input_file . good () ) + { + std::cerr << "File " << file_face << " not found.\n"; + throw std::runtime_error("File Parsing Error: File ! found"); + } + // First line is the number of edges + std::size_t line_number = 0; + if (! input_file.eof()) + { + ++line_number ; + std::string line; + getline( input_file, line ); + check_sanity_line(line, file_face) ; + // First number is the number of nodes + std::istringstream is (line); + is >> f_nfaces ; + } + while ( !(input_file.eof()) && (f_nfaces>0)) + { + size_t trash, i, j, k ; + ++ line_number; + --f_nfaces ; + std::string line; + getline( input_file, line ); + check_sanity_line(line, file_face) ; + std::istringstream is (line); + is >> trash ; + is >> i ; + is >> j ; + is >> k ; + Io_cell_type cell({i, j, k}) ; + this->add_cell(cell) ; + } + input_file.close() ; + std::cout << "--- " << f_nfaces << " faces" << std::endl ; + } + + void add_tets() + { + const std::string file_ele = ftets_from_prefix(_prefix) ; + std::ifstream input_file ( file_ele ); + size_t f_ntets, tmp ; + // Open the input file + if ( ! input_file . good () ) + { + std::cerr << "File " << file_ele << " not found.\n"; + throw std::runtime_error("File Parsing Error: File ! found"); + } + // First line is the number of edges + std::size_t line_number = 0; + if (! input_file.eof()) + { + ++line_number ; + std::string line; + getline( input_file, line ); + check_sanity_line(line, file_ele) ; + // First number is the number of nodes + std::istringstream is (line); + is >> f_ntets ; + + } + tmp = f_ntets ; + while ( !(input_file.eof()) && (tmp>0)) + { + size_t trash, i, j, k, l ; + ++ line_number; + --tmp ; + std::string line; + getline( input_file, line ); + check_sanity_line(line, file_ele) ; + std::istringstream is (line); + is >> trash ; + is >> i ; + is >> j ; + is >> k ; + is >> l ; + Io_cell_type tmp_cell({i, j, k, l}); + this->add_cell(tmp_cell, true) ; + } + input_file.close() ; + std::cout << "--- " << f_ntets << " tets" << std::endl ; + } +private: + std::string _prefix ; + std::string fnodes_from_prefix(const std::string &prefix) {return prefix+".1.node"; } + std::string fedges_from_prefix(const std::string &prefix) {return prefix+".1.edge"; } + std::string ffaces_from_prefix(const std::string &prefix) {return prefix+".1.face"; } + std::string ftets_from_prefix(const std::string &prefix) {return prefix+".1.ele"; } +} ; + +} /* end namespace Homological_discrete_vector_field */ +} /* end namespace CGAL */ + + +#endif // CGAL_HDVF_TET_OBJECT_H diff --git a/HDVF/include/CGAL/HDVF/Triangulation_3_io.h b/HDVF/include/CGAL/HDVF/Triangulation_3_io.h new file mode 100644 index 00000000000..f67fc6f90c6 --- /dev/null +++ b/HDVF/include/CGAL/HDVF/Triangulation_3_io.h @@ -0,0 +1,114 @@ +// Copyright (c) 2025 LIS Marseille (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Alexandra Bac + +#ifndef CGAL_HDVF_TRIANGULATION_3_IO_H +#define CGAL_HDVF_TRIANGULATION_3_IO_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace CGAL { +namespace Homological_discrete_vector_field { + + +/*! + \ingroup PkgHDVFIOClasses + + The class `Triangulation_3_io` is an intermediate %IO class, used to load a `Triangulation_3` and produce simplicial complexes. The class loads the vertices and the cells (ie. tetrahedra) of the `Triangulation_3` into a `Mesh_object_io`. +\tparam Triangulation3 a model of `CGAL::Triangulation_3`. +\tparam Traits a geometric traits class model of the `HDVFTraits` concept. + */ + +template +class Triangulation_3_io : public Mesh_object_io +{ +public: + typedef Triangulation3 Triangulation_3; + typedef typename Triangulation3::Vertex_handle vertex_descriptor ; + typedef typename Triangulation3::Cell_handle cell_descriptor ; + typedef typename Triangulation3::Point Point ; +private: + + /** \brief Storage of vertices Io_cell_type <-> Cell_handle permutation. + * + * Vertices maps: + *- `_io_cell_to_vertex_handle` maps a vertex Io_cell_type to its associated Vertex_handle in the triangulation + *- `_vertex_handle_to_io_cell` stores the map between Vertex_handle and Io_cell_type of vertices (of dimension 0). + * + *Cells maps (dimension 3): + *- `_io_cell_to_cell_handle` maps a Io_cell_type to its associated Cell_handle in the triangulation + * + *- `_cell_handle_to_io_cell` stores the map between Cell_handle and Io_cell_type of Cells (of dimension 3). + */ + std::map _io_cell_to_vertex_handle; + std::map _vertex_handle_to_io_cell; + + std::map _io_cell_to_cell_handle; + std::map _cell_handle_to_io_cell; + +public: + /** \brief Constructor from a `Triangulation_3`. + * + */ + Triangulation_3_io(const Triangulation_3& triangulation) : Mesh_object_io(3) { + typedef typename Traits::Point Point; + + this->nvertices = triangulation.number_of_vertices(); + this->ncells = triangulation.number_of_vertices() + triangulation.number_of_finite_cells(); + + // Load nodes + for (Point p : triangulation.points()) { + this->nodes.push_back(p); + } + // Load vertices + size_t tmp_index(0); + for (vertex_descriptor v : triangulation.finite_vertex_handles()) { + // Corresponding Io_cell_type + Io_cell_type tmp_io_cell({tmp_index}); + // Add to cells + this->cells.push_back(tmp_io_cell); + // Store the permutation + _io_cell_to_vertex_handle[tmp_index] = v; + _vertex_handle_to_io_cell[v] = tmp_index; + ++tmp_index; + } + // Load cells + for (cell_descriptor c : triangulation.finite_cell_handles()) { + // Compute corresponding Io_cell + std::vector tmp_cell; + // Visit vertices around the face to get the indices of vertices + std::array verts(triangulation.vertices(c)); + for (int i=0; i<4; ++i){ + tmp_cell.push_back(_vertex_handle_to_io_cell[verts[i]]); + } + // Sort this vector + std::sort(tmp_cell.begin(), tmp_cell.end()); + // Add to cells + this->cells.push_back(tmp_cell); + // Set the permutation + _io_cell_to_cell_handle[tmp_cell] = c; + _cell_handle_to_io_cell[c] = tmp_cell; + } + } +} ; + +} /* end namespace Homological_discrete_vector_field */ +} /* end namespace CGAL */ + + +#endif // CGAL_HDVF_TRIANGULATION_3_IO_H diff --git a/HDVF/include/CGAL/OSM/Bitboard.h b/HDVF/include/CGAL/OSM/Bitboard.h new file mode 100644 index 00000000000..67bfa460275 --- /dev/null +++ b/HDVF/include/CGAL/OSM/Bitboard.h @@ -0,0 +1,686 @@ +// Copyright (c) 2025 LIS Marseille (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Alexandra Bac +// Kevin Fedyna + +#ifndef CGAL_OSM_BITBOARD_H +#define CGAL_OSM_BITBOARD_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace CGAL { +namespace OSM { + +/* Constants */ +const size_t size_t_maxvalue = std::numeric_limits::max() ; + +#define BITBOARD_INT_SIZE 64 +#define VECTOR_SIZE(size) size / BITBOARD_INT_SIZE + (size % BITBOARD_INT_SIZE != 0) + + +/* \brief Table used for fast 64bit log2. */ +const int tab64[64] = { + 63, 0, 58, 1, 59, 47, 53, 2, + 60, 39, 48, 27, 54, 33, 42, 3, + 61, 51, 37, 40, 49, 18, 28, 20, + 55, 30, 34, 11, 43, 14, 22, 4, + 62, 57, 46, 52, 38, 26, 32, 41, + 50, 36, 17, 19, 29, 10, 13, 21, + 56, 45, 25, 31, 35, 16, 9, 12, + 44, 24, 15, 8, 23, 7, 6, 5 +}; + +/* \brief Table used for De Bruijn multiplication. */ +const int index64[64] = { + 0, 47, 1, 56, 48, 27, 2, 60, + 57, 49, 41, 37, 28, 16, 3, 61, + 54, 58, 35, 52, 50, 42, 21, 44, + 38, 32, 29, 23, 17, 11, 4, 62, + 46, 55, 26, 59, 40, 36, 15, 53, + 34, 51, 20, 43, 31, 22, 10, 45, + 25, 39, 14, 33, 19, 30, 9, 24, + 13, 18, 8, 12, 7, 6, 5, 63 +}; + +/*! + \ingroup PkgHDVFAlgorithmClasses + + The class `Bitboard` implements an accelerating structure, derived from the chess community. This structure maps stores a sequence of bits. However, unlike vectors or arrays, it provides fast forward and reverse iterators over `1` bits. It is used in `OSM::Sparse_matrix` to build a fast iterator over non-zero chains. + +*/ + +class Bitboard { +private: + /* \brief The inner data storage (a contiguous array of 64 unsigned bit integers). */ + std::vector boards; + + /* \brief The number of bits we want to store. */ + std::size_t _size; + + /* + * \brief Compute fast 64bit int log2. + * \pre The value must have a population count of 1. + * + * \param _value The value we want to get the bit index (i.e. log2). + * \returns The bit position. + */ + static std::size_t bit_index(std::uint64_t _value) { + if (_value == 1) return 0; + return tab64[((std::uint64_t)((_value - (_value >> 1)) * 0x07EDD5E59A4E28C2)) >> 58] + 1; + } + +public: + /** + * \struct Bitboard::iterator + * \brief The iterator over bitboards. + * + * Return all indices with a bit set to 1. + * + * \note The iterator is constant. + */ + struct iterator { + /** C++ mandatory type definitions. */ + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = std::uint64_t; + using pointer = value_type*; + using reference = value_type&; + + /** + * \brief Iterator constructor. + * \param _index The initial index. + * \param _size The bitboard size. + * \param _boards The reference to the bitboards. + * + * \author Fedyna K. + * \version 0.2.0 + * \date 23/05/2024 + */ + iterator(const std::size_t _index, const std::size_t _size, const std::vector &_boards): + index(_index), + size(_size), + iteratedBoards(_boards), + arrayIndex(0) + { + if (index == 0) ++(*this); + } + + /** + * \brief Iterator dereference. + * \returns A no-null index on the billboard or past-the-end index. + */ + const std::size_t operator*() const { return index; } + + /** + * \brief Prefix incrementation. Finds the next not-null index. + * \returns The reference to the current iterator. + */ + iterator& operator++() { + // The iterator rely on bit manipulation that are quite fast on the CPU. + // + // The main ideas come from the chess programming community that deals with 8x8 sparse matrices. + // The LSB1 (Less Significant Bit On) is given by X & -X. + // This same bit can be "reset" using X & (X - 1), which means that all ones remain except the LSB1. + // + // We can use this to iterate through all ones in order. + // The following code scales up this principle for multiple contiguous 64bit bitboards. + std::size_t innerIndex ; //= bit_index(iteratedBoards[arrayIndex] & -iteratedBoards[arrayIndex]); + if (arrayIndex < iteratedBoards.size()) + { + do { + innerIndex = bit_index(iteratedBoards[arrayIndex] & -iteratedBoards[arrayIndex]); + index = arrayIndex * BITBOARD_INT_SIZE + innerIndex; + iteratedBoards[arrayIndex] &= iteratedBoards[arrayIndex] - 1; + + arrayIndex += innerIndex / 64; + } while (innerIndex == 64 && arrayIndex < iteratedBoards.size()); + } + + index = index > size ? size : index; + + return *this; + } + + /** + * \brief Postfix incrementation. Finds the next not-null index. + * \returns The pre-incremented iterator. + */ + iterator operator++(int) { iterator tmp = *this; ++(*this); return tmp; } + + /** + * \brief Equality check. + * \returns True if the indices are equal. + */ + friend bool operator==(const iterator &a, const iterator &b) { return a.index == b.index; } + + /** + * \brief Inequality check. + * \returns True if the indices are different. + */ + friend bool operator!=(const iterator &a, const iterator &b) { return a.index != b.index; } + + private: + std::size_t index; + std::size_t arrayIndex; + std::size_t size; + std::vector iteratedBoards; + }; + + /** + * \struct Bitboard::reverse_iterator + * \brief The reverse iterator over bitboards. + * + * Return all indices with a bit set to 1 in reverse order. + * + * \note The reverse iterator is constant. + */ + struct reverse_iterator { + /** C++ mandatory type definitions. */ + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = std::uint64_t; + using pointer = value_type*; + using reference = value_type&; + + /** + * \brief Reverse iterator constructor. + * \param _index The initial index. + * \param _size The bitboard size. + * \param _boards The reference to the bitboards. + */ + reverse_iterator(const std::size_t _index, const std::size_t _size, const std::vector &_boards): + index(_index), + size(_size), + iteratedBoards(_boards), + arrayIndex(index/64) + { + // set 0 for all bits larger than index + if (index < size_t_maxvalue) + { + do { + ++(*this); + } while (index >_index); + } + + } + + /** + * \brief Iterator dereference. + * \returns A no-null index on the billboard or past-the-end index. + */ + const std::size_t operator*() const { return index; } + + /** + * \brief Prefix incrementation. Finds the next not-null index. + * \returns The reference to the current iterator. + */ + reverse_iterator& operator++() { + // The reverse iterator rely on bit manipulation that are quite fast on the CPU. + // + // The main ideas come from the chess programming community that deals with 8x8 sparse matrices. + // The MSB1 (Most Significant Bit On) is computed using De Bruijn Multiplication. + // See https://www.chessprogramming.org/BitScan#Index_of_LS1B_by_Popcount + // + // int bitScanReverse(U64 bb) { + // const U64 debruijn64 = C64(0x03f79d71b4cb0a89); + // assert (bb != 0); + // bb |= bb >> 1; + // bb |= bb >> 2; + // bb |= bb >> 4; + // bb |= bb >> 8; + // bb |= bb >> 16; + // bb |= bb >> 32; + // return tab64[(bb * debruijn64) >> 58]; + // } + + // We can use this to iterate through all ones in reverse order. + // The following code scales up this principle for multiple contiguous 64bit bitboards. + // MSB are stored in the rightmost 64bits bitboard. + + std::size_t innerIndex ; //= bit_index(iteratedBoards[arrayIndex] & -iteratedBoards[arrayIndex]); + if (arrayIndex != size_t_maxvalue) + { + do { + std::size_t bb(iteratedBoards[arrayIndex]) ; + if (bb == 0) + innerIndex == 64 ; + else + { + // Compute MSB1 + const std::size_t debruijn64 = std::size_t(0x03f79d71b4cb0a89); + bb |= bb >> 1; + bb |= bb >> 2; + bb |= bb >> 4; + bb |= bb >> 8; + bb |= bb >> 16; + bb |= bb >> 32; + innerIndex = index64[(bb * debruijn64) >> 58] ; + // Compute corresponding index + index = arrayIndex * BITBOARD_INT_SIZE + innerIndex; + // Reset bit + iteratedBoards[arrayIndex] ^= 1< iteratedBoards; + }; + + /** + * \brief Bitboard default constructor. Initialize bitboard with all zeros and size 64. + */ + Bitboard(): boards({0UL}), _size(64) {} + + /** + * \brief Bitboard value intializer. Initialize bitboard with given vector of bitboards. + * + * \param _bitboard The vector of 64bit ints. + */ + Bitboard(const std::vector &_bitboard): + boards(_bitboard), + _size(_bitboard.size() * 64) + {} + + /** + * \brief Bitboard size initializer. Initialize bitboard with given size. + * + * \param _size The size of the bitboard. + * \param empty If the boolean is `true`, create an empty bitboard (all bits set to 0), otherwise create a full bitboard (all bits set to 1). + */ + Bitboard(const std::size_t _size, bool empty = true): + boards(std::vector(_size / BITBOARD_INT_SIZE + (_size % BITBOARD_INT_SIZE != 0))), + _size(_size) + { if (!empty) bit_not() ; } + + /** + * \brief Bitboard copy constructor. Initialize bitboard with given bitboard. + * + * \param _bitboard The bitboard to copy. + */ + Bitboard(const Bitboard &_bitboard): + boards(_bitboard.boards), + _size(_bitboard._size) + {} + + + /** + * \brief Bitboard assign operator. Initialize bitboard with given bitboard. + * + * \param _bitboard The bitboard to copy. + */ + Bitboard& operator=(const Bitboard &_bitboard) { + this->boards = _bitboard.boards; + this->_size = _bitboard._size; + + return *this; + } + + /** + * \brief Bitwise NOT on a bitboard. + * + * \returns The reference to the NOTed bitboard. + */ + Bitboard& bit_not() { + for (std::size_t i = 0 ; i < boards.size() ; ++i) + { + boards[i] = ~boards[i]; + } + + return *this; + } + + /** + * \brief Bitwise NOT of a bitboard. + * + * \param _bitboard The argument bitboard. + * \returns A new bitboard which is the result of the NOT. + */ + friend Bitboard operator~(const Bitboard& _bitboard) { + Bitboard res(_bitboard); + return res.bit_not(); + } + + /** + * \brief Bitboard begin iterator that allows to loop through all non-null indices. + * \returns The begin iterator. + * + * \note The iterator is constant. + */ + iterator begin() const { return iterator(0, _size, boards); } + + /** + * \brief Bitboard past-the-end iterator. + * \returns The past-the-end iterator. + * + * \note The iterator is constant. + */ + iterator end() const { return iterator(_size, _size, boards); } + + /** + * \brief Bitboard reverse_begin reverse_iterator that allows to loop through all non-null indices in decreasing order. + * \returns The reverse_begin iterator. + * + * \note The reverse_iterator is constant. + */ + reverse_iterator reverse_begin() const { return reverse_iterator(_size-1, _size, boards); } + reverse_iterator reverse_begin(size_t index) const { std::cout << "reverse with index " << index << std::endl ; return reverse_iterator(index, _size, boards); } + + /** + * \brief Bitboard past-the-end reverse_iterator. + * \returns The past-the-end reverse_iterator. + * + * \note The reverse_iterator is constant. + */ + reverse_iterator reverse_end() const { return reverse_iterator(size_t_maxvalue, _size, boards); } + + /** + * \brief Stream operator that displays all bits. + * + * \param[in,out] _stream The stream to edit. + * \param _bitboard The bitboard to display. + * \returns The edited stream. + */ + friend std::ostream& operator<<(std::ostream &_stream, const Bitboard &_bitboard) { + // Cheat for speed, iterate through all non-null indices and fill the in-between with zeros. + std::size_t last = -1; + for (auto index: _bitboard) { + _stream << std::string(index - last - 1, '0') << "1"; + last = index; + } + // Add the leading zeros. + _stream << std::string(_bitboard._size - last - 1, '0'); + + return _stream; + } + + /** + * \brief Bitwise OR between two bitboards. + * + * \param _left The left hand side. + * \param _right The right hand side. + * \returns A new bitboard which is the result of the OR. + */ + friend Bitboard operator|(const Bitboard &_left, const Bitboard &_right) { + Bitboard res = _left; + res |= _right; + return res; + } + + /** + * \brief Bitwise OR between a bitboard and a single bit at given position. + * + * \param _left The left hand side. + * \param _right The right hand side. + * \returns A new bitboard which is the result of the OR. + */ + friend Bitboard operator|(const Bitboard &_left, const std::size_t &_right) { + Bitboard res = _left; + res |= _right; + return res; + } + + /** + * \brief Bitwise OR between a bitboard and a single bit at given position. + * + * \param _left The left hand side. + * \param _right The right hand side. + * \returns A new bitboard which is the result of the OR. + */ + friend Bitboard operator|(const std::size_t &_left, const Bitboard &_right) { + Bitboard res = _right; + res |= _left; + return res; + } + + /** + * \brief Bitwise AND between two bitboards. + * + * \param _left The left hand side. + * \param _right The right hand side. + * \returns A new bitboard which is the result of the AND. + */ + friend Bitboard operator&(const Bitboard &_left, const Bitboard &_right) { + Bitboard res = _left; + res &= _right; + return res; + } + + /** + * \brief Bitwise AND between a bitboard and a single bit at given position. + * + * \param _left The left hand side. + * \param _right The right hand side. + * \returns A new bitboard which is the result of the AND. + */ + friend Bitboard operator&(const Bitboard &_left, const std::size_t &_right) { + Bitboard res = _left; + res &= _right; + return res; + } + + /** + * \brief Bitwise AND between a bitboard and a single bit at given position. + * + * \param _left The left hand side. + * \param _right The right hand side. + * \returns A new bitboard which is the result of the AND. + */ + friend Bitboard operator&(const std::size_t &_left, const Bitboard &_right) { + Bitboard res = _right; + res &= _left; + return res; + } + + /** + * \brief Bitwise XOR between two bitboards. + * + * \param _left The left hand side. + * \param _right The right hand side. + * \returns A new bitboard which is the result of the XOR. + */ + friend Bitboard operator^(const Bitboard &_left, const Bitboard &_right) { + Bitboard res = _left; + res ^= _right; + return res; + } + + /** + * \brief Bitwise XOR between a bitboard and a single bit at given position. + * + * \param _left The left hand side. + * \param _right The right hand side. + * \returns A new bitboard which is the result of the XOR. + */ + friend Bitboard operator^(const Bitboard &_left, const std::size_t &_right) { + Bitboard res = _left; + res ^= _right; + return res; + } + + /** + * \brief Bitwise XOR between a bitboard and a single bit at given position. + * + * \param _left The left hand side. + * \param _right The right hand side. + * \returns A new bitboard which is the result of the XOR. + */ + friend Bitboard operator^(const std::size_t &_left, const Bitboard &_right) { + Bitboard res = _right; + res ^= _left; + return res; + } + + /** + * \brief Bitwise OR between two bitboards. + * + * \param _other The other bitboard. + * \returns The reference to the ORed bitboard. + */ + Bitboard& operator|=(const Bitboard &_other) { + for (std::size_t i = 0 ; i < boards.size() ; i++) { + boards[i] |= _other.boards[i]; + } + + return *this; + } + + /** + * \brief Bitwise OR between a bitboard and a single bit at given position. + * + * \param _other The bit position. + * \returns The reference to the ORed bitboard. + */ + Bitboard& operator|=(const std::size_t &_other) { + std::size_t boardIndex = _other / BITBOARD_INT_SIZE; + std::uint64_t bit = 1ULL << (_other % BITBOARD_INT_SIZE); + + boards[boardIndex] |= bit; + return *this; + } + + /** + * \brief Bitwise AND between two bitboards. + * + * \param _other The other bitboard. + * \returns The reference to the ANDed bitboard. + */ + Bitboard& operator&=(const Bitboard &_other) { + for (std::size_t i = 0 ; i < boards.size() ; i++) { + boards[i] &= _other.boards[i]; + } + + return *this; + } + + /** + * \brief Bitwise AND between a bitboard and a single bit at given position. + * + * \param _other The bit position. + * \returns The reference to the ANDed bitboard. + */ + Bitboard& operator&=(const std::size_t &_other) { + std::size_t boardIndex = _other / BITBOARD_INT_SIZE; + std::uint64_t bit = 1ULL << (_other % BITBOARD_INT_SIZE); + + boards[boardIndex] &= bit; + return *this; + } + + /** + * \brief Bitwise XOR between two bitboards. + * + * \param _other The other bitboard. + * \returns The reference to the XORed bitboard. + */ + Bitboard& operator^=(const Bitboard &_other) { + for (std::size_t i = 0 ; i < boards.size() ; i++) { + boards[i] ^= _other.boards[i]; + } + + return *this; + } + + /** + * \brief Bitwise XOR between a bitboard and a single bit at given position. + * + * \param _other The bit position. + * \returns The reference to the XORed bitboard. + */ + Bitboard& operator^=(const std::size_t &_other) { + std::size_t boardIndex = _other / BITBOARD_INT_SIZE; + std::uint64_t bit = 1ULL << (_other % BITBOARD_INT_SIZE); + + boards[boardIndex] ^= bit; + return *this; + } + + /** + * \brief Toggle on and off a given bit. + * + * \param _index The bit position. + */ + void toggle(const std::size_t &_index) { + *this ^= _index; + } + + /** + * \brief Toggle on a given bit. + * + * \param _index The bit position. + */ + void set_on(const std::size_t &_index) { + *this |= _index; + } + + /** + * \brief Toggle off a given bit. + * + * \param _index The bit position. + */ + void set_off(const std::size_t &_index) { + std::size_t boardIndex = _index / BITBOARD_INT_SIZE; + std::uint64_t bit = 1ULL << (_index % BITBOARD_INT_SIZE); + + boards[boardIndex] &= ~bit; + } + + bool is_on(const std::size_t &_index) const { + std::size_t boardIndex = _index / BITBOARD_INT_SIZE; + std::uint64_t bit = 1ULL << (_index % BITBOARD_INT_SIZE); + + return (boards[boardIndex] & bit) != 0; + } + + size_t size() const { return _size ; } +}; + +} /* end namespace OSM */ +} /* end namespace CGAL */ + +#endif // CGAL_OSM_BITBOARD_H diff --git a/HDVF/include/CGAL/OSM/OSM.h b/HDVF/include/CGAL/OSM/OSM.h new file mode 100644 index 00000000000..4fa7c6fc264 --- /dev/null +++ b/HDVF/include/CGAL/OSM/OSM.h @@ -0,0 +1,28 @@ +// Copyright (c) 2025 LIS Marseille (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Alexandra Bac +// Kevin Fedyna + + +/* Include all the Optimised Sparsed Matrix (OSM) library. + * Should be included directly. + */ + +#ifndef CGAL_OSM_OSM_H +#define CGAL_OSM_OSM_H + +#include + +//#define DEBUG +#include +#include +#include + +#endif // CGAL_OSM_OSM_H diff --git a/HDVF/include/CGAL/OSM/Sparse_chain.h b/HDVF/include/CGAL/OSM/Sparse_chain.h new file mode 100644 index 00000000000..658e16f5f1e --- /dev/null +++ b/HDVF/include/CGAL/OSM/Sparse_chain.h @@ -0,0 +1,808 @@ +// Copyright (c) 2025 LIS Marseille (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Alexandra Bac +// Kevin Fedyna + +#ifndef CGAL_OSM_SPARSE_CHAIN_H +#define CGAL_OSM_SPARSE_CHAIN_H + +#include + +#include +#include +#include +#include +#include +#include + +namespace CGAL { +namespace OSM { + +/*! + \ingroup PkgHDVFAlgorithmClasses + + The class `Sparse_chain` implements the concept `SparseChain`, that is, sparse vectors (encoding homological chains) optimized for topological computations. + Given a complex (for instance a simplicial, cubical or cellular complex) which cells in a given dimension \f$q\f$ are numbered + \f$\{\sigma_i\,;\, i=1\ldots n_q\}\f$, homology with coefficients in a given ring \f$\mathbb K\f$ defines \f$q\f$-chains as formal linear combinations: + \f\[\gamma = \sum_{i=1}^{n_q}\lambda_i\cdot \sigma_i\f\] + with coefficients \f$\lambda_i\in\mathbb K\f$. + In the basis \f$\{\sigma_i\,;\, i=1\ldots n_q\}\f$, the coordinates of \f$\gamma\f$ are given by the vector: \f$[\lambda_1,\ldots \lambda_{n_q}]\f$. + + Now, as chains considered in homology are boundaries of cells, most \f$\lambda_i\f$ coefficients are null. + Hence `Sparse_chain` encodes such vectors in a "sparse" way, that is, storing only non zero coefficients through a map: + \f\[i\mapsto \lambda_i\ \ \ \forall i=1\ldots n_q\text{ such that }\lambda_i\neq 0\f\] + Moreover, as per any linear algebra vector, a `Sparse_chain` is either a column or row vector (the `StorageFormat` parameter determines this storage format). + + The class `Sparse_chain` provides standard linear algebra operators and fast iterators and block operations (set, get and nullify) which are required to implement efficiently HDVFs. + + \cgalModels{SparseChain} + + \tparam CoefficientRing a model of the `IntegralDomainWithoutDivision` concept, providing the ring used to compute homology. + \tparam StorageFormat an integer constant encoding the storage format of matrices (`CGAL::OSM::COLUMN` or `CGAL::OSM::ROW`). +*/ + +template +class Sparse_chain { + +public: + /*! Type of the coefficient ring */ + typedef CoefficientRing Coefficient_ring; + + /*! + Type of chains iterators. + */ + typedef typename std::unordered_map::iterator iterator; + + /*! + Type of chains constant iterators. + */ + typedef typename std::unordered_map::const_iterator const_iterator; + + // Allow the Sparse_matrix class to access other templated Sparse_matrix and + // Sparse_chain protected members. + template + friend class Sparse_chain; + + template + friend class Sparse_matrix; + +protected: + /* \brief Type of data stored in the chain: map between indices and coefficients. */ + typedef std::pair pair; + + /* \brief The chain inner representation and storage of data. */ + std::unordered_map _chainData; + + /* \brief The chain boundary. */ + size_t _upperBound; + +public: + /** + * \brief Creates new empty sparse chain. + * + * Creates a sparse chain encoding an empty linear combination of cells. + */ + Sparse_chain() + : _upperBound(0), _chainData() + {} + + /** + * \brief Creates new empty sparse chain (ie. zero-chain) of given size. + * + * Constructor with size, initializes an empty sparse chain encoding a linear combination of cells with all coefficients null. + * + * \param chain_size The size of the sparse chain. + */ + Sparse_chain(const size_t chain_size) + : _upperBound(chain_size), _chainData() + {} + + + + /** + * \brief Creates new SparseChain by copy. + * + * Copy constructor, initialize a sparse chain from an existing sparse chain. + * + * \pre The chains have the same `CoefficientRing` and `StorageFormat`. + + * \param otherToCopy The chain to copy. + */ + Sparse_chain(const Sparse_chain &otherToCopy) + : _upperBound(otherToCopy._upperBound), _chainData(otherToCopy._chainData) + {} + + /** + * \brief Assigns to other chain. + * + * Assign to other chain coefficient-wise, equivalent to copying it. + * + * \pre The chains have the same coefficent type. + * + * \warning Chains must have the same `CoefficientRing`. + * + * \param otherToCopy The chain we want to copy. + */ + Sparse_chain& operator=(const Sparse_chain &otherToCopy) { + _upperBound = otherToCopy._upperBound; + _chainData = otherToCopy._chainData; + + return *this; + } + + /** + * \brief Size of the chain + * + * \return Size allocated for the chain. + */ + size_t dimension() const { return _upperBound ; } + + /** \relates Sparse_chain + * + * \brief writes a sparse chain in the output stream. + * + * \param stream The output stream. + * \param chain The chain to display. + * + * \return A reference to the modified stream. + */ + friend std::ostream& operator<<(std::ostream &stream, const Sparse_chain &chain) { + stream << "["; + for (const_iterator i = chain._chainData.begin() ; i != chain._chainData.end() ; ++i) { + stream << i->first << ": " << i->second << ", "; + } + + if (chain._chainData.size() > 0) { + stream << "\b\b"; + } + stream << "]"; + + return stream; + } + + /** + * \brief Adds two chains. + * + * Adds two chains and return the result in a new matrix. + * + * \pre Chains must have the same `CoefficientRing` and the same `StorageFormat`. + * + * \warning Will raise an error if the two chains are not the same `CoefficientRing`. + * \warning Will raise a compilation error if the two chains don't have the same `StorageFormat`. + * + * \param other The other chain. + * + * \return A new chain representing the result. + */ + Sparse_chain operator+(const Sparse_chain &other) { + Sparse_chain newChain = *this; + newChain += other; + + return newChain; + } + + /** + * \brief Subtracts a chain from current chain. + * + * Subtract `other` chain from current chain and return the result in a new matrix. + * + * \pre Chains must have the same `CoefficientRing` and the same `StorageFormat`. + * + * \warning Will raise an error if the two chains are not the same `CoefficientRing`. + * \warning Will raise a compilation error if the two chains don't have the same `StorageFormat`. + * + * \param other The other chain. + * + * \return A new chain representing the result. + */ + Sparse_chain operator-(const Sparse_chain &other) { + Sparse_chain newChain = *this; + newChain -= other; + + return newChain; + } + + /*! \relates Sparse_chain + * + * \brief Applies multiplication on each coefficient. + * + * \param lambda The factor to apply. + * \param chain The chain. + * + * \return A new chain representing the result. + */ + template + friend Sparse_chain operator*(const CoefficientRing& lambda, const Sparse_chain &chain) { + Sparse_chain newChain = chain; + newChain *= lambda; + + return newChain; + } + + /** + * \brief Applies multiplication on each coefficient. + * + * \param lambda The factor to apply. + * + * \return A new chain representing the result. + */ + Sparse_chain operator*(const CoefficientRing& lambda) { + Sparse_chain newChain = *this; + newChain *= lambda; + + return newChain; + } + + /** \relates Sparse_chain + * + * \brief Performs matrix multiplication between two chains (COLUMN x ROW) and return a COLUMN matrix. + * + * Generate a column-based matrix from the matrix multiplication and return it. + * + * \pre Chains must have the same `CoefficientRing`. + * + * \warning Will raise an error if chains do not have the same `CoefficientRing`. + * + * \param column The column chain. + * \param row The row chain. + * + * \return The result of the matrix multiplication, column-based. + */ + template + friend Sparse_matrix<_CT, COLUMN> operator*(const Sparse_chain<_CT, COLUMN>& column, const Sparse_chain<_CT, ROW>& row); + + /** \relates Sparse_chain + * + * \brief Performs matrix multiplication between two chains (COLUMN x ROW) and return a ROW matrix. + * + * Generate a row-based matrix from the matrix multiplication and return it. + * + * \pre Chains must have the same `CoefficientRing`. + * + * \warning Will raise an error if chains do not have the same `CoefficientRing`. + * + * \param column The column chain. + * \param row The row chain. + * + * \return The result of the matrix multiplication, row-based. + */ + template + friend Sparse_matrix<_CT, ROW> operator%(const Sparse_chain<_CT, COLUMN> &column, const Sparse_chain<_CT, ROW> &row); + + /** \relates Sparse_chain + * + * \brief Performs dot product between two chains (ROW x COLUMN). + * + * \pre Chains must have the same `CoefficientRing`. + * + * \warning Will raise an error if the chains do not have the same `CoefficientRing`. + * + * \param row The row chain. + * \param column The column chain. + * + * \return The result of type CoefficientRing. + */ + template + friend _CT operator*(const Sparse_chain<_CT, ROW> &row, const Sparse_chain<_CT, COLUMN> &column); + + /** + * \brief Adds a chain to `this`. + * + * Add a chain to `this`. + * + * \pre Chains must have the same `CoefficientRing` and the same `StorageFormat`. + * + * \warning Will raise an error if the two chains are not the same `CoefficientRing`. + * \warning Will raise an error if the two chains don't have the same `StorageFormat`. + * + * \param other The other chain. + * + * \return The modified chain representing the result. + */ + Sparse_chain& operator+=(const Sparse_chain &other) { + if (this->_upperBound != other._upperBound) { + throw std::runtime_error("Chains must be the same size."); + } + + for (pair pair: other._chainData) { + this->_chainData[pair.first] += pair.second; + + if (this->_chainData[pair.first] == 0) { + this->_chainData.erase(pair.first); + } + } + + return *this; + } + + /** + * \brief Sustracts a chain to `this`. + * + * Subtract a chain to `this`. + * + * \pre Chains must have the same `CoefficientRing` and the same `StorageFormat`. + * + * \warning Will raise an error if the two chains are not the same `CoefficientRing`. + * \warning Will raise an error if the two chains don't have the same `StorageFormat`. + * + * \param other The other chain. + * + * \return The modified chain representing the result. + */ + Sparse_chain& operator-=(const Sparse_chain &other) { + if (this->_upperBound != other._upperBound) { + throw std::runtime_error("Chains must be the same size."); + } + + for (pair pair: other._chainData) { + this->_chainData[pair.first] -= pair.second; + + if (this->_chainData[pair.first] == 0) { + this->_chainData.erase(pair.first); + } + } + + return *this; + } + + /** + * \brief Applies multiplication on each coefficient of `this`. + * + * If `lambda` is null, this function comes to nullify the chain. + * + * \param lambda The factor to apply. + * + * \return The modified chain representing the result. + */ + Sparse_chain& operator*=(const CoefficientRing& lambda) { + if (lambda == 0) { + this->_chainData.clear(); + return *this; + } + + for (pair pair: this->_chainData) { + this->_chainData[pair.first] = pair.second * lambda; + } + + return *this; + } + + /** + * \brief Gets the value of a coefficient of the chain. + * + * \warning The chain will perform boundary check. + * + * \param index The coefficient index. + * + * \return The value of the coefficient. + */ + Coefficient_ring operator[](size_t index) const { + if (index >= _upperBound) { + throw std::runtime_error("Provided index should be less than " + std::to_string(_upperBound) + "."); + } + + if (_chainData.find(index) == _chainData.end()) + return 0 ; + else + return _chainData.at(index); + } + + /** + * \brief Gets the value of a coefficient of the chain. + * + * \warning The chain will perform boundary check. + * + * \param index The coefficient index. + * + * \return The value of the coefficient. + */ + inline Coefficient_ring get_coefficient(size_t index) const { + if (index >= _upperBound) { + throw std::runtime_error("Provided index should be less than " + std::to_string(_upperBound) + "."); + } + + if (_chainData.find(index) == _chainData.end()) + return 0 ; + else + return _chainData.at(index); + } + + /** + * \brief Sets a given coefficient of the chain. + * + * Set the value of the coefficient in the chain at `index`. + * + * \warning The chain will perform boundary check. + * + * \param index The coefficient index. + * \param d Value of the coefficient + */ + inline void set_coefficient(size_t index, Coefficient_ring d) + { + if (index >= _upperBound) { + throw std::runtime_error("Provided index should be less than " + std::to_string(_upperBound) + "."); + } + if (d == 0) + *this /= index ; + else + _chainData[index] = d ; + } + + /** + * \brief Checks if a coefficient is null. + * + * \param index The index to check. + * \return True if the data is null at given index. + */ + const bool is_null(size_t index) const { + return _chainData.find(index) == _chainData.end(); + } + + /** + * \brief Checks if the chain is null. + * + * \return True if the chain is null. + */ + const bool is_null() const { + return _chainData.size() == 0; + } + + + /** + * \brief Gets a subchain from the chain. + * + * Return a new chain where all coefficients of indices provided in the vector are removed. + * + * \note Will return a copy of the chain if `indices` is empty. + * + * \param indices The indices to remove. + * + * \return A new chain representing the result. + */ + Sparse_chain operator/(const std::vector &indices) { + Sparse_chain newChain = *this; + newChain /= indices; + return newChain; + } + + /** + * \brief Gets a subchain from the chain. + * + * Return a new chain where the coefficients at a given index is removed. + * + * \param index The index to remove. + */ + Sparse_chain operator/(size_t index) { + Sparse_chain newChain = *this; + newChain /= index; + return newChain; + } + + /** + * \brief Restricts the chain to a sub-chain by removing indices. + * + * Removes all indices provided in the vector from the chain. Return a reference to the modified chain. + * + * \note Will not alter the chain if `indices` is empty. + * + * \param indices The indices to remove. + * + * \return Return a reference to the modified chain. + */ + Sparse_chain& operator/=(const std::vector &indices) { + for (size_t index : indices) { + this->_chainData.erase(index); + } + + return *this; + } + + /** + * \brief Restricts the chain to a sub-chain by removing a given index. + * + * Removes the index provided from the chain. + * + * \note Will not alter the chain if given vector is empty. + * + * \param index The index to remove. + * + * \return Return a reference to the modified chain. + */ + Sparse_chain& operator/=(const size_t index) { + this->_chainData.erase(index); + + return *this; + } + + /** + * \brief Removes all coefficients from the chain. + * + * The function comes to set all coefficients to zero. + */ + void nullify() { + this->_chainData.clear(); + } + + /** + * \brief Iterator to the beginning of the chain. + * + * \warning The chain is stored in an unordered map for speed reason. + * + * \return The function returns an iterator to the first non zero index. + */ + iterator begin() noexcept { + return _chainData.begin(); + } + + /** + * \brief Constant iterator to the beginning of the chain. + * + * \warning The chain is stored unordered for speed reason. + * + * \return The function returns a constant iterator to the first non zero index. + */ + const_iterator begin() const noexcept { + return _chainData.begin(); + } + + /** + * \brief Constant iterator to the beginning of the chain. + * + * \warning The chain is stored unordered for speed reason. + * + * \return The function returns a constant iterator to the first non zero index. + */ + const_iterator cbegin() const noexcept { + return _chainData.cbegin(); + } + + /** + * \brief Iterator to the end of the chain. + * + * \warning The chain is stored unordered for speed reason. + * + * \return The function returns an iterator past-the-end of the chain. + */ + iterator end() noexcept { + return _chainData.end(); + } + + /** + * \brief Constant iterator to the end of the chain. + * + * \warning The chain is stored unordered for speed reason. + * + * \return The function returns a constant iterator past-the end of the chain. + */ + const_iterator end() const noexcept { + return _chainData.end(); + } + + /** + * \brief Constant iterator to the end of the chain. + * + * \warning The chain is stored unordered for speed reason. + * + * \return The function returns a constant iterator to the ending of the chain. + */ + const_iterator cend() const noexcept { + return _chainData.cend(); + } + + /** + * \brief Transposes a sparse chain. + * + * The result is a chain with `StorageFormat` switched between COLUMN and ROW. + * + * \return A new chain where the `StorageFormat` is changed. + */ + Sparse_chain transpose() { + Sparse_chain chain; + + chain._upperBound = this->_upperBound; + chain._chainData = this->_chainData; + + return chain; + } + + /** + * \brief Checks if chain is a column. + * + * \return true if chain is column-major, false otherwise. + */ + bool is_column() const { + return StorageFormat == COLUMN; + } + + /** + * \brief Checks if chain is a row. + * + * \return true if chain is row-major, false otherwise. + */ + bool is_row() const { + return StorageFormat == ROW; + } + +private: + /* + * \brief Get a reference on a coefficient of the chain. + * + * Used to set a coefficient in the chain. + * + * \warning The chain will perform boundary check. + * + * \param index The coefficient index. + * + * \return The reference to the assigned coefficient. + */ + Coefficient_ring& operator[](const size_t index) { + if (index >= _upperBound) { + throw std::runtime_error("Provided index should be less than " + std::to_string(_upperBound) + "."); + } + + return _chainData[index]; + } + + /** \relates Sparse_chain + * + * \brief Comparison of two `COLUMN` chains. + */ + template + friend bool operator==(const Sparse_chain<_CT, OSM::COLUMN>& chain, const Sparse_chain<_CT, OSM::COLUMN> &other); + + /** \relates Sparse_chain + * + * \brief Comparison of a `COLUMN` and a `ROW` chain. + */ + template + friend bool operator==(const Sparse_chain<_CT, OSM::COLUMN>& chain, const Sparse_chain<_CT, OSM::ROW> &other); + + /** \relates Sparse_chain + * + * \brief Comparison of a `ROW` and a `COLUMN` chain. + */ + template + friend bool operator==(const Sparse_chain<_CT, OSM::ROW>& chain, const Sparse_chain<_CT, OSM::COLUMN> &other); + + /** \relates Sparse_chain + * + * \brief Comparison of two `ROW` chains. + */ + template + friend bool operator==(const Sparse_chain<_CT, OSM::ROW>& chain, const Sparse_chain<_CT, OSM::ROW> &other); +}; + +// COLUMN chain x ROW chain -> COLUMN matrix +template +Sparse_matrix<_CT, COLUMN> operator*(const Sparse_chain<_CT, COLUMN> &column, const Sparse_chain<_CT, ROW> &row) { + Sparse_matrix<_CT, COLUMN> matrix(column._upperBound, row._upperBound); + + for (std::pair pair : row._chainData) { + OSM::set_column(matrix,pair.first,column * pair.second) ; + } + + return matrix; +} + +// COLUMN chain x ROW chain -> ROW matrix +template +Sparse_matrix<_CT, ROW> operator%(const Sparse_chain<_CT, COLUMN> &column, const Sparse_chain<_CT, ROW> &row) { + Sparse_matrix<_CT, ROW> matrix(column._upperBound, row._upperBound); + + for (std::pair pair : column._chainData) { + OSM::set_row(matrix,pair.first,row * pair.second); + } + + return matrix; +} + +// Dot product (ROW chain x COLUMN chain) +template +CoefficientRing operator*(const Sparse_chain &row, const Sparse_chain &column) { + // Get indices (avoid adding double indices). + std::unordered_map indices; + for (std::pair pair: row._chainData) { + indices[pair.first] = 1; + } + for (std::pair pair: column._chainData) { + indices[pair.first] += 1; + } + + // Perform dot product + CoefficientRing result = CoefficientRing(); + for (std::pair index: indices) { + if (index.second == 2) { + result += row._chainData.at(index.first) * column._chainData.at(index.first); + } + } + + return result; +} + +//// Get a subchain from the chain and assign. +//template +//Sparse_chain<_CT, _CTF> operator/(const Sparse_chain<_CT, _CTF> &chain, const std::vector &indices) { +// Sparse_chain<_CT, _CTF> newChain = chain; +// newChain /= indices; +// return newChain; +//} +// +//// Get a subchain from the chain and assign. +//template +//Sparse_chain<_CT, _CTF> operator/(const Sparse_chain<_CT, _CTF> &chain, size_t index) { +// Sparse_chain<_CT, _CTF> newChain = chain; +// newChain /= index; +// return newChain; +//} + +template +bool operator==(const Sparse_chain<_CT, OSM::COLUMN>& chain, const Sparse_chain<_CT, OSM::COLUMN> &other) +{ + typedef Sparse_chain<_CT, OSM::COLUMN> ChainType; + bool res = true ; + // Check that chains have the same size + res = res && (chain._upperBound == other._upperBound) ; + // Check that each coefficient of chain also belongs to other + for (typename ChainType::const_iterator it = chain.begin(); res && (it != chain.end()); ++it) + { + res = res && (it->second == other.get_coefficient(it->first)) ; + } + // Check that each coefficient of other also belongs to chain + for (typename ChainType::const_iterator it = other.begin(); res && (it != other.end()); ++it) + { + res = res && (it->second == chain.get_coefficient(it->first)) ; + } + return res ; +} + +template +bool operator==(const Sparse_chain<_CT, OSM::ROW>& chain, const Sparse_chain<_CT, OSM::ROW> &other) +{ + typedef Sparse_chain<_CT, OSM::ROW> ChainType; + bool res = true ; + // Check that chains have the same size + res = res && (chain._upperBound == other._upperBound) ; + // Check that each coefficient of chain also belongs to other + for (typename ChainType::const_iterator it = chain.begin(); (it != chain.end()) && res; ++it) + { + res = res && (it->second == other.get_coefficient(it->first)) ; + } + // Check that each coefficient of other also belongs to chain + for (typename ChainType::const_iterator it = other.begin(); res && (it != other.end()); ++it) + { + res = res && (it->second == chain.get_coefficient(it->first)) ; + } + return res ; +} + +template +bool operator==(const Sparse_chain<_CT, OSM::COLUMN>& chain, const Sparse_chain<_CT, OSM::ROW> &other) +{ + return false; +} + +template +bool operator==(const Sparse_chain<_CT, OSM::ROW>& chain, const Sparse_chain<_CT, OSM::COLUMN> &other) +{ + return false; +} + +template +Sparse_chain<_CT, _CTF> operator*(const Sparse_chain<_CT, _CTF> &chain, const _CT& lambda) { + Sparse_chain newChain = chain; + newChain *= lambda; + + return newChain; +} + +} /* end namespace OSM */ +} /* end namespace CGAL */ + +#endif // CGAL_OSM_SPARSE_CHAIN_H diff --git a/HDVF/include/CGAL/OSM/Sparse_matrix.h b/HDVF/include/CGAL/OSM/Sparse_matrix.h new file mode 100644 index 00000000000..73ef9f11ce1 --- /dev/null +++ b/HDVF/include/CGAL/OSM/Sparse_matrix.h @@ -0,0 +1,2077 @@ +// Copyright (c) 2025 LIS Marseille (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Alexandra Bac +// Kevin Fedyna + +#ifndef CGAL_OSM_SPARSE_MATRIX_H +#define CGAL_OSM_SPARSE_MATRIX_H + +#include + +#include +#include +#include +#include +#include + +#include +#include + +// DEBUG : matrix output for SparseMatrices / no DEBUG : chain output for SparseMatrices +//#define DEBUG + +namespace CGAL { +namespace OSM { + +/*! + \ingroup PkgHDVFAlgorithmClasses + + The class `Sparse_matrix` implements the concept `SparseMatrix`, that is, sparse matrices optimized for topological computations. It provides standard linear algebra operators and fast iterators and block operations (set, get and nullify) which are required to implement efficiently HDVFs. + + The implementation is based on mapped sparse matrices. Hence matrices of the `Sparse_matrix` class are either column of row major (the `StorageFormat` parameter determines this storage format). A column-major (resp. row-major) `Sparse_matrix` is a vector of `Sparse_chain` which encode columns (res. rows). Moreover, in order to efficiently iterate over non empty columns (resp. rows) the `Bitboard` data structure implements the concept `SparseMatrix::NonZeroChainIndices`. A bitboard is basically a bucket of bits recording the indices of non empty chains. However, this data structure has been designed in order to efficiently remove or add indices, as well as provide efficient iterators to visit non empty chains. + + For instance, let us consider the \f$5\times 4\f$ matrix: + \f[ + A = \left(\begin{array}{cccc} + 1 & \cdot & \cdot & \cdot \\ + -1 & \cdot & 2 & \cdot\\ + \cdot & \cdot & 1 & \cdot \\ + \cdot & \cdot & \cdot & \cdot \\ + \cdot & \cdot & \cdot & \cdot \\ + \end{array}\right) + \f] + where \f$\cdot\f$ means \f$0\f$. + + Figures below shows the data structures are created according to the chosen representation (left: column-major, right: row-major): + + + + + \cgalModels{SparseMatrix} + + \tparam CoefficientRing a model of the `IntegralDomainWithoutDivision` concept, providing the ring used to compute homology. + \tparam StorageFormat an integer constant encoding the storage format of matrices (`OSM::COLUMN` or `OSM::ROW`). +*/ + +template +class Sparse_matrix { + +public: + + /*! + Type of coefficient ring. + */ + typedef CoefficientRing Coefficient_ring; + + /*! + Type of chains associated to the matrix. + */ + typedef Sparse_chain Matrix_chain; + + // Allow the Sparse_matrix class to access other templated Sparse_matrix private members. + template + friend class Sparse_matrix; + +protected: + /* \brief The inner chain storage. */ + std::vector> _chains; + + /* \brief A bitboard containing state of each columns. */ + Bitboard _chainsStates; + + /* \brief The matrix size as a (row, column) pair. */ + std::pair _size; + + /* + * \brief Get a reference on a chain from a matrix and change its state (for assignment). + * + * Used only internally. + * + * \warning The operator changes the status of the chain and the matrix will perform boundary check. + * + * \param _index The coefficient index. + * + * \return The reference to the chain stored at given index. + */ + Matrix_chain& operator[](const size_t _index) { + if (StorageFormat == COLUMN && _index >= _size.second) { + throw std::runtime_error("Provided index should be less than " + std::to_string(_size.second) + "."); + } + if (StorageFormat == ROW && _index >= _size.first) { + throw std::runtime_error("Provided index should be less than " + std::to_string(_size.first) + "."); + } + + _chainsStates |= _index; + return _chains[_index]; + } + +public: + /** + * \brief Default constructor (empty new `Sparse_matrix` object). + * + * Create an empty matrix of type `StorageFormat` with coefficients of type `CoefficientRing`. + * The default matrix size is 0x0. + */ + Sparse_matrix() { + _chains = std::vector(0); + _chainsStates = Bitboard(0); + _size = {0, 0}; + } + + /** + * \brief Constructor with given rows/columns sizes. + * + * Create a new empty Sparse_matrix object of type `StorageFormat` with coefficients of type `CoefficientRing` and a given size along rows/columns. + * + * \param rowCount The number of rows to preallocate. + * \param columnCount The number of columns to preallocate. + */ + Sparse_matrix(const size_t rowCount, const size_t columnCount) { + size_t mainSize = StorageFormat == COLUMN ? columnCount : rowCount; + size_t secondarySize = StorageFormat == COLUMN ? rowCount : columnCount; + + _chains = std::vector>(mainSize); + for (size_t i = 0 ; i < mainSize ; i++) { + _chains[i] = Sparse_chain(secondarySize); + } + + _chainsStates = Bitboard(mainSize); + _size = {rowCount, columnCount}; + } + + /** + * \brief Copy constructor. + * + * Create a new SparseMatrix from another SparseMatrix object (with possibly a different `StorageFormat`). Initialize a SparseMatrix of same sizes, containing the same coefficients (but not necessarly of the same `StorageFormat`). + * If types are different, the constructor performs conversion. + * + * \param otherToCopy The matrix copied. + */ + template + Sparse_matrix(const Sparse_matrix &otherToCopy) { + if (StorageFormat == CTF) + { + _chainsStates = otherToCopy._chainsStates; + _size = otherToCopy._size; + // Copy of _chains as such + _chains.resize(otherToCopy._chains.size()) ; + for (size_t i = 0; i& tmp(otherToCopy._chains.at(i)) ; + Sparse_chain res(tmp.dimension()) ; + for (typename Sparse_chain::const_iterator it = tmp.cbegin(); it != tmp.cend(); ++it) + { + res[it->first] = it->second ; + } + _chains[i] = res ; + } + } + else // Copy coefficient by coefficient + { + _size = otherToCopy._size; + size_t vec_size = (StorageFormat == OSM::COLUMN)?_size.second:_size.first ; + size_t chain_size = (StorageFormat == OSM::COLUMN)?_size.first:_size.second ; + _chains.resize(vec_size) ; + _chainsStates = OSM::Bitboard(vec_size) ; + for (size_t i=0; i(chain_size) ; + } + + for (size_t i = 0; i& tmp(otherToCopy._chains.at(i)) ; + for (typename Sparse_chain::const_iterator it = tmp.cbegin(); it != tmp.cend(); ++it) + { + _chains[it->first][i] = it->second ; + _chainsStates.set_on(it->first) ; + } + } + } + } + + /** + * \brief Assigns to other matrix. + * + * Assign to other matrix coefficient-wise, equivalent to copying it. + * + * \pre The matrices must have the same type. + * + * \param otherToCopy The matrix we want to copy. + * + * \return The reference to the modified matrix. + */ + Sparse_matrix& operator=(const Sparse_matrix& otherToCopy) + { + _chainsStates = otherToCopy._chainsStates; + _size = otherToCopy._size; + _chains = otherToCopy._chains; + + return *this; + } + + + /** + * \brief Cleans a SparseMatrix (set all coefficients to zero). + * + * Empty all structures of the sparse matrix. + */ + void nullify() { + std::vector coefs ; + for (OSM::Bitboard::iterator it = begin(); it != end(); ++it) + { + coefs.push_back(*it) ; + } + *this /= coefs ; + } + + /** + * \brief Tests if a SparseMatrix is null. + * + * The function return `true` is the SparseMatrix is null (that is, empty) and `false` otherwise. + */ + bool is_null() + { + return (_chainsStates.begin() == _chainsStates.end()) ; + } + + /** + * \defgroup MatrixMatrixComparison Compares two matrices. + * \ingroup PkgHDVFAlgorithmClasses + * @brief Compare two matrices and return `true` if both matrices equal (and `false` otherwise). + * + * @param matrix The first matrix. + * @param other The second matrix. + * @return A boolean. + * @{ + */ + + /** \relates Sparse_matrix + * + * \brief Comparison of two COLUMN matrices. + */ + template + friend bool operator==(const Sparse_matrix<_CT, OSM::COLUMN>& matrix, const Sparse_matrix<_CT, OSM::COLUMN> &other); + + /** \relates Sparse_matrix + * + * \brief Comparison of a COLUMN and a ROW matrix. + */ + template + friend bool operator==(const Sparse_matrix<_CT, OSM::COLUMN>& matrix, const Sparse_matrix<_CT, OSM::ROW> &other); + + /** \relates Sparse_matrix + * + * \brief Comparison of a ROW and a COLUMN matrix. + */ + template + friend bool operator==(const Sparse_matrix<_CT, OSM::ROW>& matrix, const Sparse_matrix<_CT, OSM::COLUMN> &other); + + /** \relates Sparse_matrix + * + * \brief Comparison of two ROW matrices. + */ + template + friend bool operator==(const Sparse_matrix<_CT, OSM::ROW>& matrix, const Sparse_matrix<_CT, OSM::ROW> &other); + + /** @} */ + + /** \relates Sparse_matrix + * + * \brief Writes a sparse matrix in the output stream. + * + * \param stream The output stream. + * \param matrix The matrix to display. + * + * \return A reference to the modified stream. + */ + friend std::ostream& operator<<(std::ostream &stream, const Sparse_matrix &matrix) { +#ifndef DEBUG + bool empty = true; + + stream << "["; + for (size_t index : matrix._chainsStates) { + empty = false; + stream << index << ": " << matrix._chains[index] << ", "; + } + + if (!empty) { + stream << "\b\b"; + } + stream << "]" << std::endl ; +#else + for (size_t i=0; i + friend std::ostream& write_matrix (const Sparse_matrix<_CT, OSM::COLUMN>& M, std::ostream& out); + + /** \relates Sparse_matrix + * + * \brief Writes a sparse ROW matrix to a stream. + */ + template + friend std::ostream& write_matrix (const Sparse_matrix<_CT, OSM::ROW>& M, std::ostream& out); + + /** \relates Sparse_matrix + * + * \brief Writes a sparse COLUMN matrix to a file. + */ + template + friend void write_matrix (const Sparse_matrix<_CT, OSM::COLUMN>& M, std::string filename); + + /** \relates Sparse_matrix + * + * \brief Writes a sparse ROW matrix to a file. + */ + template + friend void write_matrix (const Sparse_matrix<_CT, OSM::ROW>& M, std::string filename); + + /** @} */ + + /** \relates Sparse_matrix + * + * \defgroup ReadMatrix Reads matrix from an input stream. + * \ingroup PkgHDVFAlgorithmClasses + * @brief Read a sparse matrix from an input stream or a file using the `.osm` file format. + * + * Read a sparse matrix (the input must respect the `.osm` file format). + * + * @{ + */ + + /** \relates Sparse_matrix + * + * \brief Reads a sparse COLUMN matrix from a stream. + */ + template + friend std::istream& read_matrix (Sparse_matrix<_CT, OSM::COLUMN>& M, std::istream& in); + + /** \relates Sparse_matrix + * + * \brief Reads a sparse ROW matrix from a stream. + */ + template + friend std::istream& read_matrix (Sparse_matrix<_CT, OSM::ROW>& M, std::istream& in); + + /** \relates Sparse_matrix + * + * \brief Reads a sparse COLUMN matrix from a file. + */ + template + friend void read_matrix (Sparse_matrix<_CT, OSM::COLUMN>& M, std::string filename); + + /** \relates Sparse_matrix + * + * \brief Reads a sparse ROW matrix from a file. + */ + template + friend void read_matrix (Sparse_matrix<_CT, OSM::ROW>& M, std::string filename); + + /** @} */ + + /** + * \brief Adds two matrices into a new matrix. + * + * Adds each coefficient of the matrices and returns a new matrix (of the same type as `this`) representing the result (when possible, prefer `+=` for efficiency). + * + * \pre Matrices must have the same `CoefficientRing` but can have different `StorageFormat`. + * + * \warning Will raise an error if the other matrix is not the same `CoefficientRing`. + * + * \param other The second matrix. + * + * \return A new matrix representing the result. + */ + template + Sparse_matrix operator+(const Sparse_matrix &other) { + Sparse_matrix newMatrix(*this); + newMatrix += other; + + return newMatrix; + } + + /** + * \brief Subtracts two matrices into a new matrix. + * + * Subtracts each coefficient of the matrix `other` and returns a new matrix (of the same type as `this`) representing the result (when possible, prefer `-=` for efficiency). + * + * \pre Matrices must have the same `CoefficientRing` but can have different `StorageFormat`. + * + * \warning Will raise an error if the other matrix is not the same `CoefficientRing`. + * + * \param other The second matrix. + * + * \return A new matrix representing the result. + */ + template + Sparse_matrix operator-(const Sparse_matrix &other) { + Sparse_matrix newMatrix(*this); + newMatrix -= other; + + return newMatrix; + } + + /** \relates Sparse_matrix + * + * \brief Applies factor on each coefficients into a new matrix. + * + * This method creates a new matrix obtained by multiplying the matrix by a scalar factor `lambda`. + * + * If `lambda` is zero, the function comes to nullify the matrix (when possible, prefer `*=` for efficiency). + * + * \param lambda The factor to apply. + * \param matrix The matrix. + * + * \return A new matrix representing the result. + */ + template + friend Sparse_matrix operator*(const _CT& lambda, const Sparse_matrix<_CT, _CTF> &matrix) { + Sparse_matrix newMatrix = matrix; + newMatrix *= lambda; + + return newMatrix; + } + + /** + * \brief Applies factor on each coefficients into a new matrix. + * + * This method creates a new matrix obtained by multiplying the matrix by a scalar factor `lambda`. + * + * If `lambda` is zero, the function comes to nullify the matrix (when possible, prefer `*=` for efficiency). + * + * \param lambda The factor to apply. + * + * \return A new matrix representing the result. + */ + Sparse_matrix operator*(const CoefficientRing& lambda) { + Sparse_matrix newMatrix = *this; + newMatrix *= lambda; + + return newMatrix; + } + + /** \relates Sparse_matrix + * + * \defgroup MatrixMatrixProdCol Matrix multiplication (with column-based result). + * \ingroup PkgHDVFAlgorithmClasses + * @brief Perform multiplication of matrices and return a new column-major matrix. + * + * Perform standard linear algebra multiplication of matrices and return a new column-major matrix (when possible, prefer `*=` for efficiency). Matrices must have the same `CoefficientRing` but can have different `StorageFormat`. The multiplication is optimized (using standard definition or block multiplications) for each combination of `StorageFormat`. However, efficiency depends on `StorageFormat` (when possible, prefer row-major by column-major multiplications). + * + * @param first The first matrix. + * @param second The second matrix. + * @return The result of the matrix multiplication, column-based. + * @{ + */ + + /** \relates Sparse_matrix + * + * \brief Matrix multiplication: COLUMN x COLUMN -> COLUMN + */ + template + friend Sparse_matrix<_CT, COLUMN> operator*(const Sparse_matrix<_CT, COLUMN> &first, const Sparse_matrix<_CT, COLUMN> &second); + + /** \relates Sparse_matrix + * + * \brief Matrix multiplication: ROW x COLUMN -> COLUMN + */ + template + friend Sparse_matrix<_CT, COLUMN> operator*(const Sparse_matrix<_CT, ROW> &first, const Sparse_matrix<_CT, COLUMN> &second); + + /** \relates Sparse_matrix + * + * \brief Matrix multiplication: COLUMN x ROW -> COLUMN + */ + template + friend Sparse_matrix<_CT, COLUMN> operator*(const Sparse_matrix<_CT, COLUMN> &first, const Sparse_matrix<_CT, ROW> &second); + + /** \relates Sparse_matrix + * + * \brief Matrix multiplication: ROW x ROW -> COLUMN + */ + template + friend Sparse_matrix<_CT, COLUMN> operator*(const Sparse_matrix<_CT, ROW> &first, const Sparse_matrix<_CT, ROW> &second); + + /** @} */ + + /** \relates Sparse_matrix + * + *\defgroup MatrixChainProd Matrix / column-chain multiplication (with column-chain result). + * \ingroup PkgHDVFAlgorithmClasses + * @brief Perform multiplication of a sparse matrix (column or row major) and a column-chain. The function returns a new column-major chain. + * + * Perform standard linear algebra multiplication of a matrix and a column-chain (ie.\ matrix / column vector multiplication) and returns a new column-major chain. Both arguments must have the same `CoefficientRing` but the matrix can have any `StorageFormat` (and the multiplication is optimized for each of them). + * + * @param matrix The matrix. + * @param column The column-major chain. + * @return The result of the matrix multiplication, column-based. + * @{ + */ + + /** \relates Sparse_matrix + * + * \brief Matrix/column chain multiplication: COLUMN matrix x COLUMN chain -> COLUMN chain. + */ + template + friend Sparse_chain<_CT, COLUMN> operator*(const Sparse_matrix<_CT, COLUMN> &_matrix, const Sparse_chain<_CT, COLUMN> &_column); + + /** \relates Sparse_matrix + * + * \brief Matrix/column chain multiplication: ROW matrix x COLUMN chain -> COLUMN chain. + */ + template + friend Sparse_chain<_CT, COLUMN> operator*(const Sparse_matrix<_CT, ROW> &_matrix, const Sparse_chain<_CT, COLUMN> &_column); + + /** @} */ + + /** \relates Sparse_matrix + * + * \defgroup ChainMatrixProd Row-chain / matrix multiplication (with row-chain result). + * \ingroup PkgHDVFAlgorithmClasses + * @brief Perform multiplication of a row-chain and a sparse matrix (column or row major). The function returns a new row-major chain. + * + * Perform standard linear algebra multiplication of a row-chain and a matrix (ie.\ row vector / matrix multiplication) and returns a new row-major chain. Both arguments must have the same `CoefficientRing` but the matrix can have any `StorageFormat` (and the multiplication is optimized for each of them). + * + * @param row The row-major chain. + * @param matriw The matrix. + * @return The result of the matrix multiplication, row-based. + * @{ + */ + + /** \relates Sparse_matrix + * + * \brief Row chain/matrix multiplication: ROW chain x COLUMN matrix -> ROW chain. + */ + template + friend Sparse_chain<_CT, ROW> operator*(const Sparse_chain<_CT, ROW> &_row, const Sparse_matrix<_CT, ROW> &_matrix) ; + + /** \relates Sparse_matrix + * + * \brief Row chain/matrix multiplication: ROW chain x ROW matrix -> ROW chain. + */ + template + friend Sparse_chain<_CT, ROW> operator*(const Sparse_chain<_CT, ROW> &_row, const Sparse_matrix<_CT, COLUMN> &_matrix) ; + + /** @} */ + + /** \relates Sparse_matrix + * + * \defgroup MatrixMatrixProdRow matrix multiplication (with row-based result). + * \ingroup PkgHDVFAlgorithmClasses + * @brief Perform multiplication of matrices and return a new row-major matrix. + * + * Perform standard linear algebra multiplication of matrices and return a new row-major matrix (when possible, prefer `*=` for efficiency). Matrices must have the same `CoefficientRing` but can have different `StorageFormat`. The multiplication is optimized (using standard definition or block multiplications) for each combination of `StorageFormat`. However, efficiency depends on `StorageFormat`. + * + * @param first The first matrix. + * @param second The second matrix. + * @return The result of the matrix multiplication, row-based. + * @{ + */ + + /** \relates Sparse_matrix + * + * \brief matrix multiplication: COLUMN x COLUMN -> ROW + */ + template + friend Sparse_matrix<_CT, ROW> operator%(const Sparse_matrix<_CT, COLUMN> &_first, const Sparse_matrix<_CT, COLUMN> &_second); + + /** \relates Sparse_matrix + * + * \brief matrix multiplication: ROW x COLUMN -> ROW + */ + template + friend Sparse_matrix<_CT, ROW> operator%(const Sparse_matrix<_CT, ROW> &_first, const Sparse_matrix<_CT, COLUMN> &_second); + + /** \relates Sparse_matrix + * + * \brief matrix multiplication: COLUMN x ROW -> ROW + */ + template + friend Sparse_matrix<_CT, ROW> operator%(const Sparse_matrix<_CT, COLUMN> &_first, const Sparse_matrix<_CT, ROW> &_second); + + /** \relates Sparse_matrix + * + * \brief matrix multiplication: ROW x ROW -> ROW + */ + template + friend Sparse_matrix<_CT, ROW> operator%(const Sparse_matrix<_CT, ROW> &_first, const Sparse_matrix<_CT, ROW> &_second); + + /** @} */ + + /** \relates Sparse_matrix + * + * \defgroup MatrixMatrixAddAssign Sums matrices and assign. + * \ingroup PkgHDVFAlgorithmClasses + * @brief Perform addition of matrices and assign the result to `matrix`. + * + * Perform standard linear algebra addition. Matrices must have the same `CoefficientRing` but can have different `StorageFormat`. The addition is optimized for each combination of `StorageFormat`. + * + * @param matrix The first matrix. + * @param other The second matrix. + * @return A reference to the modified matrix `matrix` containing the result. + * @{ + */ + + /** \relates Sparse_matrix + * + * \brief Matrices sum and assign: COLUMN += COLUMN or ROW += ROW. + */ + Sparse_matrix& operator+=(const Sparse_matrix &other) { + if (this->_size != other._size) { + throw std::runtime_error("Matrices must be the same _size."); + } + + for (size_t index: other._chainsStates) { + this->_chainsStates |= index; + this->_chains[index] += other._chains[index]; + + if (this->_chains[index].is_null()) { + this->_chainsStates.set_off(index); + } + } + + return *this; + } + + /** \relates Sparse_matrix + * + * \brief Matrices sum and assign: COLUMN += ROW. + */ + template + friend Sparse_matrix<_CT, COLUMN>& operator+=(Sparse_matrix<_CT, COLUMN> &matrix, const Sparse_matrix<_CT, ROW> &other); + + /** \relates Sparse_matrix + * + * \brief Matrices sum and assign: ROW += COLUMN. + */ + template + friend Sparse_matrix<_CT, ROW>& operator+=(Sparse_matrix<_CT, ROW> &matrix, const Sparse_matrix<_CT, COLUMN> &other); + + /** @} */ + + /** \relates Sparse_matrix + * + * \defgroup MatrixMatrixSubtractAssign Subtracts matrices and assign. + * \ingroup PkgHDVFAlgorithmClasses + * @brief Perform subtraction of matrices and assign the result to `matrix`. + * + * Perform standard linear algebra subtraction. Matrices must have the same `CoefficientRing` but can have different `StorageFormat`. The addition is optimized for each combination of `StorageFormat`. + * + * @param matrix The first matrix. + * @param other The second matrix. + * @return A reference to the modified matrix `matrix` containing the result. + * @{ + */ + + /** \relates Sparse_matrix + * + * \brief Matrices subtraction and assign: COLUMN -= COLUMN or ROW -= ROW. + */ + Sparse_matrix& operator-=(const Sparse_matrix &other) { + if (this->_size != other._size) { + throw std::runtime_error("Matrices must be the same _size."); + } + + for (size_t index: other._chainsStates) { + this->_chainsStates |= index; + this->_chains[index] -= other._chains[index]; + + if (this->_chains[index].is_null()) { + this->_chainsStates.set_off(index); + } + } + + return *this; + } + + /** \relates Sparse_matrix + * + * \brief Matrices subtraction and assign: COLUMN -= ROW. + */ + template + friend Sparse_matrix<_CT, COLUMN>& operator-=(Sparse_matrix<_CT, COLUMN> &matrix, const Sparse_matrix<_CT, ROW> &other); + + /** \relates Sparse_matrix + * + * \brief Matrices subtraction and assign: ROW -= COLUMN. + */ + template + friend Sparse_matrix<_CT, ROW>& operator-=(Sparse_matrix<_CT, ROW> &matrix, const Sparse_matrix<_CT, COLUMN> &other); + + /** @} */ + + /** + * \brief Applies factor on each coefficients and assign. + * + * If `lambda` is 0, this comes to nullify the matrix. + * + * \param lambda The factor to apply. + * + * \return The modified matrix representing the result. + */ + Sparse_matrix& operator*=(const CoefficientRing& lambda) { + if (lambda == 0) { + this->nullify(); + return *this; + } + + for (size_t index: this->_chainsStates) { + this->_chains[index] *= lambda; + } + + return *this; + } + + /** + * \brief Computes the negative of a matrix (unary operator). + * + * \return The resulting matrix. + */ + Sparse_matrix operator-() { + Sparse_matrix res(this->_size.first, this->_size.second) ; + + for (size_t index: this->_chainsStates) + { + const Matrix_chain& tmp_chain(this->_chains[index]) ; + for (typename Matrix_chain::const_iterator it = tmp_chain.cbegin(); it != tmp_chain.cend(); ++it) + res[index][it->first] = -it->second ; + } + return res ; + } + + /** \relates Sparse_matrix + * + * \defgroup MatrixMatrixProdAssign Multiplies matrices and assign. + * \ingroup PkgHDVFAlgorithmClasses + * @brief Perform multiplication of matrices and assign the result to `matrix`. + * + * Perform standard linear algebra multiplication. Matrices must have the same `CoefficientRing` but can have different `StorageFormat`. The multiplication is optimized for each combination of `StorageFormat`. + * + * @param matrix The first matrix. + * @param other The second matrix. + * @return A reference to the modified matrix `matrix` containing the result. + * @{ + */ + + /** \relates Sparse_matrix + * + * \brief Matrix multiplication and assign: COLUMN *= COLUMN. + */ + template + friend Sparse_matrix<_CT, COLUMN>& operator*=(Sparse_matrix<_CT, COLUMN> &matrix, const Sparse_matrix<_CT, COLUMN> &other); + + /** \relates Sparse_matrix + * + * \brief Matrix multiplication and assign: ROW *= ROW. + */ + template + friend Sparse_matrix<_CT, ROW>& operator*=(Sparse_matrix<_CT, ROW> &matrix, const Sparse_matrix<_CT, ROW> &other); + + /** \relates Sparse_matrix + * + * \brief Matrix multiplication and assign: COLUMN *= ROW. + */ + template + friend Sparse_matrix<_CT, COLUMN>& operator*=(Sparse_matrix<_CT, COLUMN> &matrix, const Sparse_matrix<_CT, ROW> &other); + + /** \relates Sparse_matrix + * + * \brief Matrix multiplication and assign: ROW *= COLUMN. + */ + template + friend Sparse_matrix<_CT, ROW>& operator*=(Sparse_matrix<_CT, ROW> &matrix, const Sparse_matrix<_CT, COLUMN> &other); + + /** @} */ + + /** + * \brief Gets the value of a chain from a const matrix. + * + * \warning The matrix will perform boundary check. + * + * \param index The chain index. + * + * \return The chain stored at given index. + */ + Matrix_chain operator[](size_t index) const { + if (StorageFormat == COLUMN && index >= _size.second) { + throw std::runtime_error("Provided index should be less than " + std::to_string(_size.second) + "."); + } + if (StorageFormat == ROW && index >= _size.first) { + throw std::runtime_error("Provided index should be less than " + std::to_string(_size.first) + "."); + } + + return _chains[index]; + } + +protected: + // Protected method for set_coefficient + inline void set_coefficient(const size_t i, const size_t j, const Coefficient_ring d) { + if (i >= _size.first) { + throw std::runtime_error("Provided i index should be less than " + std::to_string(_size.first) + "."); + } + if (j >= _size.second) { + throw std::runtime_error("Provided j index should be less than " + std::to_string(_size.second) + "."); + } + + if (d != 0) + { + if (StorageFormat == COLUMN) + { + (*this)[j][i] = d ; + } + else + (*this)[i][j] = d ; + } + else + { + remove_coefficient(i, j); + } + } +public: + /** \relates Sparse_matrix + * + * \brief Sets a given coefficient in `matrix`. + * + * Assign the scalar `d` to the coefficient on row `i` and column `j`. + * + * \warning The matrix will perform boundary check. + * + * \param matrix Reference on the matrix to modify. + * \param i The row index. + * \param j The column index. + * \param d The value. + */ + template + friend void set_coefficient(Sparse_matrix<_CT, _CTF>& matrix, size_t i, size_t j, const _CT d); + +protected: + // Protected method for get_coefficient + inline Coefficient_ring get_coefficient(const size_t i, const size_t j) const { + if (i >= _size.first) { + throw std::runtime_error("Provided _i index should be less than " + std::to_string(_size.first) + "."); + } + if (j >= _size.second) { + throw std::runtime_error("Provided _j index should be less than " + std::to_string(_size.second) + "."); + } + + if (StorageFormat == COLUMN) + return (this->_chains)[j][i] ; + else // ROW + return (this->_chains)[i][j] ; + } +public: + /** \relates Sparse_matrix + * + * \brief Gets a given coefficient. + * + * Returns the coefficient on row `i` and column `j` of the matrix. + * + * \warning The matrix will perform boundary check. + * + * \param matrix Constant reference on the matrix. + * \param i The row index. + * \param j The column index. + * + * \return The value of the given coefficient. + */ + template + friend _CT get_coefficient(const Sparse_matrix<_CT, _CTF>& matrix, size_t i, size_t j); + + /** \relates Sparse_matrix + * + * \defgroup GetColumn Gets a column. + * \ingroup PkgHDVFAlgorithmClasses + * @brief Get the value of the column at a given `index` from the matrix (whatever the `StorageFormat` of the matrix). + * + * \note For column-matrices, it is equivalent to `operator[]`, for row-matrices a traversal of the matrix is required (in \f$\mathcal O(n)\f$). + * + * \warning The matrix will perform boundary check. + * + * @param matrix The matrix considered. + * @param index The coefficient index. + * @return The column at given index. + * @{ + */ + + /** \relates Sparse_matrix + * + * \brief Gets a column from a COLUMN matrix. + */ + template + friend Sparse_chain<_CT, COLUMN> get_column(const Sparse_matrix<_CT, COLUMN> &matrix, size_t index); + + /** \relates Sparse_matrix + * + * \brief Gets a column from a ROW matrix. + */ + template + friend Sparse_chain<_CT, COLUMN> get_column(const Sparse_matrix<_CT, ROW> &matrix, size_t index); + + /** @} */ + + /** \relates Sparse_matrix + * + * \defgroup GetRow Gets a row. + * \ingroup PkgHDVFAlgorithmClasses + * @brief Get the value of the row at a given `index` from the matrix (whatever the `StorageFormat` of the matrix). + * + * \note For row-matrices, it is equivalent to `operator[]`, for column-matrices a traversal of the matrix is required (in \f$\mathcal O(n)\f$). + * + * \warning The matrix will perform boundary check. + * + * @param matrix The matrix considered. + * @param index The coefficient index. + * @return The row at given index. + * @{ + */ + + /** \relates Sparse_matrix + * + * \brief Gets a row from a COLUMN matrix. + */ + template + friend Sparse_chain<_CT, ROW> get_row(const Sparse_matrix<_CT, COLUMN> &matrix, size_t index); + + /** \relates Sparse_matrix + * + * \brief Gets a row from a ROW matrix. + */ + template + friend Sparse_chain<_CT, ROW> get_row(const Sparse_matrix<_CT, ROW> &matrix, size_t index); + + /** @} */ + + /** \relates Sparse_matrix + * + * \brief Gets a const reference over a column from a column matrix. + * + * Constant time get. + * + * \warning The matrix will perform boundary check. + * + * \param matrix The matrix considered. + * \param index The column index. + * + * \return A constant reference over the column stored at given index. + */ + template + friend const Sparse_chain<_CT, COLUMN> & cget_column(const Sparse_matrix<_CT, COLUMN> &matrix, size_t index); + + /** \relates Sparse_matrix + * + * \brief Gets a constant reference over a row from a row matrix + * + * Constant time get. + * + * \warning The matrix will perform boundary check. + * + * \param matrix The matrix considered. + * \param index The row index. + * + * \return A const reference over the row stored at given index. + */ + template + friend const Sparse_chain<_CT, ROW> & cget_row(const Sparse_matrix<_CT, ROW> &matrix, const size_t index); + + + /** \relates Sparse_matrix + * + * \defgroup SetColumn Sets a column. + * \ingroup PkgHDVFAlgorithmClasses + * @brief Set the value of the column at a given `index` from the matrix to `column` (whatever the `StorageFormat` of the matrix). + * + * \note For column-matrices, it is equivalent to `operator[]` followed by an assignment, for row-matrices a traversal of the matrix is required (in \f$\mathcal O(n)\f$). + * + * \warning The matrix will perform boundary check. + * + * @param matrix The matrix. + * @param index The column index. + * @param column The new column value. + * @{ + */ + + /** \relates Sparse_matrix + * + * \brief Sets a column in a COLUMN matrix. + */ + template + friend void set_column(Sparse_matrix<_CT, COLUMN> &matrix, size_t index, const Sparse_chain<_CT, COLUMN> &column); + + /** \relates Sparse_matrix + * + * \brief Sets a column in a ROW matrix. + */ + template + friend void set_column(Sparse_matrix<_CT, ROW> &matrix, size_t index, const Sparse_chain<_CT, COLUMN> &column); + + /** @} */ + + /** \relates Sparse_matrix + * + * \defgroup SetRow Sets a row. + * \ingroup PkgHDVFAlgorithmClasses + * @brief Set the value of the row at a given `index` from the matrix to `chain` (whatever the `StorageFormat` of the matrix). + * + * \note For row-matrices, it is equivalent to `operator[]` followed by an assignment, for column-matrices a traversal of the matrix is required (in \f$\mathcal O(n)\f$). + * + * \warning The matrix will perform boundary check. + * + * @param matrix The matrix. + * @param index The row index. + * @param row The new row value. + * @{ + */ + + /** \relates Sparse_matrix + * + * \brief Sets a row in a COLUMN matrix. + */ + template + friend void set_row(Sparse_matrix<_CT, COLUMN> &matrix, size_t index, const Sparse_chain<_CT, ROW> &row); + + /** \relates Sparse_matrix + * + * \brief Sets a row in a ROW matrix. + */ + template + friend void set_row(Sparse_matrix<_CT, ROW> &matrix, size_t index, const Sparse_chain<_CT, ROW> &row); + + /** @} */ + + /** + * \defgroup GetBlockMatrix Gets a sub-matrix by removing chains in a matrix. + * \ingroup PkgHDVFAlgorithmClasses + * @brief Build and return a new matrix by copying a `matrix` and removing all chains with indices in the `indices` vector (or at a given `index`). The result is thus a block of the initial matrix. When possible, for efficiency, prefer `\=`. + * + * \note Will return a copy of the matrix if given vector is empty. + * + * \warning The matrix will perform boundary check. + * + * @param matrix The initial matrix. + * @param indices/index The indice(s) of the chain(s) to remove. + * + * @return A new matrix containing the result. + * @{ + */ + + /** \brief Removes a set of chains from a copy of the matrix. */ + Sparse_matrix operator/(const std::vector &_indices) { + Sparse_matrix res(*this); + res /= _indices; + return res; + } + + /** \brief Removes the chain at a given `index` from a copy of the matrix. */ + Sparse_matrix operator/(size_t index) { + Sparse_matrix res(*this); + res /= index; + return res; + } + + /** @} */ + + /** + * \defgroup GetBlockMatrixAssign Assign a sub-matrix by removing chains in a matrix. + * \ingroup PkgHDVFAlgorithmClasses + * @brief Remove all chains with indices in the `indices` vector (or at a given `index`). The result is thus a block of the initial matrix. + * + * \warning The matrix will perform boundary check. + * + * @param indices/index The indice(s) of the chain(s) to remove. + * + * @return A reference on the modified matrix. + * @{ + */ + + /** \brief Removes a set of chains from a matrix. */ + Sparse_matrix& operator/=(const std::vector &indices) { + for (size_t index : indices) { + *this /= index; + } + + return *this; + } + + /** \brief Removes the chain at a given `index` from a matrix. */ + Sparse_matrix& operator/=(const size_t index) { + _chains[index].nullify(); + _chainsStates.set_off(index); + + return *this; + } + + /** @} */ + +protected: + // Protected version of remove_column + Sparse_matrix& remove_column(size_t index) { + std::vector tmp_id{index} ; + if (StorageFormat == OSM::COLUMN) + { + (*this)/=tmp_id ; + } + else // OSM::ROW + { + for (size_t ind : _chainsStates) + { + Matrix_chain &tmp(_chains[ind]) ; + tmp/=tmp_id ; + // If the row index has become empty: update + if (tmp.is_null()) + *this /= std::vector({ind}) ; + } + } + + return *this; + } +public: + /** \relates Sparse_matrix + * + * \brief Removes a column from the matrix. + * + * Removes column of index `index` whatever the `StorageFormat` of the matrix. For column matrices, it just comes to the `\=` operator and for row matrices, it entails a traversal of the matrix. + * + * \param matrix Reference on the matrix to modify. + * \param index The index to remove. + * + * \return The modified matrix representing the result. + */ + template + friend Sparse_matrix<_CT, _CTF>& remove_column(Sparse_matrix<_CT, _CTF>& matrix, size_t index); + +protected: + // Protected version of remove_row + Sparse_matrix& remove_row(size_t index) + { + std::vector tmp_id{index}; + if (StorageFormat == OSM::ROW) { + (*this) /= tmp_id; + } else // OSM::COLUMN + { + for (size_t ind : _chainsStates) { + Matrix_chain &tmp(_chains[ind]); + tmp /= tmp_id; + // If the column index has become empty: update + if (tmp.is_null()) + *this /= std::vector({ind}) ; + } + } + + return *this; + } + +public: + /** \relates Sparse_matrix + * + * \brief Removes a row from the matrix. + * + * Removes row of index `index` whatever the `StorageFormat` of the matrix. For row matrices, it just comes to the `\=` operator and for column matrices, it entails a traversal of the matrix. + * + * \param matrix Reference on the matrix to modify. + * \param index The index to remove. + * + * \return The modified matrix representing the result. + */ + template + friend Sparse_matrix<_CT, _CTF>& remove_row(Sparse_matrix<_CT, _CTF>& matrix, size_t index); + +protected: + // Protected version of remove_coefficient + Sparse_matrix& remove_coefficient(size_t i, size_t j) { + // OSM::COLUMN + if (StorageFormat == OSM::COLUMN) { + std::vector tmp_id({i}) ; + Matrix_chain &tmp(_chains[j]); + tmp /= tmp_id; + if (tmp.is_null()) + _chainsStates.set_off(j) ; + } else // OSM::ROW + { + std::vector tmp_id({j}) ; + Matrix_chain &tmp(_chains[i]); + tmp /= tmp_id; + if (tmp.is_null()) + _chainsStates.set_off(i) ; + } + return *this; + } + +public: + /** \relates Sparse_matrix + * + * \brief Removes a coefficient from the matrix. + * + * Removes coefficient at row `i` and column `j`. + * + * \param matrix Reference on the matrix to modify. + * \param i Index of the row + * \param j Index of the column + * + * \return The modified matrix representing the result. + */ + template + friend Sparse_matrix<_CT, _CTF>& remove_coefficient(Sparse_matrix<_CT, _CTF>& matrix, size_t i, size_t j); + + /** + * \brief Iterator to the index of the first non null chain. + * + * Return an iterator to the index of the first non null chain (the iterator visits indices of non null chains along the major dimension of the matrix). + * + * \return The iterator to the index of the first non null chain. + */ + inline Bitboard::iterator begin() const noexcept { return _chainsStates.begin(); } + + /** + * \brief Iterator past-the-end of chain indices. + * + * \return The iterator past-the-end of chain indices. + */ + inline Bitboard::iterator end() const noexcept { return _chainsStates.end(); } + + /** + * \brief Reverse iterator to the index of the last non null chain. + * + * Return a reverse iiterator to the index of the last non null chain (the iterator visits indices of non null chains, in decreading order, along the major dimension of the matrix). + * + * \return The reverse iterator to the index of the last non null chain. + */ + inline Bitboard::reverse_iterator reverse_begin() noexcept { return _chainsStates.reverse_begin(); } + inline Bitboard::reverse_iterator reverse_begin(size_t index) noexcept { return _chainsStates.reverse_begin(index); } + + /** + * \brief Reverse iterator past-the-end of chain indices. + * + * \return The reverse iterator past-the-end of chain indices. + */ + inline Bitboard::reverse_iterator reverse_end() noexcept { return _chainsStates.reverse_end(); } + + + /** + * \brief Transposes a matrix. + * + * \return A new matrix where the `StorageFormat` has been swapped between COLUMN and ROW and data chains have been transposed. + */ + Sparse_matrix transpose() { + Sparse_matrix transposed(this->_size.second, this->_size.first); + + for (size_t index : this->_chainsStates) { + transposed._chains[index] = this->_chains[index].transpose(); + } + + transposed._chainsStates = this->_chainsStates; + + return transposed; + } + + /** + * \brief Gets the matrix sizes. + * + * \return The matrix size as a row/column pair. + */ + std::pair dimensions() const { + return this->_size; + } +}; + +template +Sparse_matrix<_CT, _CTF> operator*(const Sparse_matrix<_CT, _CTF> &matrix, const _CT& lambda){ + Sparse_matrix<_CT, _CTF> newMatrix = matrix; + newMatrix *= lambda; + + return newMatrix; +} + + +// Matrix-matrix multiplication +// COLUMN x COLUMN -> COLUMN +template +Sparse_matrix<_CT, COLUMN> operator*(const Sparse_matrix<_CT, COLUMN> &first, const Sparse_matrix<_CT, COLUMN> &second) { + Sparse_matrix<_CT, COLUMN> res(first._size.first, second._size.second); + + // Perform col-col matrix multiplication with linear combination of columns. + for (size_t index: second._chainsStates) { + Sparse_chain<_CT, COLUMN> column(first._size.first); + + for (auto colRight: second._chains[index]) { + if (first._chainsStates.is_on(colRight.first)) { + column += colRight.second * first._chains[colRight.first]; + } + } + + res[index] = column; + } + + return res; +} + +// Matrix-matrix multiplication +// ROW x COLUMN -> COLUMN +template +Sparse_matrix<_CT, COLUMN> operator*(const Sparse_matrix<_CT, ROW> &first, const Sparse_matrix<_CT, COLUMN> &second) { + Sparse_matrix<_CT, COLUMN> res(first._size.first, second._size.second); + + // Perform row-col matrix multiplication with dot products. + for (size_t colRight: second._chainsStates) { + Sparse_chain<_CT, COLUMN> column(first._size.first); + + for (size_t rowLeft: first._chainsStates) { + _CT coef = first._chains[rowLeft] * second._chains[colRight]; + if (coef != 0) { + column.set_coefficient(rowLeft, coef); + } + } + + res[colRight] = column; + } + + return res; +} + +// Matrix-matrix multiplication +// COLUMN x ROW -> COLUMN +template +Sparse_matrix<_CT, COLUMN> operator*(const Sparse_matrix<_CT, COLUMN> &first, const Sparse_matrix<_CT, ROW> &second) { + Sparse_matrix<_CT, COLUMN> res(first._size.first, second._size.second); + + // Perform row-col matrix multiplication with dot products. + for (size_t colLeft: first._chainsStates) { + res += first._chains[colLeft] * second._chains[colLeft]; + } + + return res; +} + +// Matrix-matrix multiplication +// ROW x ROW -> COLUMN +template +Sparse_matrix<_CT, COLUMN> operator*(const Sparse_matrix<_CT, ROW> &first, const Sparse_matrix<_CT, ROW> &second) { + Sparse_matrix<_CT, COLUMN> res(first._size.first, second._size.second); + + // Perform row-col matrix multiplication with dot products. + for (size_t i = 0 ; i < second._size.second ; i++) { + Sparse_chain<_CT, COLUMN> column(first._size.first); + + for (size_t rowLeft: first._chainsStates) { + _CT coef = first._chains[rowLeft] * get_column(second, i); + if (coef != 0) { + column.set_coefficient(rowLeft, coef); + } + } + + if (!column.is_null()) { + res[i] = column; + } + } + + return res; +} + +// Matrix - column chain multiplication +// COLUMN matrix +template +Sparse_chain<_CT, COLUMN> operator*(const Sparse_matrix<_CT, COLUMN> &first, const Sparse_chain<_CT, COLUMN> &second) +{ + // Perform col-col matrix multiplication with linear combination of columns. + Sparse_chain<_CT, COLUMN> column(first._size.first); + + for (typename Sparse_chain<_CT, COLUMN>::const_iterator it = second.begin(); it != second.end(); ++it) + { + column += it->second * first._chains[it->first]; + } + + return column; +} + +// Matrix - column chain multiplication +// ROW matrix +template +Sparse_chain<_CT, COLUMN> operator*(const Sparse_matrix<_CT, ROW> &first, const Sparse_chain<_CT, COLUMN> &second) +{ + // Perform row-col matrix multiplication with dots + Sparse_chain<_CT, COLUMN> column(first._size.first); + + for (size_t index : first._chainsStates) + { + _CT tmp(first[index] * second) ; + if (tmp != 0) +// column[index] = tmp ; + column.set_coefficient(index, tmp); + } + return column; +} + +// Row chain - matrix multiplication +// ROW matrix +template +Sparse_chain<_CT, ROW> operator*(const Sparse_chain<_CT, ROW> &first, const Sparse_matrix<_CT, ROW> &second) +{ + // Perform row-row matrix multiplication with linear combination of rows. + Sparse_chain<_CT, ROW> row(second._size.second); + + for (typename Sparse_chain<_CT, ROW>::const_iterator it = first.begin(); it != first.end(); ++it) + { + row += it->second * second._chains[it->first]; + } + + return row; +} + +// Row chain - matrix multiplication +// COLUMN matrix +template +Sparse_chain<_CT, ROW> operator*(const Sparse_chain<_CT, ROW> &first, const Sparse_matrix<_CT, COLUMN> &second) +{ + // Perform row-col matrix multiplication with dots + Sparse_chain<_CT, ROW> row(second._size.second); + + for (size_t index : second._chainsStates) + { + _CT tmp(first * second[index]) ; + if (tmp != 0) +// row[index] = tmp ; + row.set_coefficient(index, tmp); + } + return row; +} + +// Matrix-matrix multiplication +// COLUMN x COLUMN -> ROW +template +Sparse_matrix<_CT, ROW> operator%(const Sparse_matrix<_CT, COLUMN> &first, const Sparse_matrix<_CT, COLUMN> &second) { + Sparse_matrix<_CT, ROW> res(first._size.first, second._size.second); + + for (size_t i = 0 ; i < first._size.first ; i++) { + Sparse_chain<_CT, ROW> row(second._size.second); + + for (size_t colRight: second._chainsStates) { + _CT coef = get_row(first, i) * second._chains[colRight]; + if (coef != 0) { + row.set_coefficient(colRight, coef); + } + } + + if (!row.is_null()) { + res[i] = row; + } + } + + return res; +} + +// Matrix-matrix multiplication +// ROW x COLUMN -> ROW +template +Sparse_matrix<_CT, ROW> operator%(const Sparse_matrix<_CT, ROW> &first, const Sparse_matrix<_CT, COLUMN> &second) { + Sparse_matrix<_CT, ROW> res(first._size.first, second._size.second); + + // Perform row-col matrix multiplication with dot products. + for (size_t rowLeft: first._chainsStates) { + Sparse_chain<_CT, ROW> row(second._size.second); + + for (size_t colRight: second._chainsStates) { + _CT coef = first._chains[rowLeft] * second._chains[colRight]; + if (coef != 0) { + row.set_coefficient(colRight,coef); + } + } + + res[rowLeft] = row; + } + + return res; +} + +// Matrix-matrix multiplication +// COLUMN x ROW -> ROW +template +Sparse_matrix<_CT, ROW> operator%(const Sparse_matrix<_CT, COLUMN> &first, const Sparse_matrix<_CT, ROW> &second) { + Sparse_matrix<_CT, ROW> res(first._size.first, second._size.second); + + // Perform row-col matrix multiplication with dot products. + for (size_t colLeft: first._chainsStates) { + res += first._chains[colLeft] % second._chains[colLeft]; + } + + return res; +} + +// Matrix-matrix multiplication +// ROW x ROW -> ROW +template +Sparse_matrix<_CT, ROW> operator%(const Sparse_matrix<_CT, ROW> &first, const Sparse_matrix<_CT, ROW> &second) { + Sparse_matrix<_CT, ROW> res(first._size.first, second._size.second); + + // Perform row-row matrix multiplication with linear combination of rows. + for (size_t index: first._chainsStates) { + Sparse_chain<_CT, ROW> row(second._size.second); + + for (auto colRight: first._chains[index]) { + if (first._chainsStates.is_on(colRight.first)) { + row += colRight.second * second._chains[colRight.first]; + } + } + + res[index] = row; + } + + return res; +} + +// Matrices sum and assign +// COLUMN += ROW +template +Sparse_matrix<_CT, COLUMN>& operator+=(Sparse_matrix<_CT, COLUMN> &matrix, const Sparse_matrix<_CT, ROW> &other) { + if (matrix._size != other._size) { + throw std::runtime_error("Matrices must be the same size."); + } + + for (size_t index = 0 ; index < other._size.second ; index++) { + Sparse_chain<_CT, COLUMN> column = get_column(other, index); + if (!column.is_null()) { + matrix._chainsStates |= index; + matrix._chains[index] += get_column(other, index); + + if (matrix._chains[index].is_null()) { + matrix._chainsStates.set_off(index); + } + } + } + + return matrix; +} + +// Matrices sum and assign +// ROW += COLUMN +template +Sparse_matrix<_CT, ROW>& operator+=(Sparse_matrix<_CT, ROW> &matrix, const Sparse_matrix<_CT, COLUMN> &other) { + if (matrix._size != other._size) { + throw std::runtime_error("Matrices must be the same size."); + } + + for (size_t index = 0 ; index < other._size.first ; index++) { + Sparse_chain<_CT, ROW> row = get_row(other, index); + if (!row.is_null()) { + matrix._chainsStates |= index; + matrix._chains[index] += get_row(other, index); + + if (matrix._chains[index].is_null()) { + matrix._chainsStates.set_off(index); + } + } + } + + return matrix; +} + +// Matrices subtraction and assign +// COLUMN -= ROW +template +Sparse_matrix<_CT, COLUMN>& operator-=(Sparse_matrix<_CT, COLUMN> &matrix, const Sparse_matrix<_CT, ROW> &other) { + if (matrix._size != other._size) { + throw std::runtime_error("Matrices must be the same size."); + } + + for (size_t index = 0 ; index < other._size.second ; index++) { + Sparse_chain<_CT, COLUMN> column = get_column(other, index); + if (!column.is_null()) { + matrix._chainsStates |= index; + matrix._chains[index] -= get_column(other, index); + + if (matrix._chains[index].is_null()) { + matrix._chainsStates.set_off(index); + } + } + } + + return matrix; +} + +// Matrices subtraction and assign +// ROW -= COLUMN +template +Sparse_matrix<_CT, ROW>& operator-=(Sparse_matrix<_CT, ROW> &matrix, const Sparse_matrix<_CT, COLUMN> &other) { + if (matrix._size != other._size) { + throw std::runtime_error("Matrices must be the same size."); + } + + for (size_t index = 0 ; index < other._size.second ; index++) { + Sparse_chain<_CT, ROW> row = get_row(other, index); + if (!row.is_null()) { + matrix._chainsStates |= index; + matrix._chains[index] -= get_row(other, index); + + if (matrix._chains[index].is_null()) { + matrix._chainsStates.set_off(index); + } + } + } + + return matrix; +} + +// matrix multiplication and assign +// COLUMN += COLUMN +template +Sparse_matrix<_CT, COLUMN>& operator*=(Sparse_matrix<_CT, COLUMN> &matrix, const Sparse_matrix<_CT, COLUMN> &other) { + matrix = matrix * other; + return matrix; +} + +// matrix multiplication and assign +// ROW *= ROW +template +Sparse_matrix<_CT, ROW>& operator*=(Sparse_matrix<_CT, ROW> &matrix, const Sparse_matrix<_CT, ROW> &other) { + matrix = matrix % other; + return matrix; +} + +// matrix multiplication and assign +// COLUMN *= ROW +template +Sparse_matrix<_CT, COLUMN>& operator*=(Sparse_matrix<_CT, COLUMN> &matrix, const Sparse_matrix<_CT, ROW> &other) { + matrix = matrix * other; + return matrix; +} + +// matrix multiplication and assign +// ROW *= COLUMN +template +Sparse_matrix<_CT, ROW>& operator*=(Sparse_matrix<_CT, ROW> &matrix, const Sparse_matrix<_CT, COLUMN> &other) { + matrix = matrix % other; + return matrix; +} + +// Get column (in COLUMN matrix) +template +Sparse_chain<_CT, COLUMN> get_column(const Sparse_matrix<_CT, COLUMN> &matrix, size_t index) { + return matrix._chains[index]; +} + +// Get column (in ROW matrix) +template +Sparse_chain<_CT, COLUMN> get_column(const Sparse_matrix<_CT, ROW> &matrix, size_t index) { + Sparse_chain<_CT, COLUMN> column(matrix._size.first); + if (matrix._size.first > 0) + { + for (size_t i : matrix._chainsStates) { + if (!matrix._chains[i].is_null(index)) { + column.set_coefficient(i, matrix._chains[i][index]); + } + } + } + + return column; +} + +// Get row (in COLUMN matrix) +template +Sparse_chain<_CT, ROW> get_row(const Sparse_matrix<_CT, COLUMN> &matrix, size_t index) { + Sparse_chain<_CT, ROW> row(matrix._size.second); + if (matrix._size.second > 0) + { + for (size_t i : matrix._chainsStates) { + if (!matrix._chains[i].is_null(index)) { + row.set_coefficient(i, matrix._chains[i][index]); + } + } + } + + return row; +} + +// Get row (in COLUMN matrix) +template +Sparse_chain<_CT, ROW> get_row(const Sparse_matrix<_CT, ROW> &matrix, size_t index) { + return matrix._chains[index]; +} + +// Get constant reference over a column in a column-matrix +template +const Sparse_chain<_CT, COLUMN> & cget_column(const Sparse_matrix<_CT, COLUMN> &matrix, size_t index) +{ + if (index >= matrix._size.second) { + throw std::runtime_error("Provided index should be less than " + std::to_string(matrix._size.second) + "."); + } + return matrix._chains[index]; +} + +// Get constant reference over a row in a row-matrix +template +const Sparse_chain<_CT, ROW> & cget_row(const Sparse_matrix<_CT, ROW> &matrix, const size_t index) +{ + if (index >= matrix._size.first) { + throw std::runtime_error("Provided index should be less than " + std::to_string(matrix._size.first) + "."); + } + return matrix._chains[index]; +} + +// Set column in a COLUMN matrix +template +void set_column(Sparse_matrix<_CT, COLUMN> &matrix, size_t index, const Sparse_chain<_CT, COLUMN> &chain) { + if(matrix.dimensions().first != chain.dimension()) + throw std::runtime_error("set_column dimension error") ; + matrix[index] = chain; + if (chain.is_null()) + matrix._chainsStates.set_off(index) ; +} + +// Set column in a ROW matrix +template +void set_column(Sparse_matrix<_CT, ROW> &matrix, size_t index, const Sparse_chain<_CT, COLUMN> &chain) { + if(matrix.dimensions().first != chain.dimension()) + throw std::runtime_error("set_column dimension error") ; + for (size_t i = 0 ; i < matrix._size.first ; i++) { + if (!matrix._chains[i].is_null(index) && chain.is_null(i)) { + matrix._chains[i] /= index; + + if (matrix._chains[i].is_null()) { + matrix._chainsStates.set_off(i); + } + } + + if (!chain.is_null(i)) { + matrix._chainsStates |= i; + matrix._chains[i].set_coefficient(index, chain[i]); + } + } +} + +// Set row in a COLUMN matrix +template +void set_row(Sparse_matrix<_CT, COLUMN> &matrix, size_t index, const Sparse_chain<_CT, ROW> &chain) { + if(matrix.dimensions().second != chain.dimension()) + throw("set_column dimension error") ; + for (size_t i = 0 ; i < matrix._size.second ; i++) { + if (!matrix._chains[i].is_null(index) && chain.is_null(i)) { + matrix._chains[i] /= index; + + if (matrix._chains[i].is_null()) { + matrix._chainsStates.set_off(i); + } + } + + if (!chain.is_null(i)) { + matrix._chainsStates |= i; + matrix._chains[i].set_coefficient(index, chain[i]); + } + } +} + +// Set row in a ROW matrix +template +void set_row(Sparse_matrix<_CT, ROW> &matrix, size_t index, const Sparse_chain<_CT, ROW> &chain) { + if(matrix.dimensions().second != chain.dimension()) + throw("set_column dimension error") ; + matrix[index] = chain; + if (chain.is_null()) + matrix._chainsStates.set_off(index) ; +} + +template +inline void set_coefficient(Sparse_matrix<_CT, _CTF>& matrix, size_t i, size_t j, const _CT d) +{ + matrix.set_coefficient(i, j, d); +} + +template +inline _CT get_coefficient(const Sparse_matrix<_CT, _CTF>& matrix, size_t i, size_t j) +{ + matrix.get_coefficient(i, j); +} + +template +inline Sparse_matrix<_CT, _CTF>& remove_column(Sparse_matrix<_CT, _CTF>& matrix, size_t index) +{ + return matrix.remove_column(index); +} + +template +inline Sparse_matrix<_CT, _CTF>& remove_row(Sparse_matrix<_CT, _CTF>& matrix, size_t index) +{ + return matrix.remove_row(index); +} + +template +inline Sparse_matrix<_CT, _CTF>& remove_coefficient(Sparse_matrix<_CT, _CTF>& matrix, size_t i, size_t j) +{ + return matrix.remove_coefficient(i, j); +} + +template +std::ostream& write_matrix (const Sparse_matrix<_CT, OSM::COLUMN>& M, std::ostream& out) +{ + typedef Sparse_chain<_CT, OSM::COLUMN> Column_chain; + std::vector vec_i, vec_j; + std::vector<_CT> vec_val; + // Matrix type : 0 for (COLUMN), 1 for (ROW) + out << "0" << std::endl ; + // Size : nb rows / nb cols + out << M._size.first << " " << M._size.second << std::endl; + // Get all coefficients + for(OSM::Bitboard::iterator it = M.begin(); it != M.end(); ++it) + { + const Column_chain& col(OSM::cget_column(M, *it)); + // Iterate over the column + for (typename Column_chain::const_iterator it_col = col.begin(); it_col != col.end(); ++it_col) + { + vec_j.push_back(*it) ; + vec_i.push_back(it_col->first) ; + vec_val.push_back(it_col->second) ; + } + } + // Output the number of coefficients + out << vec_i.size() << std::endl ; + // Output all coefficients : i j val + for (int n=0; n +std::ostream& write_matrix (const Sparse_matrix<_CT, OSM::ROW>& M, std::ostream& out) +{ + typedef Sparse_chain<_CT, OSM::ROW> Row_chain; + std::vector vec_i, vec_j; + std::vector<_CT> vec_val; + // Matrix type : 0 for (COLUMN), 1 for (ROW) + out << "1" << std::endl ; + // Size : nb rows / nb cols + out << M._size.first << " " << M._size.second << std::endl; + // Get all coefficients + for(OSM::Bitboard::iterator it = M.begin(); it != M.end(); ++it) + { + const Row_chain& row(OSM::cget_row(M, *it)); + // Iterate over the column + for (typename Row_chain::const_iterator it_row = row.begin(); it_row != row.end(); ++it_row) + { + vec_i.push_back(*it) ; + vec_j.push_back(it_row->first) ; + vec_val.push_back(it_row->second) ; + } + } + // Output the number of coefficients + out << vec_i.size() << std::endl ; + // Output all coefficients : i j val + for (int n=0; n +void write_matrix (const Sparse_matrix<_CT, OSM::COLUMN>& M, std::string filename) +{ + std::ofstream out ( filename, std::ios::out | std::ios::trunc); + if ( not out . good () ) { + std::cerr << "Out fatal Error:\n " << filename << " not found.\n"; + throw std::runtime_error("File Parsing Error: File not found"); + } + + CGAL::OSM::write_matrix(M, out) ; + + out.close(); +} + +template +void write_matrix (const Sparse_matrix<_CT, OSM::ROW>& M, std::string filename) +{ + std::ofstream out ( filename, std::ios::out | std::ios::trunc); + if ( not out . good () ) { + std::cerr << "Out fatal Error:\n " << filename << " not found.\n"; + throw std::runtime_error("File Parsing Error: File not found"); + } + + CGAL::OSM::write_matrix(M, out) ; + + out.close(); +} + +template +std::istream& read_matrix (Sparse_matrix<_CT, OSM::COLUMN>& M, std::istream& in) +{ + // Read and check type + // Matrix type : 0 for (COLUMN), 1 for (ROW) + int type ; + in >> type ; + if (type != 0) + throw("read_matrix error: trying to load a ROW matrix representation into a COLUMN matrix"); + // Read and adjust size + // Size : nb rows / nb cols + size_t nrows, ncols ; + in >> nrows >> ncols ; + M = Sparse_matrix<_CT, OSM::COLUMN>(nrows, ncols) ; + + // Read number of coefficients + size_t n ; + in >> n ; + // Read all coefficients and load them into the matrix + size_t i, j ; + _CT val ; + for (size_t k=0; k> i >> j ; + in >> val ; + OSM::set_coefficient(M, i, j, val) ; + } + return in ; +} + +template +std::istream& read_matrix (Sparse_matrix<_CT, OSM::ROW>& M, std::istream& in) +{ + // Read and check type + // Matrix type : 0 for (COLUMN), 1 for (ROW) + int type ; + in >> type ; + if (type != 1) + throw("read_matrix error: trying to load a COLUMN matrix representation into a ROW matrix"); + // Read and adjust size + // Size : nb rows / nb cols + size_t nrows, ncols ; + in >> nrows >> ncols ; + M = Sparse_matrix<_CT, OSM::ROW>(nrows, ncols) ; + + // Read number of coefficients + size_t n ; + in >> n ; + // Read all coefficients and load them into the matrix + size_t i, j ; + _CT val ; + for (size_t k=0; k> i >> j ; + in >> val ; + OSM::set_coefficient(M, i, j, val) ; + } + return in ; +} + +template +void read_matrix (Sparse_matrix<_CT, OSM::COLUMN>& M, std::string filename) +{ + std::ifstream in_file (filename); + if ( not in_file . good () ) { + std::cerr << "Out fatal Error:\n " << filename << " not found.\n"; + throw std::runtime_error("File Parsing Error: File not found"); + } + + CGAL::OSM::read_matrix(M, in_file) ; + + in_file.close(); +} + +template +void read_matrix (Sparse_matrix<_CT, OSM::ROW>& M, std::string filename) +{ + std::ifstream in_file (filename); + if ( not in_file . good () ) { + std::cerr << "Out fatal Error:\n " << filename << " not found.\n"; + throw std::runtime_error("File Parsing Error: File not found"); + } + + CGAL::OSM::read_matrix(M, in_file) ; + + in_file.close(); +} + +template +bool operator==(const Sparse_matrix<_CT, OSM::COLUMN>& matrix, const Sparse_matrix<_CT, OSM::COLUMN> &other) +{ + typedef Sparse_chain<_CT, OSM::COLUMN> SparseChainType; + bool res = true ; + // Checks that sizes are similar + res = res && (matrix._size == other._size) ; + // Checks that all the chains of matrix belong to other + for (OSM::Bitboard::iterator it = matrix.begin() ; res && (it != matrix.end()); ++it) + { + const SparseChainType& chain1(OSM::cget_column(matrix, *it)) ; + const SparseChainType& chain2(OSM::cget_column(other, *it)) ; + res = res && (chain1 == chain2) ; + } + // Checks that all the chains of other belong to matrix + for (OSM::Bitboard::iterator it = other.begin() ; res && (it != other.end()); ++it) + { + const SparseChainType& chain1(OSM::cget_column(matrix, *it)) ; + const SparseChainType& chain2(OSM::cget_column(other, *it)) ; + res = res && (chain1 == chain2) ; + } + return res ; +} + +template +bool operator==(const Sparse_matrix<_CT, OSM::ROW>& matrix, const Sparse_matrix<_CT, OSM::ROW> &other) +{ + typedef Sparse_chain<_CT, OSM::ROW> SparseChainType; + bool res = true ; + // Checks that sizes are similar + res = res && (matrix._size == other._size) ; + // Checks that all the chains of matrix belong to other + for (OSM::Bitboard::iterator it = matrix.begin() ; it != matrix.end(); ++it) + { + const SparseChainType& chain1(OSM::cget_row(matrix, *it)) ; + const SparseChainType& chain2(OSM::cget_row(other, *it)) ; + res = res && (chain1 == chain2) ; + } + // Checks that all the chains of other belong to matrix + for (OSM::Bitboard::iterator it = other.begin() ; it != other.end(); ++it) + { + const SparseChainType& chain1(OSM::cget_row(matrix, *it)) ; + const SparseChainType& chain2(OSM::cget_row(other, *it)) ; + res = res && (chain1 == chain2) ; + } + return res ; +} + +template +bool operator==(const Sparse_matrix<_CT, OSM::ROW>& matrix, const Sparse_matrix<_CT, OSM::COLUMN> &other) +{ + return false; +} + +template +bool operator==(const Sparse_matrix<_CT, OSM::COLUMN>& matrix, const Sparse_matrix<_CT, OSM::ROW> &other) +{ + return false; +} + +} /* end namespace OSM */ +} /* end namespace CGAL */ + +#endif // CGAL_OSM_SPARSE_MATRIX_H diff --git a/HDVF/include/CGAL/OSM/__base.h b/HDVF/include/CGAL/OSM/__base.h new file mode 100644 index 00000000000..b8c6bcd1056 --- /dev/null +++ b/HDVF/include/CGAL/OSM/__base.h @@ -0,0 +1,48 @@ +// Copyright (c) 2025 LIS Marseille (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Alexandra Bac +// Kevin Fedyna + + +#ifndef CGAL_OSM__BASE_H +#define CGAL_OSM__BASE_H + +#include + +#include + +namespace CGAL { +namespace OSM { + +/** \brief StorageFormat for column chain. */ +const int COLUMN = 0b01; +/** \brief StorageFormat flag for row chain. */ +const int ROW = 0b10; + +/** \brief The default type for signed integers. */ +typedef int ZCoefficient; + +// Class Sparse_matrix +template +class Sparse_matrix; + +// Class Sparse_chain + +template +class Sparse_chain; + +// Class Bitboard + +class Bitboard; + +} /* end namespace OSM */ +} /* end namespace CGAL */ + +#endif // CGAL_OSM__BASE_H diff --git a/HDVF/package_info/HDVF/copyright b/HDVF/package_info/HDVF/copyright new file mode 100644 index 00000000000..22062b19aa9 --- /dev/null +++ b/HDVF/package_info/HDVF/copyright @@ -0,0 +1 @@ +University of Marseille diff --git a/HDVF/package_info/HDVF/description.txt b/HDVF/package_info/HDVF/description.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/HDVF/package_info/HDVF/license.txt b/HDVF/package_info/HDVF/license.txt new file mode 100644 index 00000000000..8bb8efcb72b --- /dev/null +++ b/HDVF/package_info/HDVF/license.txt @@ -0,0 +1 @@ +GPL (v3 or later) diff --git a/HDVF/package_info/HDVF/long_description.txt b/HDVF/package_info/HDVF/long_description.txt new file mode 100644 index 00000000000..6527bfe696b --- /dev/null +++ b/HDVF/package_info/HDVF/long_description.txt @@ -0,0 +1 @@ +Homological Discrete Vector Fields diff --git a/HDVF/package_info/HDVF/maintainer b/HDVF/package_info/HDVF/maintainer new file mode 100644 index 00000000000..5cc6bd025a7 --- /dev/null +++ b/HDVF/package_info/HDVF/maintainer @@ -0,0 +1 @@ +Alexandra Bac diff --git a/HDVF/test/HDVF/CMakeLists.txt b/HDVF/test/HDVF/CMakeLists.txt new file mode 100644 index 00000000000..65a852242ec --- /dev/null +++ b/HDVF/test/HDVF/CMakeLists.txt @@ -0,0 +1,16 @@ +# Created by the script cgal_create_cmake_script +# This is the CMake script for compiling a CGAL application. + +cmake_minimum_required(VERSION 3.12...3.31) +project(HDVF_Tests) + +find_package(CGAL REQUIRED) + +# create a target per cppfile +file( + GLOB cppfiles + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) +foreach(cppfile ${cppfiles}) + create_single_source_cgal_program("${cppfile}") +endforeach() diff --git a/HDVF/test/HDVF/data/cub_data/Eight_3D.pgm b/HDVF/test/HDVF/data/cub_data/Eight_3D.pgm new file mode 100644 index 00000000000..f3f7381c5b9 --- /dev/null +++ b/HDVF/test/HDVF/data/cub_data/Eight_3D.pgm @@ -0,0 +1,87 @@ +P2 +4 12 7 +255 +0 0 0 0 +0 0 0 0 +0 0 0 0 +0 0 0 0 +0 0 0 0 +0 0 0 0 +0 0 0 0 +0 0 0 0 +0 0 0 0 +0 0 0 0 +0 0 0 0 +0 0 0 0 +0 0 0 0 +0 0 0 0 +0 1 1 0 +0 1 1 0 +0 0 1 0 +0 0 0 0 +0 0 0 0 +0 1 1 0 +0 1 1 0 +0 1 1 0 +0 0 0 0 +0 0 0 0 +0 0 0 0 +0 1 1 0 +0 1 1 0 +0 0 0 0 +0 1 1 0 +0 1 1 0 +0 1 1 0 +0 1 1 0 +0 0 0 0 +0 1 1 0 +0 1 1 0 +0 0 0 0 +0 0 0 0 +0 1 1 0 +0 0 0 0 +0 0 0 0 +0 0 0 0 +0 1 1 0 +0 1 1 0 +0 0 0 0 +0 0 0 0 +0 0 0 0 +0 1 1 0 +0 0 0 0 +0 0 0 0 +0 1 1 0 +0 1 1 0 +0 0 0 0 +0 1 1 0 +0 1 1 0 +0 1 1 0 +0 1 1 0 +0 0 0 0 +0 1 1 0 +0 1 1 0 +0 0 0 0 +0 0 0 0 +0 0 0 0 +0 0 1 0 +0 1 1 0 +0 0 0 0 +0 0 0 0 +0 0 0 0 +0 0 0 0 +0 1 1 0 +0 0 1 0 +0 0 0 0 +0 0 0 0 +0 0 0 0 +0 0 0 0 +0 0 0 0 +0 0 0 0 +0 0 0 0 +0 0 0 0 +0 0 0 0 +0 0 0 0 +0 0 0 0 +0 0 0 0 +0 0 0 0 +0 0 0 0 diff --git a/HDVF/test/HDVF/data/dim_4/klein4.nodes b/HDVF/test/HDVF/data/dim_4/klein4.nodes new file mode 100644 index 00000000000..49809f440d1 --- /dev/null +++ b/HDVF/test/HDVF/data/dim_4/klein4.nodes @@ -0,0 +1,401 @@ +400 4 0 0 +0 -4 -4.898587e-16 -7.498799e-33 1.224647e-16 +1 -4.048943e+00 -4.958526e-16 -1.892183e-17 3.090170e-01 +2 -4.190983e+00 -5.132474e-16 -3.599147e-17 5.877853e-01 +3 -4.412215e+00 -5.403405e-16 -4.953800e-17 8.090170e-01 +4 -4.690983e+00 -5.744797e-16 -5.823542e-17 9.510565e-01 +5 -5 -6.123234e-16 -6.123234e-17 1 +6 -5.309017e+00 -6.501671e-16 -5.823542e-17 9.510565e-01 +7 -5.587785e+00 -6.843063e-16 -4.953800e-17 8.090170e-01 +8 -5.809017e+00 -7.113994e-16 -3.599147e-17 5.877853e-01 +9 -5.951057e+00 -7.287942e-16 -1.892183e-17 3.090170e-01 +10 -6 -7.347881e-16 0 0 +11 -5.951057e+00 -7.287942e-16 1.892183e-17 -3.090170e-01 +12 -5.809017e+00 -7.113994e-16 3.599147e-17 -5.877853e-01 +13 -5.587785e+00 -6.843063e-16 4.953800e-17 -8.090170e-01 +14 -5.309017e+00 -6.501671e-16 5.823542e-17 -9.510565e-01 +15 -5 -6.123234e-16 6.123234e-17 -1 +16 -4.690983e+00 -5.744797e-16 5.823542e-17 -9.510565e-01 +17 -4.412215e+00 -5.403405e-16 4.953800e-17 -8.090170e-01 +18 -4.190983e+00 -5.132474e-16 3.599147e-17 -5.877853e-01 +19 -4.048943e+00 -4.958526e-16 1.892183e-17 -3.090170e-01 +20 -3.804226e+00 -1.236068e+00 -1.915770e-17 1.209569e-16 +21 -3.850774e+00 -1.251192e+00 -4.834091e-02 3.052125e-01 +22 -3.985862e+00 -1.295085e+00 -9.194987e-02 5.805486e-01 +23 -4.196266e+00 -1.363449e+00 -1.265581e-01 7.990567e-01 +24 -4.461390e+00 -1.449593e+00 -1.487780e-01 9.393474e-01 +25 -4.755283e+00 -1.545085e+00 -1.564345e-01 9.876883e-01 +26 -5.049175e+00 -1.640576e+00 -1.487780e-01 9.393474e-01 +27 -5.314300e+00 -1.726721e+00 -1.265581e-01 7.990567e-01 +28 -5.524703e+00 -1.795085e+00 -9.194987e-02 5.805486e-01 +29 -5.659791e+00 -1.838978e+00 -4.834091e-02 3.052125e-01 +30 -5.706339e+00 -1.854102e+00 0 0 +31 -5.659791e+00 -1.838978e+00 4.834091e-02 -3.052125e-01 +32 -5.524703e+00 -1.795085e+00 9.194987e-02 -5.805486e-01 +33 -5.314300e+00 -1.726721e+00 1.265581e-01 -7.990567e-01 +34 -5.049175e+00 -1.640576e+00 1.487780e-01 -9.393474e-01 +35 -4.755283e+00 -1.545085e+00 1.564345e-01 -9.876883e-01 +36 -4.461390e+00 -1.449593e+00 1.487780e-01 -9.393474e-01 +37 -4.196266e+00 -1.363449e+00 1.265581e-01 -7.990567e-01 +38 -3.985862e+00 -1.295085e+00 9.194987e-02 -5.805486e-01 +39 -3.850774e+00 -1.251192e+00 4.834091e-02 -3.052125e-01 +40 -3.236068e+00 -2.351141e+00 -3.784367e-17 1.164708e-16 +41 -3.275664e+00 -2.379909e+00 -9.549150e-02 2.938926e-01 +42 -3.390576e+00 -2.463398e+00 -1.816356e-01 5.590170e-01 +43 -3.569557e+00 -2.593435e+00 -2.500000e-01 7.694209e-01 +44 -3.795085e+00 -2.757291e+00 -2.938926e-01 9.045085e-01 +45 -4.045085e+00 -2.938926e+00 -3.090170e-01 9.510565e-01 +46 -4.295085e+00 -3.120562e+00 -2.938926e-01 9.045085e-01 +47 -4.520613e+00 -3.284418e+00 -2.500000e-01 7.694209e-01 +48 -4.699593e+00 -3.414455e+00 -1.816356e-01 5.590170e-01 +49 -4.814506e+00 -3.497943e+00 -9.549150e-02 2.938926e-01 +50 -4.854102e+00 -3.526712e+00 0 0 +51 -4.814506e+00 -3.497943e+00 9.549150e-02 -2.938926e-01 +52 -4.699593e+00 -3.414455e+00 1.816356e-01 -5.590170e-01 +53 -4.520613e+00 -3.284418e+00 2.500000e-01 -7.694209e-01 +54 -4.295085e+00 -3.120562e+00 2.938926e-01 -9.045085e-01 +55 -4.045085e+00 -2.938926e+00 3.090170e-01 -9.510565e-01 +56 -3.795085e+00 -2.757291e+00 2.938926e-01 -9.045085e-01 +57 -3.569557e+00 -2.593435e+00 2.500000e-01 -7.694209e-01 +58 -3.390576e+00 -2.463398e+00 1.816356e-01 -5.590170e-01 +59 -3.275664e+00 -2.379909e+00 9.549150e-02 -2.938926e-01 +60 -2.351141e+00 -3.236068e+00 -5.559780e-17 1.091168e-16 +61 -2.379909e+00 -3.275664e+00 -1.402908e-01 2.753362e-01 +62 -2.463398e+00 -3.390576e+00 -2.668489e-01 5.237205e-01 +63 -2.593435e+00 -3.569557e+00 -3.672860e-01 7.208394e-01 +64 -2.757291e+00 -3.795085e+00 -4.317706e-01 8.473976e-01 +65 -2.938926e+00 -4.045085e+00 -4.539905e-01 8.910065e-01 +66 -3.120562e+00 -4.295085e+00 -4.317706e-01 8.473976e-01 +67 -3.284418e+00 -4.520613e+00 -3.672860e-01 7.208394e-01 +68 -3.414455e+00 -4.699593e+00 -2.668489e-01 5.237205e-01 +69 -3.497943e+00 -4.814506e+00 -1.402908e-01 2.753362e-01 +70 -3.526712e+00 -4.854102e+00 0 0 +71 -3.497943e+00 -4.814506e+00 1.402908e-01 -2.753362e-01 +72 -3.414455e+00 -4.699593e+00 2.668489e-01 -5.237205e-01 +73 -3.284418e+00 -4.520613e+00 3.672860e-01 -7.208394e-01 +74 -3.120562e+00 -4.295085e+00 4.317706e-01 -8.473976e-01 +75 -2.938926e+00 -4.045085e+00 4.539905e-01 -8.910065e-01 +76 -2.757291e+00 -3.795085e+00 4.317706e-01 -8.473976e-01 +77 -2.593435e+00 -3.569557e+00 3.672860e-01 -7.208394e-01 +78 -2.463398e+00 -3.390576e+00 2.668489e-01 -5.237205e-01 +79 -2.379909e+00 -3.275664e+00 1.402908e-01 -2.753362e-01 +80 -1.236068e+00 -3.804226e+00 -7.198293e-17 9.907601e-17 +81 -1.251192e+00 -3.850774e+00 -1.816356e-01 2.500000e-01 +82 -1.295085e+00 -3.985862e+00 -3.454915e-01 4.755283e-01 +83 -1.363449e+00 -4.196266e+00 -4.755283e-01 6.545085e-01 +84 -1.449593e+00 -4.461390e+00 -5.590170e-01 7.694209e-01 +85 -1.545085e+00 -4.755283e+00 -5.877853e-01 8.090170e-01 +86 -1.640576e+00 -5.049175e+00 -5.590170e-01 7.694209e-01 +87 -1.726721e+00 -5.314300e+00 -4.755283e-01 6.545085e-01 +88 -1.795085e+00 -5.524703e+00 -3.454915e-01 4.755283e-01 +89 -1.838978e+00 -5.659791e+00 -1.816356e-01 2.500000e-01 +90 -1.854102e+00 -5.706339e+00 0 0 +91 -1.838978e+00 -5.659791e+00 1.816356e-01 -2.500000e-01 +92 -1.795085e+00 -5.524703e+00 3.454915e-01 -4.755283e-01 +93 -1.726721e+00 -5.314300e+00 4.755283e-01 -6.545085e-01 +94 -1.640576e+00 -5.049175e+00 5.590170e-01 -7.694209e-01 +95 -1.545085e+00 -4.755283e+00 5.877853e-01 -8.090170e-01 +96 -1.449593e+00 -4.461390e+00 5.590170e-01 -7.694209e-01 +97 -1.363449e+00 -4.196266e+00 4.755283e-01 -6.545085e-01 +98 -1.295085e+00 -3.985862e+00 3.454915e-01 -4.755283e-01 +99 -1.251192e+00 -3.850774e+00 1.816356e-01 -2.500000e-01 +100 2.449294e-16 -4 -8.659561e-17 8.659561e-17 +101 2.479263e-16 -4.048943e+00 -2.185080e-01 2.185080e-01 +102 2.566237e-16 -4.190983e+00 -4.156269e-01 4.156269e-01 +103 2.701702e-16 -4.412215e+00 -5.720614e-01 5.720614e-01 +104 2.872399e-16 -4.690983e+00 -6.724985e-01 6.724985e-01 +105 3.061617e-16 -5 -7.071068e-01 7.071068e-01 +106 3.250835e-16 -5.309017e+00 -6.724985e-01 6.724985e-01 +107 3.421532e-16 -5.587785e+00 -5.720614e-01 5.720614e-01 +108 3.556997e-16 -5.809017e+00 -4.156269e-01 4.156269e-01 +109 3.643971e-16 -5.951057e+00 -2.185080e-01 2.185080e-01 +110 3.673940e-16 -6 0 0 +111 3.643971e-16 -5.951057e+00 2.185080e-01 -2.185080e-01 +112 3.556997e-16 -5.809017e+00 4.156269e-01 -4.156269e-01 +113 3.421532e-16 -5.587785e+00 5.720614e-01 -5.720614e-01 +114 3.250835e-16 -5.309017e+00 6.724985e-01 -6.724985e-01 +115 3.061617e-16 -5 7.071068e-01 -7.071068e-01 +116 2.872399e-16 -4.690983e+00 6.724985e-01 -6.724985e-01 +117 2.701702e-16 -4.412215e+00 5.720614e-01 -5.720614e-01 +118 2.566237e-16 -4.190983e+00 4.156269e-01 -4.156269e-01 +119 2.479263e-16 -4.048943e+00 2.185080e-01 -2.185080e-01 +120 1.236068e+00 -3.804226e+00 -9.907601e-17 7.198293e-17 +121 1.251192e+00 -3.850774e+00 -2.500000e-01 1.816356e-01 +122 1.295085e+00 -3.985862e+00 -4.755283e-01 3.454915e-01 +123 1.363449e+00 -4.196266e+00 -6.545085e-01 4.755283e-01 +124 1.449593e+00 -4.461390e+00 -7.694209e-01 5.590170e-01 +125 1.545085e+00 -4.755283e+00 -8.090170e-01 5.877853e-01 +126 1.640576e+00 -5.049175e+00 -7.694209e-01 5.590170e-01 +127 1.726721e+00 -5.314300e+00 -6.545085e-01 4.755283e-01 +128 1.795085e+00 -5.524703e+00 -4.755283e-01 3.454915e-01 +129 1.838978e+00 -5.659791e+00 -2.500000e-01 1.816356e-01 +130 1.854102e+00 -5.706339e+00 0 0 +131 1.838978e+00 -5.659791e+00 2.500000e-01 -1.816356e-01 +132 1.795085e+00 -5.524703e+00 4.755283e-01 -3.454915e-01 +133 1.726721e+00 -5.314300e+00 6.545085e-01 -4.755283e-01 +134 1.640576e+00 -5.049175e+00 7.694209e-01 -5.590170e-01 +135 1.545085e+00 -4.755283e+00 8.090170e-01 -5.877853e-01 +136 1.449593e+00 -4.461390e+00 7.694209e-01 -5.590170e-01 +137 1.363449e+00 -4.196266e+00 6.545085e-01 -4.755283e-01 +138 1.295085e+00 -3.985862e+00 4.755283e-01 -3.454915e-01 +139 1.251192e+00 -3.850774e+00 2.500000e-01 -1.816356e-01 +140 2.351141e+00 -3.236068e+00 -1.091168e-16 5.559780e-17 +141 2.379909e+00 -3.275664e+00 -2.753362e-01 1.402908e-01 +142 2.463398e+00 -3.390576e+00 -5.237205e-01 2.668489e-01 +143 2.593435e+00 -3.569557e+00 -7.208394e-01 3.672860e-01 +144 2.757291e+00 -3.795085e+00 -8.473976e-01 4.317706e-01 +145 2.938926e+00 -4.045085e+00 -8.910065e-01 4.539905e-01 +146 3.120562e+00 -4.295085e+00 -8.473976e-01 4.317706e-01 +147 3.284418e+00 -4.520613e+00 -7.208394e-01 3.672860e-01 +148 3.414455e+00 -4.699593e+00 -5.237205e-01 2.668489e-01 +149 3.497943e+00 -4.814506e+00 -2.753362e-01 1.402908e-01 +150 3.526712e+00 -4.854102e+00 0 0 +151 3.497943e+00 -4.814506e+00 2.753362e-01 -1.402908e-01 +152 3.414455e+00 -4.699593e+00 5.237205e-01 -2.668489e-01 +153 3.284418e+00 -4.520613e+00 7.208394e-01 -3.672860e-01 +154 3.120562e+00 -4.295085e+00 8.473976e-01 -4.317706e-01 +155 2.938926e+00 -4.045085e+00 8.910065e-01 -4.539905e-01 +156 2.757291e+00 -3.795085e+00 8.473976e-01 -4.317706e-01 +157 2.593435e+00 -3.569557e+00 7.208394e-01 -3.672860e-01 +158 2.463398e+00 -3.390576e+00 5.237205e-01 -2.668489e-01 +159 2.379909e+00 -3.275664e+00 2.753362e-01 -1.402908e-01 +160 3.236068e+00 -2.351141e+00 -1.164708e-16 3.784367e-17 +161 3.275664e+00 -2.379909e+00 -2.938926e-01 9.549150e-02 +162 3.390576e+00 -2.463398e+00 -5.590170e-01 1.816356e-01 +163 3.569557e+00 -2.593435e+00 -7.694209e-01 2.500000e-01 +164 3.795085e+00 -2.757291e+00 -9.045085e-01 2.938926e-01 +165 4.045085e+00 -2.938926e+00 -9.510565e-01 3.090170e-01 +166 4.295085e+00 -3.120562e+00 -9.045085e-01 2.938926e-01 +167 4.520613e+00 -3.284418e+00 -7.694209e-01 2.500000e-01 +168 4.699593e+00 -3.414455e+00 -5.590170e-01 1.816356e-01 +169 4.814506e+00 -3.497943e+00 -2.938926e-01 9.549150e-02 +170 4.854102e+00 -3.526712e+00 0 0 +171 4.814506e+00 -3.497943e+00 2.938926e-01 -9.549150e-02 +172 4.699593e+00 -3.414455e+00 5.590170e-01 -1.816356e-01 +173 4.520613e+00 -3.284418e+00 7.694209e-01 -2.500000e-01 +174 4.295085e+00 -3.120562e+00 9.045085e-01 -2.938926e-01 +175 4.045085e+00 -2.938926e+00 9.510565e-01 -3.090170e-01 +176 3.795085e+00 -2.757291e+00 9.045085e-01 -2.938926e-01 +177 3.569557e+00 -2.593435e+00 7.694209e-01 -2.500000e-01 +178 3.390576e+00 -2.463398e+00 5.590170e-01 -1.816356e-01 +179 3.275664e+00 -2.379909e+00 2.938926e-01 -9.549150e-02 +180 3.804226e+00 -1.236068e+00 -1.209569e-16 1.915770e-17 +181 3.850774e+00 -1.251192e+00 -3.052125e-01 4.834091e-02 +182 3.985862e+00 -1.295085e+00 -5.805486e-01 9.194987e-02 +183 4.196266e+00 -1.363449e+00 -7.990567e-01 1.265581e-01 +184 4.461390e+00 -1.449593e+00 -9.393474e-01 1.487780e-01 +185 4.755283e+00 -1.545085e+00 -9.876883e-01 1.564345e-01 +186 5.049175e+00 -1.640576e+00 -9.393474e-01 1.487780e-01 +187 5.314300e+00 -1.726721e+00 -7.990567e-01 1.265581e-01 +188 5.524703e+00 -1.795085e+00 -5.805486e-01 9.194987e-02 +189 5.659791e+00 -1.838978e+00 -3.052125e-01 4.834091e-02 +190 5.706339e+00 -1.854102e+00 0 0 +191 5.659791e+00 -1.838978e+00 3.052125e-01 -4.834091e-02 +192 5.524703e+00 -1.795085e+00 5.805486e-01 -9.194987e-02 +193 5.314300e+00 -1.726721e+00 7.990567e-01 -1.265581e-01 +194 5.049175e+00 -1.640576e+00 9.393474e-01 -1.487780e-01 +195 4.755283e+00 -1.545085e+00 9.876883e-01 -1.564345e-01 +196 4.461390e+00 -1.449593e+00 9.393474e-01 -1.487780e-01 +197 4.196266e+00 -1.363449e+00 7.990567e-01 -1.265581e-01 +198 3.985862e+00 -1.295085e+00 5.805486e-01 -9.194987e-02 +199 3.850774e+00 -1.251192e+00 3.052125e-01 -4.834091e-02 +200 4 0 -1.224647e-16 0 +201 4.048943e+00 0 -3.090170e-01 0 +202 4.190983e+00 0 -5.877853e-01 0 +203 4.412215e+00 0 -8.090170e-01 0 +204 4.690983e+00 0 -9.510565e-01 0 +205 5 0 -1 0 +206 5.309017e+00 0 -9.510565e-01 0 +207 5.587785e+00 0 -8.090170e-01 0 +208 5.809017e+00 0 -5.877853e-01 0 +209 5.951057e+00 0 -3.090170e-01 0 +210 6 0 0 0 +211 5.951057e+00 0 3.090170e-01 0 +212 5.809017e+00 0 5.877853e-01 0 +213 5.587785e+00 0 8.090170e-01 0 +214 5.309017e+00 0 9.510565e-01 0 +215 5 0 1 0 +216 4.690983e+00 0 9.510565e-01 0 +217 4.412215e+00 0 8.090170e-01 0 +218 4.190983e+00 0 5.877853e-01 0 +219 4.048943e+00 0 3.090170e-01 0 +220 3.804226e+00 1.236068e+00 -1.209569e-16 -1.915770e-17 +221 3.850774e+00 1.251192e+00 -3.052125e-01 -4.834091e-02 +222 3.985862e+00 1.295085e+00 -5.805486e-01 -9.194987e-02 +223 4.196266e+00 1.363449e+00 -7.990567e-01 -1.265581e-01 +224 4.461390e+00 1.449593e+00 -9.393474e-01 -1.487780e-01 +225 4.755283e+00 1.545085e+00 -9.876883e-01 -1.564345e-01 +226 5.049175e+00 1.640576e+00 -9.393474e-01 -1.487780e-01 +227 5.314300e+00 1.726721e+00 -7.990567e-01 -1.265581e-01 +228 5.524703e+00 1.795085e+00 -5.805486e-01 -9.194987e-02 +229 5.659791e+00 1.838978e+00 -3.052125e-01 -4.834091e-02 +230 5.706339e+00 1.854102e+00 0 0 +231 5.659791e+00 1.838978e+00 3.052125e-01 4.834091e-02 +232 5.524703e+00 1.795085e+00 5.805486e-01 9.194987e-02 +233 5.314300e+00 1.726721e+00 7.990567e-01 1.265581e-01 +234 5.049175e+00 1.640576e+00 9.393474e-01 1.487780e-01 +235 4.755283e+00 1.545085e+00 9.876883e-01 1.564345e-01 +236 4.461390e+00 1.449593e+00 9.393474e-01 1.487780e-01 +237 4.196266e+00 1.363449e+00 7.990567e-01 1.265581e-01 +238 3.985862e+00 1.295085e+00 5.805486e-01 9.194987e-02 +239 3.850774e+00 1.251192e+00 3.052125e-01 4.834091e-02 +240 3.236068e+00 2.351141e+00 -1.164708e-16 -3.784367e-17 +241 3.275664e+00 2.379909e+00 -2.938926e-01 -9.549150e-02 +242 3.390576e+00 2.463398e+00 -5.590170e-01 -1.816356e-01 +243 3.569557e+00 2.593435e+00 -7.694209e-01 -2.500000e-01 +244 3.795085e+00 2.757291e+00 -9.045085e-01 -2.938926e-01 +245 4.045085e+00 2.938926e+00 -9.510565e-01 -3.090170e-01 +246 4.295085e+00 3.120562e+00 -9.045085e-01 -2.938926e-01 +247 4.520613e+00 3.284418e+00 -7.694209e-01 -2.500000e-01 +248 4.699593e+00 3.414455e+00 -5.590170e-01 -1.816356e-01 +249 4.814506e+00 3.497943e+00 -2.938926e-01 -9.549150e-02 +250 4.854102e+00 3.526712e+00 0 0 +251 4.814506e+00 3.497943e+00 2.938926e-01 9.549150e-02 +252 4.699593e+00 3.414455e+00 5.590170e-01 1.816356e-01 +253 4.520613e+00 3.284418e+00 7.694209e-01 2.500000e-01 +254 4.295085e+00 3.120562e+00 9.045085e-01 2.938926e-01 +255 4.045085e+00 2.938926e+00 9.510565e-01 3.090170e-01 +256 3.795085e+00 2.757291e+00 9.045085e-01 2.938926e-01 +257 3.569557e+00 2.593435e+00 7.694209e-01 2.500000e-01 +258 3.390576e+00 2.463398e+00 5.590170e-01 1.816356e-01 +259 3.275664e+00 2.379909e+00 2.938926e-01 9.549150e-02 +260 2.351141e+00 3.236068e+00 -1.091168e-16 -5.559780e-17 +261 2.379909e+00 3.275664e+00 -2.753362e-01 -1.402908e-01 +262 2.463398e+00 3.390576e+00 -5.237205e-01 -2.668489e-01 +263 2.593435e+00 3.569557e+00 -7.208394e-01 -3.672860e-01 +264 2.757291e+00 3.795085e+00 -8.473976e-01 -4.317706e-01 +265 2.938926e+00 4.045085e+00 -8.910065e-01 -4.539905e-01 +266 3.120562e+00 4.295085e+00 -8.473976e-01 -4.317706e-01 +267 3.284418e+00 4.520613e+00 -7.208394e-01 -3.672860e-01 +268 3.414455e+00 4.699593e+00 -5.237205e-01 -2.668489e-01 +269 3.497943e+00 4.814506e+00 -2.753362e-01 -1.402908e-01 +270 3.526712e+00 4.854102e+00 0 0 +271 3.497943e+00 4.814506e+00 2.753362e-01 1.402908e-01 +272 3.414455e+00 4.699593e+00 5.237205e-01 2.668489e-01 +273 3.284418e+00 4.520613e+00 7.208394e-01 3.672860e-01 +274 3.120562e+00 4.295085e+00 8.473976e-01 4.317706e-01 +275 2.938926e+00 4.045085e+00 8.910065e-01 4.539905e-01 +276 2.757291e+00 3.795085e+00 8.473976e-01 4.317706e-01 +277 2.593435e+00 3.569557e+00 7.208394e-01 3.672860e-01 +278 2.463398e+00 3.390576e+00 5.237205e-01 2.668489e-01 +279 2.379909e+00 3.275664e+00 2.753362e-01 1.402908e-01 +280 1.236068e+00 3.804226e+00 -9.907601e-17 -7.198293e-17 +281 1.251192e+00 3.850774e+00 -2.500000e-01 -1.816356e-01 +282 1.295085e+00 3.985862e+00 -4.755283e-01 -3.454915e-01 +283 1.363449e+00 4.196266e+00 -6.545085e-01 -4.755283e-01 +284 1.449593e+00 4.461390e+00 -7.694209e-01 -5.590170e-01 +285 1.545085e+00 4.755283e+00 -8.090170e-01 -5.877853e-01 +286 1.640576e+00 5.049175e+00 -7.694209e-01 -5.590170e-01 +287 1.726721e+00 5.314300e+00 -6.545085e-01 -4.755283e-01 +288 1.795085e+00 5.524703e+00 -4.755283e-01 -3.454915e-01 +289 1.838978e+00 5.659791e+00 -2.500000e-01 -1.816356e-01 +290 1.854102e+00 5.706339e+00 0 0 +291 1.838978e+00 5.659791e+00 2.500000e-01 1.816356e-01 +292 1.795085e+00 5.524703e+00 4.755283e-01 3.454915e-01 +293 1.726721e+00 5.314300e+00 6.545085e-01 4.755283e-01 +294 1.640576e+00 5.049175e+00 7.694209e-01 5.590170e-01 +295 1.545085e+00 4.755283e+00 8.090170e-01 5.877853e-01 +296 1.449593e+00 4.461390e+00 7.694209e-01 5.590170e-01 +297 1.363449e+00 4.196266e+00 6.545085e-01 4.755283e-01 +298 1.295085e+00 3.985862e+00 4.755283e-01 3.454915e-01 +299 1.251192e+00 3.850774e+00 2.500000e-01 1.816356e-01 +300 2.449294e-16 4 -8.659561e-17 -8.659561e-17 +301 2.479263e-16 4.048943e+00 -2.185080e-01 -2.185080e-01 +302 2.566237e-16 4.190983e+00 -4.156269e-01 -4.156269e-01 +303 2.701702e-16 4.412215e+00 -5.720614e-01 -5.720614e-01 +304 2.872399e-16 4.690983e+00 -6.724985e-01 -6.724985e-01 +305 3.061617e-16 5 -7.071068e-01 -7.071068e-01 +306 3.250835e-16 5.309017e+00 -6.724985e-01 -6.724985e-01 +307 3.421532e-16 5.587785e+00 -5.720614e-01 -5.720614e-01 +308 3.556997e-16 5.809017e+00 -4.156269e-01 -4.156269e-01 +309 3.643971e-16 5.951057e+00 -2.185080e-01 -2.185080e-01 +310 3.673940e-16 6 0 0 +311 3.643971e-16 5.951057e+00 2.185080e-01 2.185080e-01 +312 3.556997e-16 5.809017e+00 4.156269e-01 4.156269e-01 +313 3.421532e-16 5.587785e+00 5.720614e-01 5.720614e-01 +314 3.250835e-16 5.309017e+00 6.724985e-01 6.724985e-01 +315 3.061617e-16 5 7.071068e-01 7.071068e-01 +316 2.872399e-16 4.690983e+00 6.724985e-01 6.724985e-01 +317 2.701702e-16 4.412215e+00 5.720614e-01 5.720614e-01 +318 2.566237e-16 4.190983e+00 4.156269e-01 4.156269e-01 +319 2.479263e-16 4.048943e+00 2.185080e-01 2.185080e-01 +320 -1.236068e+00 3.804226e+00 -7.198293e-17 -9.907601e-17 +321 -1.251192e+00 3.850774e+00 -1.816356e-01 -2.500000e-01 +322 -1.295085e+00 3.985862e+00 -3.454915e-01 -4.755283e-01 +323 -1.363449e+00 4.196266e+00 -4.755283e-01 -6.545085e-01 +324 -1.449593e+00 4.461390e+00 -5.590170e-01 -7.694209e-01 +325 -1.545085e+00 4.755283e+00 -5.877853e-01 -8.090170e-01 +326 -1.640576e+00 5.049175e+00 -5.590170e-01 -7.694209e-01 +327 -1.726721e+00 5.314300e+00 -4.755283e-01 -6.545085e-01 +328 -1.795085e+00 5.524703e+00 -3.454915e-01 -4.755283e-01 +329 -1.838978e+00 5.659791e+00 -1.816356e-01 -2.500000e-01 +330 -1.854102e+00 5.706339e+00 0 0 +331 -1.838978e+00 5.659791e+00 1.816356e-01 2.500000e-01 +332 -1.795085e+00 5.524703e+00 3.454915e-01 4.755283e-01 +333 -1.726721e+00 5.314300e+00 4.755283e-01 6.545085e-01 +334 -1.640576e+00 5.049175e+00 5.590170e-01 7.694209e-01 +335 -1.545085e+00 4.755283e+00 5.877853e-01 8.090170e-01 +336 -1.449593e+00 4.461390e+00 5.590170e-01 7.694209e-01 +337 -1.363449e+00 4.196266e+00 4.755283e-01 6.545085e-01 +338 -1.295085e+00 3.985862e+00 3.454915e-01 4.755283e-01 +339 -1.251192e+00 3.850774e+00 1.816356e-01 2.500000e-01 +340 -2.351141e+00 3.236068e+00 -5.559780e-17 -1.091168e-16 +341 -2.379909e+00 3.275664e+00 -1.402908e-01 -2.753362e-01 +342 -2.463398e+00 3.390576e+00 -2.668489e-01 -5.237205e-01 +343 -2.593435e+00 3.569557e+00 -3.672860e-01 -7.208394e-01 +344 -2.757291e+00 3.795085e+00 -4.317706e-01 -8.473976e-01 +345 -2.938926e+00 4.045085e+00 -4.539905e-01 -8.910065e-01 +346 -3.120562e+00 4.295085e+00 -4.317706e-01 -8.473976e-01 +347 -3.284418e+00 4.520613e+00 -3.672860e-01 -7.208394e-01 +348 -3.414455e+00 4.699593e+00 -2.668489e-01 -5.237205e-01 +349 -3.497943e+00 4.814506e+00 -1.402908e-01 -2.753362e-01 +350 -3.526712e+00 4.854102e+00 0 0 +351 -3.497943e+00 4.814506e+00 1.402908e-01 2.753362e-01 +352 -3.414455e+00 4.699593e+00 2.668489e-01 5.237205e-01 +353 -3.284418e+00 4.520613e+00 3.672860e-01 7.208394e-01 +354 -3.120562e+00 4.295085e+00 4.317706e-01 8.473976e-01 +355 -2.938926e+00 4.045085e+00 4.539905e-01 8.910065e-01 +356 -2.757291e+00 3.795085e+00 4.317706e-01 8.473976e-01 +357 -2.593435e+00 3.569557e+00 3.672860e-01 7.208394e-01 +358 -2.463398e+00 3.390576e+00 2.668489e-01 5.237205e-01 +359 -2.379909e+00 3.275664e+00 1.402908e-01 2.753362e-01 +360 -3.236068e+00 2.351141e+00 -3.784367e-17 -1.164708e-16 +361 -3.275664e+00 2.379909e+00 -9.549150e-02 -2.938926e-01 +362 -3.390576e+00 2.463398e+00 -1.816356e-01 -5.590170e-01 +363 -3.569557e+00 2.593435e+00 -2.500000e-01 -7.694209e-01 +364 -3.795085e+00 2.757291e+00 -2.938926e-01 -9.045085e-01 +365 -4.045085e+00 2.938926e+00 -3.090170e-01 -9.510565e-01 +366 -4.295085e+00 3.120562e+00 -2.938926e-01 -9.045085e-01 +367 -4.520613e+00 3.284418e+00 -2.500000e-01 -7.694209e-01 +368 -4.699593e+00 3.414455e+00 -1.816356e-01 -5.590170e-01 +369 -4.814506e+00 3.497943e+00 -9.549150e-02 -2.938926e-01 +370 -4.854102e+00 3.526712e+00 0 0 +371 -4.814506e+00 3.497943e+00 9.549150e-02 2.938926e-01 +372 -4.699593e+00 3.414455e+00 1.816356e-01 5.590170e-01 +373 -4.520613e+00 3.284418e+00 2.500000e-01 7.694209e-01 +374 -4.295085e+00 3.120562e+00 2.938926e-01 9.045085e-01 +375 -4.045085e+00 2.938926e+00 3.090170e-01 9.510565e-01 +376 -3.795085e+00 2.757291e+00 2.938926e-01 9.045085e-01 +377 -3.569557e+00 2.593435e+00 2.500000e-01 7.694209e-01 +378 -3.390576e+00 2.463398e+00 1.816356e-01 5.590170e-01 +379 -3.275664e+00 2.379909e+00 9.549150e-02 2.938926e-01 +380 -3.804226e+00 1.236068e+00 -1.915770e-17 -1.209569e-16 +381 -3.850774e+00 1.251192e+00 -4.834091e-02 -3.052125e-01 +382 -3.985862e+00 1.295085e+00 -9.194987e-02 -5.805486e-01 +383 -4.196266e+00 1.363449e+00 -1.265581e-01 -7.990567e-01 +384 -4.461390e+00 1.449593e+00 -1.487780e-01 -9.393474e-01 +385 -4.755283e+00 1.545085e+00 -1.564345e-01 -9.876883e-01 +386 -5.049175e+00 1.640576e+00 -1.487780e-01 -9.393474e-01 +387 -5.314300e+00 1.726721e+00 -1.265581e-01 -7.990567e-01 +388 -5.524703e+00 1.795085e+00 -9.194987e-02 -5.805486e-01 +389 -5.659791e+00 1.838978e+00 -4.834091e-02 -3.052125e-01 +390 -5.706339e+00 1.854102e+00 0 0 +391 -5.659791e+00 1.838978e+00 4.834091e-02 3.052125e-01 +392 -5.524703e+00 1.795085e+00 9.194987e-02 5.805486e-01 +393 -5.314300e+00 1.726721e+00 1.265581e-01 7.990567e-01 +394 -5.049175e+00 1.640576e+00 1.487780e-01 9.393474e-01 +395 -4.755283e+00 1.545085e+00 1.564345e-01 9.876883e-01 +396 -4.461390e+00 1.449593e+00 1.487780e-01 9.393474e-01 +397 -4.196266e+00 1.363449e+00 1.265581e-01 7.990567e-01 +398 -3.985862e+00 1.295085e+00 9.194987e-02 5.805486e-01 +399 -3.850774e+00 1.251192e+00 4.834091e-02 3.052125e-01 diff --git a/HDVF/test/HDVF/data/dim_4/klein4.simp b/HDVF/test/HDVF/data/dim_4/klein4.simp new file mode 100644 index 00000000000..b082a68c60b --- /dev/null +++ b/HDVF/test/HDVF/data/dim_4/klein4.simp @@ -0,0 +1,800 @@ +0 1 21 +0 21 20 +1 2 22 +1 22 21 +2 3 23 +2 23 22 +3 4 24 +3 24 23 +4 5 25 +4 25 24 +5 6 26 +5 26 25 +6 7 27 +6 27 26 +7 8 28 +7 28 27 +8 9 29 +8 29 28 +9 10 30 +9 30 29 +10 11 31 +10 31 30 +11 12 32 +11 32 31 +12 13 33 +12 33 32 +13 14 34 +13 34 33 +14 15 35 +14 35 34 +15 16 36 +15 36 35 +16 17 37 +16 37 36 +17 18 38 +17 38 37 +18 19 39 +18 39 38 +19 0 20 +19 20 39 +20 21 41 +20 41 40 +21 22 42 +21 42 41 +22 23 43 +22 43 42 +23 24 44 +23 44 43 +24 25 45 +24 45 44 +25 26 46 +25 46 45 +26 27 47 +26 47 46 +27 28 48 +27 48 47 +28 29 49 +28 49 48 +29 30 50 +29 50 49 +30 31 51 +30 51 50 +31 32 52 +31 52 51 +32 33 53 +32 53 52 +33 34 54 +33 54 53 +34 35 55 +34 55 54 +35 36 56 +35 56 55 +36 37 57 +36 57 56 +37 38 58 +37 58 57 +38 39 59 +38 59 58 +39 20 40 +39 40 59 +40 41 61 +40 61 60 +41 42 62 +41 62 61 +42 43 63 +42 63 62 +43 44 64 +43 64 63 +44 45 65 +44 65 64 +45 46 66 +45 66 65 +46 47 67 +46 67 66 +47 48 68 +47 68 67 +48 49 69 +48 69 68 +49 50 70 +49 70 69 +50 51 71 +50 71 70 +51 52 72 +51 72 71 +52 53 73 +52 73 72 +53 54 74 +53 74 73 +54 55 75 +54 75 74 +55 56 76 +55 76 75 +56 57 77 +56 77 76 +57 58 78 +57 78 77 +58 59 79 +58 79 78 +59 40 60 +59 60 79 +60 61 81 +60 81 80 +61 62 82 +61 82 81 +62 63 83 +62 83 82 +63 64 84 +63 84 83 +64 65 85 +64 85 84 +65 66 86 +65 86 85 +66 67 87 +66 87 86 +67 68 88 +67 88 87 +68 69 89 +68 89 88 +69 70 90 +69 90 89 +70 71 91 +70 91 90 +71 72 92 +71 92 91 +72 73 93 +72 93 92 +73 74 94 +73 94 93 +74 75 95 +74 95 94 +75 76 96 +75 96 95 +76 77 97 +76 97 96 +77 78 98 +77 98 97 +78 79 99 +78 99 98 +79 60 80 +79 80 99 +80 81 101 +80 101 100 +81 82 102 +81 102 101 +82 83 103 +82 103 102 +83 84 104 +83 104 103 +84 85 105 +84 105 104 +85 86 106 +85 106 105 +86 87 107 +86 107 106 +87 88 108 +87 108 107 +88 89 109 +88 109 108 +89 90 110 +89 110 109 +90 91 111 +90 111 110 +91 92 112 +91 112 111 +92 93 113 +92 113 112 +93 94 114 +93 114 113 +94 95 115 +94 115 114 +95 96 116 +95 116 115 +96 97 117 +96 117 116 +97 98 118 +97 118 117 +98 99 119 +98 119 118 +99 80 100 +99 100 119 +100 101 121 +100 121 120 +101 102 122 +101 122 121 +102 103 123 +102 123 122 +103 104 124 +103 124 123 +104 105 125 +104 125 124 +105 106 126 +105 126 125 +106 107 127 +106 127 126 +107 108 128 +107 128 127 +108 109 129 +108 129 128 +109 110 130 +109 130 129 +110 111 131 +110 131 130 +111 112 132 +111 132 131 +112 113 133 +112 133 132 +113 114 134 +113 134 133 +114 115 135 +114 135 134 +115 116 136 +115 136 135 +116 117 137 +116 137 136 +117 118 138 +117 138 137 +118 119 139 +118 139 138 +119 100 120 +119 120 139 +120 121 141 +120 141 140 +121 122 142 +121 142 141 +122 123 143 +122 143 142 +123 124 144 +123 144 143 +124 125 145 +124 145 144 +125 126 146 +125 146 145 +126 127 147 +126 147 146 +127 128 148 +127 148 147 +128 129 149 +128 149 148 +129 130 150 +129 150 149 +130 131 151 +130 151 150 +131 132 152 +131 152 151 +132 133 153 +132 153 152 +133 134 154 +133 154 153 +134 135 155 +134 155 154 +135 136 156 +135 156 155 +136 137 157 +136 157 156 +137 138 158 +137 158 157 +138 139 159 +138 159 158 +139 120 140 +139 140 159 +140 141 161 +140 161 160 +141 142 162 +141 162 161 +142 143 163 +142 163 162 +143 144 164 +143 164 163 +144 145 165 +144 165 164 +145 146 166 +145 166 165 +146 147 167 +146 167 166 +147 148 168 +147 168 167 +148 149 169 +148 169 168 +149 150 170 +149 170 169 +150 151 171 +150 171 170 +151 152 172 +151 172 171 +152 153 173 +152 173 172 +153 154 174 +153 174 173 +154 155 175 +154 175 174 +155 156 176 +155 176 175 +156 157 177 +156 177 176 +157 158 178 +157 178 177 +158 159 179 +158 179 178 +159 140 160 +159 160 179 +160 161 181 +160 181 180 +161 162 182 +161 182 181 +162 163 183 +162 183 182 +163 164 184 +163 184 183 +164 165 185 +164 185 184 +165 166 186 +165 186 185 +166 167 187 +166 187 186 +167 168 188 +167 188 187 +168 169 189 +168 189 188 +169 170 190 +169 190 189 +170 171 191 +170 191 190 +171 172 192 +171 192 191 +172 173 193 +172 193 192 +173 174 194 +173 194 193 +174 175 195 +174 195 194 +175 176 196 +175 196 195 +176 177 197 +176 197 196 +177 178 198 +177 198 197 +178 179 199 +178 199 198 +179 160 180 +179 180 199 +180 181 201 +180 201 200 +181 182 202 +181 202 201 +182 183 203 +182 203 202 +183 184 204 +183 204 203 +184 185 205 +184 205 204 +185 186 206 +185 206 205 +186 187 207 +186 207 206 +187 188 208 +187 208 207 +188 189 209 +188 209 208 +189 190 210 +189 210 209 +190 191 211 +190 211 210 +191 192 212 +191 212 211 +192 193 213 +192 213 212 +193 194 214 +193 214 213 +194 195 215 +194 215 214 +195 196 216 +195 216 215 +196 197 217 +196 217 216 +197 198 218 +197 218 217 +198 199 219 +198 219 218 +199 180 200 +199 200 219 +200 201 221 +200 221 220 +201 202 222 +201 222 221 +202 203 223 +202 223 222 +203 204 224 +203 224 223 +204 205 225 +204 225 224 +205 206 226 +205 226 225 +206 207 227 +206 227 226 +207 208 228 +207 228 227 +208 209 229 +208 229 228 +209 210 230 +209 230 229 +210 211 231 +210 231 230 +211 212 232 +211 232 231 +212 213 233 +212 233 232 +213 214 234 +213 234 233 +214 215 235 +214 235 234 +215 216 236 +215 236 235 +216 217 237 +216 237 236 +217 218 238 +217 238 237 +218 219 239 +218 239 238 +219 200 220 +219 220 239 +220 221 241 +220 241 240 +221 222 242 +221 242 241 +222 223 243 +222 243 242 +223 224 244 +223 244 243 +224 225 245 +224 245 244 +225 226 246 +225 246 245 +226 227 247 +226 247 246 +227 228 248 +227 248 247 +228 229 249 +228 249 248 +229 230 250 +229 250 249 +230 231 251 +230 251 250 +231 232 252 +231 252 251 +232 233 253 +232 253 252 +233 234 254 +233 254 253 +234 235 255 +234 255 254 +235 236 256 +235 256 255 +236 237 257 +236 257 256 +237 238 258 +237 258 257 +238 239 259 +238 259 258 +239 220 240 +239 240 259 +240 241 261 +240 261 260 +241 242 262 +241 262 261 +242 243 263 +242 263 262 +243 244 264 +243 264 263 +244 245 265 +244 265 264 +245 246 266 +245 266 265 +246 247 267 +246 267 266 +247 248 268 +247 268 267 +248 249 269 +248 269 268 +249 250 270 +249 270 269 +250 251 271 +250 271 270 +251 252 272 +251 272 271 +252 253 273 +252 273 272 +253 254 274 +253 274 273 +254 255 275 +254 275 274 +255 256 276 +255 276 275 +256 257 277 +256 277 276 +257 258 278 +257 278 277 +258 259 279 +258 279 278 +259 240 260 +259 260 279 +260 261 281 +260 281 280 +261 262 282 +261 282 281 +262 263 283 +262 283 282 +263 264 284 +263 284 283 +264 265 285 +264 285 284 +265 266 286 +265 286 285 +266 267 287 +266 287 286 +267 268 288 +267 288 287 +268 269 289 +268 289 288 +269 270 290 +269 290 289 +270 271 291 +270 291 290 +271 272 292 +271 292 291 +272 273 293 +272 293 292 +273 274 294 +273 294 293 +274 275 295 +274 295 294 +275 276 296 +275 296 295 +276 277 297 +276 297 296 +277 278 298 +277 298 297 +278 279 299 +278 299 298 +279 260 280 +279 280 299 +280 281 301 +280 301 300 +281 282 302 +281 302 301 +282 283 303 +282 303 302 +283 284 304 +283 304 303 +284 285 305 +284 305 304 +285 286 306 +285 306 305 +286 287 307 +286 307 306 +287 288 308 +287 308 307 +288 289 309 +288 309 308 +289 290 310 +289 310 309 +290 291 311 +290 311 310 +291 292 312 +291 312 311 +292 293 313 +292 313 312 +293 294 314 +293 314 313 +294 295 315 +294 315 314 +295 296 316 +295 316 315 +296 297 317 +296 317 316 +297 298 318 +297 318 317 +298 299 319 +298 319 318 +299 280 300 +299 300 319 +300 301 321 +300 321 320 +301 302 322 +301 322 321 +302 303 323 +302 323 322 +303 304 324 +303 324 323 +304 305 325 +304 325 324 +305 306 326 +305 326 325 +306 307 327 +306 327 326 +307 308 328 +307 328 327 +308 309 329 +308 329 328 +309 310 330 +309 330 329 +310 311 331 +310 331 330 +311 312 332 +311 332 331 +312 313 333 +312 333 332 +313 314 334 +313 334 333 +314 315 335 +314 335 334 +315 316 336 +315 336 335 +316 317 337 +316 337 336 +317 318 338 +317 338 337 +318 319 339 +318 339 338 +319 300 320 +319 320 339 +320 321 341 +320 341 340 +321 322 342 +321 342 341 +322 323 343 +322 343 342 +323 324 344 +323 344 343 +324 325 345 +324 345 344 +325 326 346 +325 346 345 +326 327 347 +326 347 346 +327 328 348 +327 348 347 +328 329 349 +328 349 348 +329 330 350 +329 350 349 +330 331 351 +330 351 350 +331 332 352 +331 352 351 +332 333 353 +332 353 352 +333 334 354 +333 354 353 +334 335 355 +334 355 354 +335 336 356 +335 356 355 +336 337 357 +336 357 356 +337 338 358 +337 358 357 +338 339 359 +338 359 358 +339 320 340 +339 340 359 +340 341 361 +340 361 360 +341 342 362 +341 362 361 +342 343 363 +342 363 362 +343 344 364 +343 364 363 +344 345 365 +344 365 364 +345 346 366 +345 366 365 +346 347 367 +346 367 366 +347 348 368 +347 368 367 +348 349 369 +348 369 368 +349 350 370 +349 370 369 +350 351 371 +350 371 370 +351 352 372 +351 372 371 +352 353 373 +352 373 372 +353 354 374 +353 374 373 +354 355 375 +354 375 374 +355 356 376 +355 376 375 +356 357 377 +356 377 376 +357 358 378 +357 378 377 +358 359 379 +358 379 378 +359 340 360 +359 360 379 +360 361 381 +360 381 380 +361 362 382 +361 382 381 +362 363 383 +362 383 382 +363 364 384 +363 384 383 +364 365 385 +364 385 384 +365 366 386 +365 386 385 +366 367 387 +366 387 386 +367 368 388 +367 388 387 +368 369 389 +368 389 388 +369 370 390 +369 390 389 +370 371 391 +370 391 390 +371 372 392 +371 392 391 +372 373 393 +372 393 392 +373 374 394 +373 394 393 +374 375 395 +374 395 394 +375 376 396 +375 396 395 +376 377 397 +376 397 396 +377 378 398 +377 398 397 +378 379 399 +378 399 398 +379 360 380 +379 380 399 +380 381 1 +380 1 0 +381 382 2 +381 2 1 +382 383 3 +382 3 2 +383 384 4 +383 4 3 +384 385 5 +384 5 4 +385 386 6 +385 6 5 +386 387 7 +386 7 6 +387 388 8 +387 8 7 +388 389 9 +388 9 8 +389 390 10 +389 10 9 +390 391 11 +390 11 10 +391 392 12 +391 12 11 +392 393 13 +392 13 12 +393 394 14 +393 14 13 +394 395 15 +394 15 14 +395 396 16 +395 16 15 +396 397 17 +396 17 16 +397 398 18 +397 18 17 +398 399 19 +398 19 18 +399 380 0 +399 0 19 diff --git a/HDVF/test/HDVF/data/dim_4/torus4D.nodes b/HDVF/test/HDVF/data/dim_4/torus4D.nodes new file mode 100644 index 00000000000..0635f78d339 --- /dev/null +++ b/HDVF/test/HDVF/data/dim_4/torus4D.nodes @@ -0,0 +1,401 @@ +400 4 0 0 +0 -7.071068e-01 -8.659561e-17 -7.071068e-01 -8.659561e-17 +1 -6.724985e-01 -2.185080e-01 -7.071068e-01 -8.659561e-17 +2 -5.720614e-01 -4.156269e-01 -7.071068e-01 -8.659561e-17 +3 -4.156269e-01 -5.720614e-01 -7.071068e-01 -8.659561e-17 +4 -2.185080e-01 -6.724985e-01 -7.071068e-01 -8.659561e-17 +5 4.329780e-17 -7.071068e-01 -7.071068e-01 -8.659561e-17 +6 2.185080e-01 -6.724985e-01 -7.071068e-01 -8.659561e-17 +7 4.156269e-01 -5.720614e-01 -7.071068e-01 -8.659561e-17 +8 5.720614e-01 -4.156269e-01 -7.071068e-01 -8.659561e-17 +9 6.724985e-01 -2.185080e-01 -7.071068e-01 -8.659561e-17 +10 7.071068e-01 0 -7.071068e-01 -8.659561e-17 +11 6.724985e-01 2.185080e-01 -7.071068e-01 -8.659561e-17 +12 5.720614e-01 4.156269e-01 -7.071068e-01 -8.659561e-17 +13 4.156269e-01 5.720614e-01 -7.071068e-01 -8.659561e-17 +14 2.185080e-01 6.724985e-01 -7.071068e-01 -8.659561e-17 +15 4.329780e-17 7.071068e-01 -7.071068e-01 -8.659561e-17 +16 -2.185080e-01 6.724985e-01 -7.071068e-01 -8.659561e-17 +17 -4.156269e-01 5.720614e-01 -7.071068e-01 -8.659561e-17 +18 -5.720614e-01 4.156269e-01 -7.071068e-01 -8.659561e-17 +19 -6.724985e-01 2.185080e-01 -7.071068e-01 -8.659561e-17 +20 -7.071068e-01 -8.659561e-17 -6.724985e-01 -2.185080e-01 +21 -6.724985e-01 -2.185080e-01 -6.724985e-01 -2.185080e-01 +22 -5.720614e-01 -4.156269e-01 -6.724985e-01 -2.185080e-01 +23 -4.156269e-01 -5.720614e-01 -6.724985e-01 -2.185080e-01 +24 -2.185080e-01 -6.724985e-01 -6.724985e-01 -2.185080e-01 +25 4.329780e-17 -7.071068e-01 -6.724985e-01 -2.185080e-01 +26 2.185080e-01 -6.724985e-01 -6.724985e-01 -2.185080e-01 +27 4.156269e-01 -5.720614e-01 -6.724985e-01 -2.185080e-01 +28 5.720614e-01 -4.156269e-01 -6.724985e-01 -2.185080e-01 +29 6.724985e-01 -2.185080e-01 -6.724985e-01 -2.185080e-01 +30 7.071068e-01 0 -6.724985e-01 -2.185080e-01 +31 6.724985e-01 2.185080e-01 -6.724985e-01 -2.185080e-01 +32 5.720614e-01 4.156269e-01 -6.724985e-01 -2.185080e-01 +33 4.156269e-01 5.720614e-01 -6.724985e-01 -2.185080e-01 +34 2.185080e-01 6.724985e-01 -6.724985e-01 -2.185080e-01 +35 4.329780e-17 7.071068e-01 -6.724985e-01 -2.185080e-01 +36 -2.185080e-01 6.724985e-01 -6.724985e-01 -2.185080e-01 +37 -4.156269e-01 5.720614e-01 -6.724985e-01 -2.185080e-01 +38 -5.720614e-01 4.156269e-01 -6.724985e-01 -2.185080e-01 +39 -6.724985e-01 2.185080e-01 -6.724985e-01 -2.185080e-01 +40 -7.071068e-01 -8.659561e-17 -5.720614e-01 -4.156269e-01 +41 -6.724985e-01 -2.185080e-01 -5.720614e-01 -4.156269e-01 +42 -5.720614e-01 -4.156269e-01 -5.720614e-01 -4.156269e-01 +43 -4.156269e-01 -5.720614e-01 -5.720614e-01 -4.156269e-01 +44 -2.185080e-01 -6.724985e-01 -5.720614e-01 -4.156269e-01 +45 4.329780e-17 -7.071068e-01 -5.720614e-01 -4.156269e-01 +46 2.185080e-01 -6.724985e-01 -5.720614e-01 -4.156269e-01 +47 4.156269e-01 -5.720614e-01 -5.720614e-01 -4.156269e-01 +48 5.720614e-01 -4.156269e-01 -5.720614e-01 -4.156269e-01 +49 6.724985e-01 -2.185080e-01 -5.720614e-01 -4.156269e-01 +50 7.071068e-01 0 -5.720614e-01 -4.156269e-01 +51 6.724985e-01 2.185080e-01 -5.720614e-01 -4.156269e-01 +52 5.720614e-01 4.156269e-01 -5.720614e-01 -4.156269e-01 +53 4.156269e-01 5.720614e-01 -5.720614e-01 -4.156269e-01 +54 2.185080e-01 6.724985e-01 -5.720614e-01 -4.156269e-01 +55 4.329780e-17 7.071068e-01 -5.720614e-01 -4.156269e-01 +56 -2.185080e-01 6.724985e-01 -5.720614e-01 -4.156269e-01 +57 -4.156269e-01 5.720614e-01 -5.720614e-01 -4.156269e-01 +58 -5.720614e-01 4.156269e-01 -5.720614e-01 -4.156269e-01 +59 -6.724985e-01 2.185080e-01 -5.720614e-01 -4.156269e-01 +60 -7.071068e-01 -8.659561e-17 -4.156269e-01 -5.720614e-01 +61 -6.724985e-01 -2.185080e-01 -4.156269e-01 -5.720614e-01 +62 -5.720614e-01 -4.156269e-01 -4.156269e-01 -5.720614e-01 +63 -4.156269e-01 -5.720614e-01 -4.156269e-01 -5.720614e-01 +64 -2.185080e-01 -6.724985e-01 -4.156269e-01 -5.720614e-01 +65 4.329780e-17 -7.071068e-01 -4.156269e-01 -5.720614e-01 +66 2.185080e-01 -6.724985e-01 -4.156269e-01 -5.720614e-01 +67 4.156269e-01 -5.720614e-01 -4.156269e-01 -5.720614e-01 +68 5.720614e-01 -4.156269e-01 -4.156269e-01 -5.720614e-01 +69 6.724985e-01 -2.185080e-01 -4.156269e-01 -5.720614e-01 +70 7.071068e-01 0 -4.156269e-01 -5.720614e-01 +71 6.724985e-01 2.185080e-01 -4.156269e-01 -5.720614e-01 +72 5.720614e-01 4.156269e-01 -4.156269e-01 -5.720614e-01 +73 4.156269e-01 5.720614e-01 -4.156269e-01 -5.720614e-01 +74 2.185080e-01 6.724985e-01 -4.156269e-01 -5.720614e-01 +75 4.329780e-17 7.071068e-01 -4.156269e-01 -5.720614e-01 +76 -2.185080e-01 6.724985e-01 -4.156269e-01 -5.720614e-01 +77 -4.156269e-01 5.720614e-01 -4.156269e-01 -5.720614e-01 +78 -5.720614e-01 4.156269e-01 -4.156269e-01 -5.720614e-01 +79 -6.724985e-01 2.185080e-01 -4.156269e-01 -5.720614e-01 +80 -7.071068e-01 -8.659561e-17 -2.185080e-01 -6.724985e-01 +81 -6.724985e-01 -2.185080e-01 -2.185080e-01 -6.724985e-01 +82 -5.720614e-01 -4.156269e-01 -2.185080e-01 -6.724985e-01 +83 -4.156269e-01 -5.720614e-01 -2.185080e-01 -6.724985e-01 +84 -2.185080e-01 -6.724985e-01 -2.185080e-01 -6.724985e-01 +85 4.329780e-17 -7.071068e-01 -2.185080e-01 -6.724985e-01 +86 2.185080e-01 -6.724985e-01 -2.185080e-01 -6.724985e-01 +87 4.156269e-01 -5.720614e-01 -2.185080e-01 -6.724985e-01 +88 5.720614e-01 -4.156269e-01 -2.185080e-01 -6.724985e-01 +89 6.724985e-01 -2.185080e-01 -2.185080e-01 -6.724985e-01 +90 7.071068e-01 0 -2.185080e-01 -6.724985e-01 +91 6.724985e-01 2.185080e-01 -2.185080e-01 -6.724985e-01 +92 5.720614e-01 4.156269e-01 -2.185080e-01 -6.724985e-01 +93 4.156269e-01 5.720614e-01 -2.185080e-01 -6.724985e-01 +94 2.185080e-01 6.724985e-01 -2.185080e-01 -6.724985e-01 +95 4.329780e-17 7.071068e-01 -2.185080e-01 -6.724985e-01 +96 -2.185080e-01 6.724985e-01 -2.185080e-01 -6.724985e-01 +97 -4.156269e-01 5.720614e-01 -2.185080e-01 -6.724985e-01 +98 -5.720614e-01 4.156269e-01 -2.185080e-01 -6.724985e-01 +99 -6.724985e-01 2.185080e-01 -2.185080e-01 -6.724985e-01 +100 -7.071068e-01 -8.659561e-17 4.329780e-17 -7.071068e-01 +101 -6.724985e-01 -2.185080e-01 4.329780e-17 -7.071068e-01 +102 -5.720614e-01 -4.156269e-01 4.329780e-17 -7.071068e-01 +103 -4.156269e-01 -5.720614e-01 4.329780e-17 -7.071068e-01 +104 -2.185080e-01 -6.724985e-01 4.329780e-17 -7.071068e-01 +105 4.329780e-17 -7.071068e-01 4.329780e-17 -7.071068e-01 +106 2.185080e-01 -6.724985e-01 4.329780e-17 -7.071068e-01 +107 4.156269e-01 -5.720614e-01 4.329780e-17 -7.071068e-01 +108 5.720614e-01 -4.156269e-01 4.329780e-17 -7.071068e-01 +109 6.724985e-01 -2.185080e-01 4.329780e-17 -7.071068e-01 +110 7.071068e-01 0 4.329780e-17 -7.071068e-01 +111 6.724985e-01 2.185080e-01 4.329780e-17 -7.071068e-01 +112 5.720614e-01 4.156269e-01 4.329780e-17 -7.071068e-01 +113 4.156269e-01 5.720614e-01 4.329780e-17 -7.071068e-01 +114 2.185080e-01 6.724985e-01 4.329780e-17 -7.071068e-01 +115 4.329780e-17 7.071068e-01 4.329780e-17 -7.071068e-01 +116 -2.185080e-01 6.724985e-01 4.329780e-17 -7.071068e-01 +117 -4.156269e-01 5.720614e-01 4.329780e-17 -7.071068e-01 +118 -5.720614e-01 4.156269e-01 4.329780e-17 -7.071068e-01 +119 -6.724985e-01 2.185080e-01 4.329780e-17 -7.071068e-01 +120 -7.071068e-01 -8.659561e-17 2.185080e-01 -6.724985e-01 +121 -6.724985e-01 -2.185080e-01 2.185080e-01 -6.724985e-01 +122 -5.720614e-01 -4.156269e-01 2.185080e-01 -6.724985e-01 +123 -4.156269e-01 -5.720614e-01 2.185080e-01 -6.724985e-01 +124 -2.185080e-01 -6.724985e-01 2.185080e-01 -6.724985e-01 +125 4.329780e-17 -7.071068e-01 2.185080e-01 -6.724985e-01 +126 2.185080e-01 -6.724985e-01 2.185080e-01 -6.724985e-01 +127 4.156269e-01 -5.720614e-01 2.185080e-01 -6.724985e-01 +128 5.720614e-01 -4.156269e-01 2.185080e-01 -6.724985e-01 +129 6.724985e-01 -2.185080e-01 2.185080e-01 -6.724985e-01 +130 7.071068e-01 0 2.185080e-01 -6.724985e-01 +131 6.724985e-01 2.185080e-01 2.185080e-01 -6.724985e-01 +132 5.720614e-01 4.156269e-01 2.185080e-01 -6.724985e-01 +133 4.156269e-01 5.720614e-01 2.185080e-01 -6.724985e-01 +134 2.185080e-01 6.724985e-01 2.185080e-01 -6.724985e-01 +135 4.329780e-17 7.071068e-01 2.185080e-01 -6.724985e-01 +136 -2.185080e-01 6.724985e-01 2.185080e-01 -6.724985e-01 +137 -4.156269e-01 5.720614e-01 2.185080e-01 -6.724985e-01 +138 -5.720614e-01 4.156269e-01 2.185080e-01 -6.724985e-01 +139 -6.724985e-01 2.185080e-01 2.185080e-01 -6.724985e-01 +140 -7.071068e-01 -8.659561e-17 4.156269e-01 -5.720614e-01 +141 -6.724985e-01 -2.185080e-01 4.156269e-01 -5.720614e-01 +142 -5.720614e-01 -4.156269e-01 4.156269e-01 -5.720614e-01 +143 -4.156269e-01 -5.720614e-01 4.156269e-01 -5.720614e-01 +144 -2.185080e-01 -6.724985e-01 4.156269e-01 -5.720614e-01 +145 4.329780e-17 -7.071068e-01 4.156269e-01 -5.720614e-01 +146 2.185080e-01 -6.724985e-01 4.156269e-01 -5.720614e-01 +147 4.156269e-01 -5.720614e-01 4.156269e-01 -5.720614e-01 +148 5.720614e-01 -4.156269e-01 4.156269e-01 -5.720614e-01 +149 6.724985e-01 -2.185080e-01 4.156269e-01 -5.720614e-01 +150 7.071068e-01 0 4.156269e-01 -5.720614e-01 +151 6.724985e-01 2.185080e-01 4.156269e-01 -5.720614e-01 +152 5.720614e-01 4.156269e-01 4.156269e-01 -5.720614e-01 +153 4.156269e-01 5.720614e-01 4.156269e-01 -5.720614e-01 +154 2.185080e-01 6.724985e-01 4.156269e-01 -5.720614e-01 +155 4.329780e-17 7.071068e-01 4.156269e-01 -5.720614e-01 +156 -2.185080e-01 6.724985e-01 4.156269e-01 -5.720614e-01 +157 -4.156269e-01 5.720614e-01 4.156269e-01 -5.720614e-01 +158 -5.720614e-01 4.156269e-01 4.156269e-01 -5.720614e-01 +159 -6.724985e-01 2.185080e-01 4.156269e-01 -5.720614e-01 +160 -7.071068e-01 -8.659561e-17 5.720614e-01 -4.156269e-01 +161 -6.724985e-01 -2.185080e-01 5.720614e-01 -4.156269e-01 +162 -5.720614e-01 -4.156269e-01 5.720614e-01 -4.156269e-01 +163 -4.156269e-01 -5.720614e-01 5.720614e-01 -4.156269e-01 +164 -2.185080e-01 -6.724985e-01 5.720614e-01 -4.156269e-01 +165 4.329780e-17 -7.071068e-01 5.720614e-01 -4.156269e-01 +166 2.185080e-01 -6.724985e-01 5.720614e-01 -4.156269e-01 +167 4.156269e-01 -5.720614e-01 5.720614e-01 -4.156269e-01 +168 5.720614e-01 -4.156269e-01 5.720614e-01 -4.156269e-01 +169 6.724985e-01 -2.185080e-01 5.720614e-01 -4.156269e-01 +170 7.071068e-01 0 5.720614e-01 -4.156269e-01 +171 6.724985e-01 2.185080e-01 5.720614e-01 -4.156269e-01 +172 5.720614e-01 4.156269e-01 5.720614e-01 -4.156269e-01 +173 4.156269e-01 5.720614e-01 5.720614e-01 -4.156269e-01 +174 2.185080e-01 6.724985e-01 5.720614e-01 -4.156269e-01 +175 4.329780e-17 7.071068e-01 5.720614e-01 -4.156269e-01 +176 -2.185080e-01 6.724985e-01 5.720614e-01 -4.156269e-01 +177 -4.156269e-01 5.720614e-01 5.720614e-01 -4.156269e-01 +178 -5.720614e-01 4.156269e-01 5.720614e-01 -4.156269e-01 +179 -6.724985e-01 2.185080e-01 5.720614e-01 -4.156269e-01 +180 -7.071068e-01 -8.659561e-17 6.724985e-01 -2.185080e-01 +181 -6.724985e-01 -2.185080e-01 6.724985e-01 -2.185080e-01 +182 -5.720614e-01 -4.156269e-01 6.724985e-01 -2.185080e-01 +183 -4.156269e-01 -5.720614e-01 6.724985e-01 -2.185080e-01 +184 -2.185080e-01 -6.724985e-01 6.724985e-01 -2.185080e-01 +185 4.329780e-17 -7.071068e-01 6.724985e-01 -2.185080e-01 +186 2.185080e-01 -6.724985e-01 6.724985e-01 -2.185080e-01 +187 4.156269e-01 -5.720614e-01 6.724985e-01 -2.185080e-01 +188 5.720614e-01 -4.156269e-01 6.724985e-01 -2.185080e-01 +189 6.724985e-01 -2.185080e-01 6.724985e-01 -2.185080e-01 +190 7.071068e-01 0 6.724985e-01 -2.185080e-01 +191 6.724985e-01 2.185080e-01 6.724985e-01 -2.185080e-01 +192 5.720614e-01 4.156269e-01 6.724985e-01 -2.185080e-01 +193 4.156269e-01 5.720614e-01 6.724985e-01 -2.185080e-01 +194 2.185080e-01 6.724985e-01 6.724985e-01 -2.185080e-01 +195 4.329780e-17 7.071068e-01 6.724985e-01 -2.185080e-01 +196 -2.185080e-01 6.724985e-01 6.724985e-01 -2.185080e-01 +197 -4.156269e-01 5.720614e-01 6.724985e-01 -2.185080e-01 +198 -5.720614e-01 4.156269e-01 6.724985e-01 -2.185080e-01 +199 -6.724985e-01 2.185080e-01 6.724985e-01 -2.185080e-01 +200 -7.071068e-01 -8.659561e-17 7.071068e-01 0 +201 -6.724985e-01 -2.185080e-01 7.071068e-01 0 +202 -5.720614e-01 -4.156269e-01 7.071068e-01 0 +203 -4.156269e-01 -5.720614e-01 7.071068e-01 0 +204 -2.185080e-01 -6.724985e-01 7.071068e-01 0 +205 4.329780e-17 -7.071068e-01 7.071068e-01 0 +206 2.185080e-01 -6.724985e-01 7.071068e-01 0 +207 4.156269e-01 -5.720614e-01 7.071068e-01 0 +208 5.720614e-01 -4.156269e-01 7.071068e-01 0 +209 6.724985e-01 -2.185080e-01 7.071068e-01 0 +210 7.071068e-01 0 7.071068e-01 0 +211 6.724985e-01 2.185080e-01 7.071068e-01 0 +212 5.720614e-01 4.156269e-01 7.071068e-01 0 +213 4.156269e-01 5.720614e-01 7.071068e-01 0 +214 2.185080e-01 6.724985e-01 7.071068e-01 0 +215 4.329780e-17 7.071068e-01 7.071068e-01 0 +216 -2.185080e-01 6.724985e-01 7.071068e-01 0 +217 -4.156269e-01 5.720614e-01 7.071068e-01 0 +218 -5.720614e-01 4.156269e-01 7.071068e-01 0 +219 -6.724985e-01 2.185080e-01 7.071068e-01 0 +220 -7.071068e-01 -8.659561e-17 6.724985e-01 2.185080e-01 +221 -6.724985e-01 -2.185080e-01 6.724985e-01 2.185080e-01 +222 -5.720614e-01 -4.156269e-01 6.724985e-01 2.185080e-01 +223 -4.156269e-01 -5.720614e-01 6.724985e-01 2.185080e-01 +224 -2.185080e-01 -6.724985e-01 6.724985e-01 2.185080e-01 +225 4.329780e-17 -7.071068e-01 6.724985e-01 2.185080e-01 +226 2.185080e-01 -6.724985e-01 6.724985e-01 2.185080e-01 +227 4.156269e-01 -5.720614e-01 6.724985e-01 2.185080e-01 +228 5.720614e-01 -4.156269e-01 6.724985e-01 2.185080e-01 +229 6.724985e-01 -2.185080e-01 6.724985e-01 2.185080e-01 +230 7.071068e-01 0 6.724985e-01 2.185080e-01 +231 6.724985e-01 2.185080e-01 6.724985e-01 2.185080e-01 +232 5.720614e-01 4.156269e-01 6.724985e-01 2.185080e-01 +233 4.156269e-01 5.720614e-01 6.724985e-01 2.185080e-01 +234 2.185080e-01 6.724985e-01 6.724985e-01 2.185080e-01 +235 4.329780e-17 7.071068e-01 6.724985e-01 2.185080e-01 +236 -2.185080e-01 6.724985e-01 6.724985e-01 2.185080e-01 +237 -4.156269e-01 5.720614e-01 6.724985e-01 2.185080e-01 +238 -5.720614e-01 4.156269e-01 6.724985e-01 2.185080e-01 +239 -6.724985e-01 2.185080e-01 6.724985e-01 2.185080e-01 +240 -7.071068e-01 -8.659561e-17 5.720614e-01 4.156269e-01 +241 -6.724985e-01 -2.185080e-01 5.720614e-01 4.156269e-01 +242 -5.720614e-01 -4.156269e-01 5.720614e-01 4.156269e-01 +243 -4.156269e-01 -5.720614e-01 5.720614e-01 4.156269e-01 +244 -2.185080e-01 -6.724985e-01 5.720614e-01 4.156269e-01 +245 4.329780e-17 -7.071068e-01 5.720614e-01 4.156269e-01 +246 2.185080e-01 -6.724985e-01 5.720614e-01 4.156269e-01 +247 4.156269e-01 -5.720614e-01 5.720614e-01 4.156269e-01 +248 5.720614e-01 -4.156269e-01 5.720614e-01 4.156269e-01 +249 6.724985e-01 -2.185080e-01 5.720614e-01 4.156269e-01 +250 7.071068e-01 0 5.720614e-01 4.156269e-01 +251 6.724985e-01 2.185080e-01 5.720614e-01 4.156269e-01 +252 5.720614e-01 4.156269e-01 5.720614e-01 4.156269e-01 +253 4.156269e-01 5.720614e-01 5.720614e-01 4.156269e-01 +254 2.185080e-01 6.724985e-01 5.720614e-01 4.156269e-01 +255 4.329780e-17 7.071068e-01 5.720614e-01 4.156269e-01 +256 -2.185080e-01 6.724985e-01 5.720614e-01 4.156269e-01 +257 -4.156269e-01 5.720614e-01 5.720614e-01 4.156269e-01 +258 -5.720614e-01 4.156269e-01 5.720614e-01 4.156269e-01 +259 -6.724985e-01 2.185080e-01 5.720614e-01 4.156269e-01 +260 -7.071068e-01 -8.659561e-17 4.156269e-01 5.720614e-01 +261 -6.724985e-01 -2.185080e-01 4.156269e-01 5.720614e-01 +262 -5.720614e-01 -4.156269e-01 4.156269e-01 5.720614e-01 +263 -4.156269e-01 -5.720614e-01 4.156269e-01 5.720614e-01 +264 -2.185080e-01 -6.724985e-01 4.156269e-01 5.720614e-01 +265 4.329780e-17 -7.071068e-01 4.156269e-01 5.720614e-01 +266 2.185080e-01 -6.724985e-01 4.156269e-01 5.720614e-01 +267 4.156269e-01 -5.720614e-01 4.156269e-01 5.720614e-01 +268 5.720614e-01 -4.156269e-01 4.156269e-01 5.720614e-01 +269 6.724985e-01 -2.185080e-01 4.156269e-01 5.720614e-01 +270 7.071068e-01 0 4.156269e-01 5.720614e-01 +271 6.724985e-01 2.185080e-01 4.156269e-01 5.720614e-01 +272 5.720614e-01 4.156269e-01 4.156269e-01 5.720614e-01 +273 4.156269e-01 5.720614e-01 4.156269e-01 5.720614e-01 +274 2.185080e-01 6.724985e-01 4.156269e-01 5.720614e-01 +275 4.329780e-17 7.071068e-01 4.156269e-01 5.720614e-01 +276 -2.185080e-01 6.724985e-01 4.156269e-01 5.720614e-01 +277 -4.156269e-01 5.720614e-01 4.156269e-01 5.720614e-01 +278 -5.720614e-01 4.156269e-01 4.156269e-01 5.720614e-01 +279 -6.724985e-01 2.185080e-01 4.156269e-01 5.720614e-01 +280 -7.071068e-01 -8.659561e-17 2.185080e-01 6.724985e-01 +281 -6.724985e-01 -2.185080e-01 2.185080e-01 6.724985e-01 +282 -5.720614e-01 -4.156269e-01 2.185080e-01 6.724985e-01 +283 -4.156269e-01 -5.720614e-01 2.185080e-01 6.724985e-01 +284 -2.185080e-01 -6.724985e-01 2.185080e-01 6.724985e-01 +285 4.329780e-17 -7.071068e-01 2.185080e-01 6.724985e-01 +286 2.185080e-01 -6.724985e-01 2.185080e-01 6.724985e-01 +287 4.156269e-01 -5.720614e-01 2.185080e-01 6.724985e-01 +288 5.720614e-01 -4.156269e-01 2.185080e-01 6.724985e-01 +289 6.724985e-01 -2.185080e-01 2.185080e-01 6.724985e-01 +290 7.071068e-01 0 2.185080e-01 6.724985e-01 +291 6.724985e-01 2.185080e-01 2.185080e-01 6.724985e-01 +292 5.720614e-01 4.156269e-01 2.185080e-01 6.724985e-01 +293 4.156269e-01 5.720614e-01 2.185080e-01 6.724985e-01 +294 2.185080e-01 6.724985e-01 2.185080e-01 6.724985e-01 +295 4.329780e-17 7.071068e-01 2.185080e-01 6.724985e-01 +296 -2.185080e-01 6.724985e-01 2.185080e-01 6.724985e-01 +297 -4.156269e-01 5.720614e-01 2.185080e-01 6.724985e-01 +298 -5.720614e-01 4.156269e-01 2.185080e-01 6.724985e-01 +299 -6.724985e-01 2.185080e-01 2.185080e-01 6.724985e-01 +300 -7.071068e-01 -8.659561e-17 4.329780e-17 7.071068e-01 +301 -6.724985e-01 -2.185080e-01 4.329780e-17 7.071068e-01 +302 -5.720614e-01 -4.156269e-01 4.329780e-17 7.071068e-01 +303 -4.156269e-01 -5.720614e-01 4.329780e-17 7.071068e-01 +304 -2.185080e-01 -6.724985e-01 4.329780e-17 7.071068e-01 +305 4.329780e-17 -7.071068e-01 4.329780e-17 7.071068e-01 +306 2.185080e-01 -6.724985e-01 4.329780e-17 7.071068e-01 +307 4.156269e-01 -5.720614e-01 4.329780e-17 7.071068e-01 +308 5.720614e-01 -4.156269e-01 4.329780e-17 7.071068e-01 +309 6.724985e-01 -2.185080e-01 4.329780e-17 7.071068e-01 +310 7.071068e-01 0 4.329780e-17 7.071068e-01 +311 6.724985e-01 2.185080e-01 4.329780e-17 7.071068e-01 +312 5.720614e-01 4.156269e-01 4.329780e-17 7.071068e-01 +313 4.156269e-01 5.720614e-01 4.329780e-17 7.071068e-01 +314 2.185080e-01 6.724985e-01 4.329780e-17 7.071068e-01 +315 4.329780e-17 7.071068e-01 4.329780e-17 7.071068e-01 +316 -2.185080e-01 6.724985e-01 4.329780e-17 7.071068e-01 +317 -4.156269e-01 5.720614e-01 4.329780e-17 7.071068e-01 +318 -5.720614e-01 4.156269e-01 4.329780e-17 7.071068e-01 +319 -6.724985e-01 2.185080e-01 4.329780e-17 7.071068e-01 +320 -7.071068e-01 -8.659561e-17 -2.185080e-01 6.724985e-01 +321 -6.724985e-01 -2.185080e-01 -2.185080e-01 6.724985e-01 +322 -5.720614e-01 -4.156269e-01 -2.185080e-01 6.724985e-01 +323 -4.156269e-01 -5.720614e-01 -2.185080e-01 6.724985e-01 +324 -2.185080e-01 -6.724985e-01 -2.185080e-01 6.724985e-01 +325 4.329780e-17 -7.071068e-01 -2.185080e-01 6.724985e-01 +326 2.185080e-01 -6.724985e-01 -2.185080e-01 6.724985e-01 +327 4.156269e-01 -5.720614e-01 -2.185080e-01 6.724985e-01 +328 5.720614e-01 -4.156269e-01 -2.185080e-01 6.724985e-01 +329 6.724985e-01 -2.185080e-01 -2.185080e-01 6.724985e-01 +330 7.071068e-01 0 -2.185080e-01 6.724985e-01 +331 6.724985e-01 2.185080e-01 -2.185080e-01 6.724985e-01 +332 5.720614e-01 4.156269e-01 -2.185080e-01 6.724985e-01 +333 4.156269e-01 5.720614e-01 -2.185080e-01 6.724985e-01 +334 2.185080e-01 6.724985e-01 -2.185080e-01 6.724985e-01 +335 4.329780e-17 7.071068e-01 -2.185080e-01 6.724985e-01 +336 -2.185080e-01 6.724985e-01 -2.185080e-01 6.724985e-01 +337 -4.156269e-01 5.720614e-01 -2.185080e-01 6.724985e-01 +338 -5.720614e-01 4.156269e-01 -2.185080e-01 6.724985e-01 +339 -6.724985e-01 2.185080e-01 -2.185080e-01 6.724985e-01 +340 -7.071068e-01 -8.659561e-17 -4.156269e-01 5.720614e-01 +341 -6.724985e-01 -2.185080e-01 -4.156269e-01 5.720614e-01 +342 -5.720614e-01 -4.156269e-01 -4.156269e-01 5.720614e-01 +343 -4.156269e-01 -5.720614e-01 -4.156269e-01 5.720614e-01 +344 -2.185080e-01 -6.724985e-01 -4.156269e-01 5.720614e-01 +345 4.329780e-17 -7.071068e-01 -4.156269e-01 5.720614e-01 +346 2.185080e-01 -6.724985e-01 -4.156269e-01 5.720614e-01 +347 4.156269e-01 -5.720614e-01 -4.156269e-01 5.720614e-01 +348 5.720614e-01 -4.156269e-01 -4.156269e-01 5.720614e-01 +349 6.724985e-01 -2.185080e-01 -4.156269e-01 5.720614e-01 +350 7.071068e-01 0 -4.156269e-01 5.720614e-01 +351 6.724985e-01 2.185080e-01 -4.156269e-01 5.720614e-01 +352 5.720614e-01 4.156269e-01 -4.156269e-01 5.720614e-01 +353 4.156269e-01 5.720614e-01 -4.156269e-01 5.720614e-01 +354 2.185080e-01 6.724985e-01 -4.156269e-01 5.720614e-01 +355 4.329780e-17 7.071068e-01 -4.156269e-01 5.720614e-01 +356 -2.185080e-01 6.724985e-01 -4.156269e-01 5.720614e-01 +357 -4.156269e-01 5.720614e-01 -4.156269e-01 5.720614e-01 +358 -5.720614e-01 4.156269e-01 -4.156269e-01 5.720614e-01 +359 -6.724985e-01 2.185080e-01 -4.156269e-01 5.720614e-01 +360 -7.071068e-01 -8.659561e-17 -5.720614e-01 4.156269e-01 +361 -6.724985e-01 -2.185080e-01 -5.720614e-01 4.156269e-01 +362 -5.720614e-01 -4.156269e-01 -5.720614e-01 4.156269e-01 +363 -4.156269e-01 -5.720614e-01 -5.720614e-01 4.156269e-01 +364 -2.185080e-01 -6.724985e-01 -5.720614e-01 4.156269e-01 +365 4.329780e-17 -7.071068e-01 -5.720614e-01 4.156269e-01 +366 2.185080e-01 -6.724985e-01 -5.720614e-01 4.156269e-01 +367 4.156269e-01 -5.720614e-01 -5.720614e-01 4.156269e-01 +368 5.720614e-01 -4.156269e-01 -5.720614e-01 4.156269e-01 +369 6.724985e-01 -2.185080e-01 -5.720614e-01 4.156269e-01 +370 7.071068e-01 0 -5.720614e-01 4.156269e-01 +371 6.724985e-01 2.185080e-01 -5.720614e-01 4.156269e-01 +372 5.720614e-01 4.156269e-01 -5.720614e-01 4.156269e-01 +373 4.156269e-01 5.720614e-01 -5.720614e-01 4.156269e-01 +374 2.185080e-01 6.724985e-01 -5.720614e-01 4.156269e-01 +375 4.329780e-17 7.071068e-01 -5.720614e-01 4.156269e-01 +376 -2.185080e-01 6.724985e-01 -5.720614e-01 4.156269e-01 +377 -4.156269e-01 5.720614e-01 -5.720614e-01 4.156269e-01 +378 -5.720614e-01 4.156269e-01 -5.720614e-01 4.156269e-01 +379 -6.724985e-01 2.185080e-01 -5.720614e-01 4.156269e-01 +380 -7.071068e-01 -8.659561e-17 -6.724985e-01 2.185080e-01 +381 -6.724985e-01 -2.185080e-01 -6.724985e-01 2.185080e-01 +382 -5.720614e-01 -4.156269e-01 -6.724985e-01 2.185080e-01 +383 -4.156269e-01 -5.720614e-01 -6.724985e-01 2.185080e-01 +384 -2.185080e-01 -6.724985e-01 -6.724985e-01 2.185080e-01 +385 4.329780e-17 -7.071068e-01 -6.724985e-01 2.185080e-01 +386 2.185080e-01 -6.724985e-01 -6.724985e-01 2.185080e-01 +387 4.156269e-01 -5.720614e-01 -6.724985e-01 2.185080e-01 +388 5.720614e-01 -4.156269e-01 -6.724985e-01 2.185080e-01 +389 6.724985e-01 -2.185080e-01 -6.724985e-01 2.185080e-01 +390 7.071068e-01 0 -6.724985e-01 2.185080e-01 +391 6.724985e-01 2.185080e-01 -6.724985e-01 2.185080e-01 +392 5.720614e-01 4.156269e-01 -6.724985e-01 2.185080e-01 +393 4.156269e-01 5.720614e-01 -6.724985e-01 2.185080e-01 +394 2.185080e-01 6.724985e-01 -6.724985e-01 2.185080e-01 +395 4.329780e-17 7.071068e-01 -6.724985e-01 2.185080e-01 +396 -2.185080e-01 6.724985e-01 -6.724985e-01 2.185080e-01 +397 -4.156269e-01 5.720614e-01 -6.724985e-01 2.185080e-01 +398 -5.720614e-01 4.156269e-01 -6.724985e-01 2.185080e-01 +399 -6.724985e-01 2.185080e-01 -6.724985e-01 2.185080e-01 diff --git a/HDVF/test/HDVF/data/dim_4/torus4D.simp b/HDVF/test/HDVF/data/dim_4/torus4D.simp new file mode 100644 index 00000000000..b082a68c60b --- /dev/null +++ b/HDVF/test/HDVF/data/dim_4/torus4D.simp @@ -0,0 +1,800 @@ +0 1 21 +0 21 20 +1 2 22 +1 22 21 +2 3 23 +2 23 22 +3 4 24 +3 24 23 +4 5 25 +4 25 24 +5 6 26 +5 26 25 +6 7 27 +6 27 26 +7 8 28 +7 28 27 +8 9 29 +8 29 28 +9 10 30 +9 30 29 +10 11 31 +10 31 30 +11 12 32 +11 32 31 +12 13 33 +12 33 32 +13 14 34 +13 34 33 +14 15 35 +14 35 34 +15 16 36 +15 36 35 +16 17 37 +16 37 36 +17 18 38 +17 38 37 +18 19 39 +18 39 38 +19 0 20 +19 20 39 +20 21 41 +20 41 40 +21 22 42 +21 42 41 +22 23 43 +22 43 42 +23 24 44 +23 44 43 +24 25 45 +24 45 44 +25 26 46 +25 46 45 +26 27 47 +26 47 46 +27 28 48 +27 48 47 +28 29 49 +28 49 48 +29 30 50 +29 50 49 +30 31 51 +30 51 50 +31 32 52 +31 52 51 +32 33 53 +32 53 52 +33 34 54 +33 54 53 +34 35 55 +34 55 54 +35 36 56 +35 56 55 +36 37 57 +36 57 56 +37 38 58 +37 58 57 +38 39 59 +38 59 58 +39 20 40 +39 40 59 +40 41 61 +40 61 60 +41 42 62 +41 62 61 +42 43 63 +42 63 62 +43 44 64 +43 64 63 +44 45 65 +44 65 64 +45 46 66 +45 66 65 +46 47 67 +46 67 66 +47 48 68 +47 68 67 +48 49 69 +48 69 68 +49 50 70 +49 70 69 +50 51 71 +50 71 70 +51 52 72 +51 72 71 +52 53 73 +52 73 72 +53 54 74 +53 74 73 +54 55 75 +54 75 74 +55 56 76 +55 76 75 +56 57 77 +56 77 76 +57 58 78 +57 78 77 +58 59 79 +58 79 78 +59 40 60 +59 60 79 +60 61 81 +60 81 80 +61 62 82 +61 82 81 +62 63 83 +62 83 82 +63 64 84 +63 84 83 +64 65 85 +64 85 84 +65 66 86 +65 86 85 +66 67 87 +66 87 86 +67 68 88 +67 88 87 +68 69 89 +68 89 88 +69 70 90 +69 90 89 +70 71 91 +70 91 90 +71 72 92 +71 92 91 +72 73 93 +72 93 92 +73 74 94 +73 94 93 +74 75 95 +74 95 94 +75 76 96 +75 96 95 +76 77 97 +76 97 96 +77 78 98 +77 98 97 +78 79 99 +78 99 98 +79 60 80 +79 80 99 +80 81 101 +80 101 100 +81 82 102 +81 102 101 +82 83 103 +82 103 102 +83 84 104 +83 104 103 +84 85 105 +84 105 104 +85 86 106 +85 106 105 +86 87 107 +86 107 106 +87 88 108 +87 108 107 +88 89 109 +88 109 108 +89 90 110 +89 110 109 +90 91 111 +90 111 110 +91 92 112 +91 112 111 +92 93 113 +92 113 112 +93 94 114 +93 114 113 +94 95 115 +94 115 114 +95 96 116 +95 116 115 +96 97 117 +96 117 116 +97 98 118 +97 118 117 +98 99 119 +98 119 118 +99 80 100 +99 100 119 +100 101 121 +100 121 120 +101 102 122 +101 122 121 +102 103 123 +102 123 122 +103 104 124 +103 124 123 +104 105 125 +104 125 124 +105 106 126 +105 126 125 +106 107 127 +106 127 126 +107 108 128 +107 128 127 +108 109 129 +108 129 128 +109 110 130 +109 130 129 +110 111 131 +110 131 130 +111 112 132 +111 132 131 +112 113 133 +112 133 132 +113 114 134 +113 134 133 +114 115 135 +114 135 134 +115 116 136 +115 136 135 +116 117 137 +116 137 136 +117 118 138 +117 138 137 +118 119 139 +118 139 138 +119 100 120 +119 120 139 +120 121 141 +120 141 140 +121 122 142 +121 142 141 +122 123 143 +122 143 142 +123 124 144 +123 144 143 +124 125 145 +124 145 144 +125 126 146 +125 146 145 +126 127 147 +126 147 146 +127 128 148 +127 148 147 +128 129 149 +128 149 148 +129 130 150 +129 150 149 +130 131 151 +130 151 150 +131 132 152 +131 152 151 +132 133 153 +132 153 152 +133 134 154 +133 154 153 +134 135 155 +134 155 154 +135 136 156 +135 156 155 +136 137 157 +136 157 156 +137 138 158 +137 158 157 +138 139 159 +138 159 158 +139 120 140 +139 140 159 +140 141 161 +140 161 160 +141 142 162 +141 162 161 +142 143 163 +142 163 162 +143 144 164 +143 164 163 +144 145 165 +144 165 164 +145 146 166 +145 166 165 +146 147 167 +146 167 166 +147 148 168 +147 168 167 +148 149 169 +148 169 168 +149 150 170 +149 170 169 +150 151 171 +150 171 170 +151 152 172 +151 172 171 +152 153 173 +152 173 172 +153 154 174 +153 174 173 +154 155 175 +154 175 174 +155 156 176 +155 176 175 +156 157 177 +156 177 176 +157 158 178 +157 178 177 +158 159 179 +158 179 178 +159 140 160 +159 160 179 +160 161 181 +160 181 180 +161 162 182 +161 182 181 +162 163 183 +162 183 182 +163 164 184 +163 184 183 +164 165 185 +164 185 184 +165 166 186 +165 186 185 +166 167 187 +166 187 186 +167 168 188 +167 188 187 +168 169 189 +168 189 188 +169 170 190 +169 190 189 +170 171 191 +170 191 190 +171 172 192 +171 192 191 +172 173 193 +172 193 192 +173 174 194 +173 194 193 +174 175 195 +174 195 194 +175 176 196 +175 196 195 +176 177 197 +176 197 196 +177 178 198 +177 198 197 +178 179 199 +178 199 198 +179 160 180 +179 180 199 +180 181 201 +180 201 200 +181 182 202 +181 202 201 +182 183 203 +182 203 202 +183 184 204 +183 204 203 +184 185 205 +184 205 204 +185 186 206 +185 206 205 +186 187 207 +186 207 206 +187 188 208 +187 208 207 +188 189 209 +188 209 208 +189 190 210 +189 210 209 +190 191 211 +190 211 210 +191 192 212 +191 212 211 +192 193 213 +192 213 212 +193 194 214 +193 214 213 +194 195 215 +194 215 214 +195 196 216 +195 216 215 +196 197 217 +196 217 216 +197 198 218 +197 218 217 +198 199 219 +198 219 218 +199 180 200 +199 200 219 +200 201 221 +200 221 220 +201 202 222 +201 222 221 +202 203 223 +202 223 222 +203 204 224 +203 224 223 +204 205 225 +204 225 224 +205 206 226 +205 226 225 +206 207 227 +206 227 226 +207 208 228 +207 228 227 +208 209 229 +208 229 228 +209 210 230 +209 230 229 +210 211 231 +210 231 230 +211 212 232 +211 232 231 +212 213 233 +212 233 232 +213 214 234 +213 234 233 +214 215 235 +214 235 234 +215 216 236 +215 236 235 +216 217 237 +216 237 236 +217 218 238 +217 238 237 +218 219 239 +218 239 238 +219 200 220 +219 220 239 +220 221 241 +220 241 240 +221 222 242 +221 242 241 +222 223 243 +222 243 242 +223 224 244 +223 244 243 +224 225 245 +224 245 244 +225 226 246 +225 246 245 +226 227 247 +226 247 246 +227 228 248 +227 248 247 +228 229 249 +228 249 248 +229 230 250 +229 250 249 +230 231 251 +230 251 250 +231 232 252 +231 252 251 +232 233 253 +232 253 252 +233 234 254 +233 254 253 +234 235 255 +234 255 254 +235 236 256 +235 256 255 +236 237 257 +236 257 256 +237 238 258 +237 258 257 +238 239 259 +238 259 258 +239 220 240 +239 240 259 +240 241 261 +240 261 260 +241 242 262 +241 262 261 +242 243 263 +242 263 262 +243 244 264 +243 264 263 +244 245 265 +244 265 264 +245 246 266 +245 266 265 +246 247 267 +246 267 266 +247 248 268 +247 268 267 +248 249 269 +248 269 268 +249 250 270 +249 270 269 +250 251 271 +250 271 270 +251 252 272 +251 272 271 +252 253 273 +252 273 272 +253 254 274 +253 274 273 +254 255 275 +254 275 274 +255 256 276 +255 276 275 +256 257 277 +256 277 276 +257 258 278 +257 278 277 +258 259 279 +258 279 278 +259 240 260 +259 260 279 +260 261 281 +260 281 280 +261 262 282 +261 282 281 +262 263 283 +262 283 282 +263 264 284 +263 284 283 +264 265 285 +264 285 284 +265 266 286 +265 286 285 +266 267 287 +266 287 286 +267 268 288 +267 288 287 +268 269 289 +268 289 288 +269 270 290 +269 290 289 +270 271 291 +270 291 290 +271 272 292 +271 292 291 +272 273 293 +272 293 292 +273 274 294 +273 294 293 +274 275 295 +274 295 294 +275 276 296 +275 296 295 +276 277 297 +276 297 296 +277 278 298 +277 298 297 +278 279 299 +278 299 298 +279 260 280 +279 280 299 +280 281 301 +280 301 300 +281 282 302 +281 302 301 +282 283 303 +282 303 302 +283 284 304 +283 304 303 +284 285 305 +284 305 304 +285 286 306 +285 306 305 +286 287 307 +286 307 306 +287 288 308 +287 308 307 +288 289 309 +288 309 308 +289 290 310 +289 310 309 +290 291 311 +290 311 310 +291 292 312 +291 312 311 +292 293 313 +292 313 312 +293 294 314 +293 314 313 +294 295 315 +294 315 314 +295 296 316 +295 316 315 +296 297 317 +296 317 316 +297 298 318 +297 318 317 +298 299 319 +298 319 318 +299 280 300 +299 300 319 +300 301 321 +300 321 320 +301 302 322 +301 322 321 +302 303 323 +302 323 322 +303 304 324 +303 324 323 +304 305 325 +304 325 324 +305 306 326 +305 326 325 +306 307 327 +306 327 326 +307 308 328 +307 328 327 +308 309 329 +308 329 328 +309 310 330 +309 330 329 +310 311 331 +310 331 330 +311 312 332 +311 332 331 +312 313 333 +312 333 332 +313 314 334 +313 334 333 +314 315 335 +314 335 334 +315 316 336 +315 336 335 +316 317 337 +316 337 336 +317 318 338 +317 338 337 +318 319 339 +318 339 338 +319 300 320 +319 320 339 +320 321 341 +320 341 340 +321 322 342 +321 342 341 +322 323 343 +322 343 342 +323 324 344 +323 344 343 +324 325 345 +324 345 344 +325 326 346 +325 346 345 +326 327 347 +326 347 346 +327 328 348 +327 348 347 +328 329 349 +328 349 348 +329 330 350 +329 350 349 +330 331 351 +330 351 350 +331 332 352 +331 352 351 +332 333 353 +332 353 352 +333 334 354 +333 354 353 +334 335 355 +334 355 354 +335 336 356 +335 356 355 +336 337 357 +336 357 356 +337 338 358 +337 358 357 +338 339 359 +338 359 358 +339 320 340 +339 340 359 +340 341 361 +340 361 360 +341 342 362 +341 362 361 +342 343 363 +342 363 362 +343 344 364 +343 364 363 +344 345 365 +344 365 364 +345 346 366 +345 366 365 +346 347 367 +346 367 366 +347 348 368 +347 368 367 +348 349 369 +348 369 368 +349 350 370 +349 370 369 +350 351 371 +350 371 370 +351 352 372 +351 372 371 +352 353 373 +352 373 372 +353 354 374 +353 374 373 +354 355 375 +354 375 374 +355 356 376 +355 376 375 +356 357 377 +356 377 376 +357 358 378 +357 378 377 +358 359 379 +358 379 378 +359 340 360 +359 360 379 +360 361 381 +360 381 380 +361 362 382 +361 382 381 +362 363 383 +362 383 382 +363 364 384 +363 384 383 +364 365 385 +364 385 384 +365 366 386 +365 386 385 +366 367 387 +366 387 386 +367 368 388 +367 388 387 +368 369 389 +368 389 388 +369 370 390 +369 390 389 +370 371 391 +370 391 390 +371 372 392 +371 392 391 +372 373 393 +372 393 392 +373 374 394 +373 394 393 +374 375 395 +374 395 394 +375 376 396 +375 396 395 +376 377 397 +376 397 396 +377 378 398 +377 398 397 +378 379 399 +378 399 398 +379 360 380 +379 380 399 +380 381 1 +380 1 0 +381 382 2 +381 2 1 +382 383 3 +382 3 2 +383 384 4 +383 4 3 +384 385 5 +384 5 4 +385 386 6 +385 6 5 +386 387 7 +386 7 6 +387 388 8 +387 8 7 +388 389 9 +388 9 8 +389 390 10 +389 10 9 +390 391 11 +390 11 10 +391 392 12 +391 12 11 +392 393 13 +392 13 12 +393 394 14 +393 14 13 +394 395 15 +394 15 14 +395 396 16 +395 16 15 +396 397 17 +396 17 16 +397 398 18 +397 18 17 +398 399 19 +398 19 18 +399 380 0 +399 0 19 diff --git a/HDVF/test/HDVF/data/mesh_data/two_rings.off b/HDVF/test/HDVF/data/mesh_data/two_rings.off new file mode 100644 index 00000000000..e70d2fb826e --- /dev/null +++ b/HDVF/test/HDVF/data/mesh_data/two_rings.off @@ -0,0 +1,302 @@ +OFF +100 200 0 +0.8068087 -0.1369752 0.1088872 +1.186916 0.0476405 -0.4613007 +1.051366 -0.1854656 -0.5673844 +0.9948984 0.1579157 -0.7269516 +0.5129524 -0.251109 -0.8064808 +0.7331458 0.2345412 -0.4945451 +0.3925214 -0.06845951 -0.653622 +0.599517 -0.1380482 -1.08827 +-0.05248506 -0.08105646 -0.7707638 +-0.1717129 -0.2492442 -0.9894599 +0.06431784 -0.08102705 -1.273498 +0.342606 0.1952076 -0.7908352 +0.4737212 0.2353228 -1.057414 +-0.3065881 0.2646746 -1.050759 +-0.3149677 -0.1966568 -0.8047859 +-0.7216832 0.2285586 -0.9096752 +-0.7562213 0.1821214 -0.3941869 +-0.3245012 0.06950673 -0.6960132 +-0.5211146 -0.1306099 -1.143401 +-0.9871557 -0.2388103 -0.5045 +-0.8831414 -0.2120184 -0.2488802 +-0.7858014 -0.06563223 -0.1045682 +-1.12535 -5.563291e-05 -0.6367278 +-1.263521 -0.008431711 -0.2632576 +-1.038682 0.2565115 -0.3126053 +-1.103953 -0.2453156 0.1328934 +-1.199863 -0.05664325 0.3913677 +-0.6667085 0.2639074 0.8171263 +-0.8712847 -0.2386155 0.7418178 +-0.6877777 0.07976526 0.3537838 +-0.6251712 -0.185322 0.5562618 +-1.023117 0.1342362 0.730703 +-0.5567074 -0.02264468 1.147629 +0.2600124 0.2476594 1.008208 +-0.005709359 -0.2740047 1.000951 +0.005354809 -0.0652919 1.255826 +0.2206438 0.1311773 1.246801 +0.2922972 -0.1298945 0.7383755 +-0.1203823 0.1054483 0.7899052 +0.6471182 -0.1618921 1.039029 +0.8357504 0.187364 0.8641226 +0.80875 -0.2490388 0.7171871 +0.7823561 0.1290996 0.2588825 +1.085175 -0.07320409 0.6466006 +1.218166 0.08923589 0.2922309 +1.055843 0.2462087 0.3810805 +1.058048 -0.258006 0.04496348 +1.255079 -0.06664666 0.0920891 +2.285683 -0.4845517 0.2109279 +2.024707 0.002134202 0.1197436 +2.098774 0.4314888 -0.28725 +2.375252 0.3278373 -0.1779933 +2.057273 -0.1226462 -0.1901059 +2.263072 0.5375854 0.1081119 +1.660319 0.5349787 -0.1904282 +1.884569 0.8514283 -0.2164229 +1.704678 0.5737287 0.1625481 +1.869542 0.9141445 0.09123326 +1.86707 0.6637395 0.2127623 +1.331254 1.106612 -0.08867647 +1.487294 1.006629 0.1417312 +1.188846 0.6773928 0.1511322 +1.302552 0.819414 -0.2878849 +1.441208 0.5777399 -0.02308238 +0.8907672 0.500017 -0.03497452 +0.9231856 1.043054 0.09019117 +0.8869826 0.8995345 0.2073351 +0.9753086 1.043986 -0.2087777 +0.5192971 0.8746189 -0.1829076 +0.2753243 0.6614934 0.03630056 +0.2756771 0.3343427 0.2163756 +0.5974456 0.3903716 -0.2351365 +0.130387 0.1778356 -0.269037 +0.02163413 0.1816406 0.04885878 +0.5400308 0.3538398 0.1483167 +0.4256888 -0.1700144 -0.186097 +0.5585842 -0.5881555 -0.01302283 +-0.003234569 -0.3567553 -0.1266778 +-0.005187098 -0.220889 0.1300562 +0.7104012 -0.8419934 0.1452288 +0.4025245 -0.9743878 0.1854717 +0.4890165 -1.139676 -0.1646613 +0.6909469 -0.8085664 -0.2201199 +0.2885037 -0.7335444 -0.2847479 +0.2674055 -0.995465 -0.06517935 +0.987806 -1.206952 0.2110438 +1.171644 -1.176069 -0.2872209 +1.024809 -1.321295 -0.2285946 +0.9032865 -1.364864 0.02805585 +1.315317 -0.9330825 -0.1789696 +1.549696 -1.375615 0.0655082 +1.561197 -0.9361805 0.1477693 +1.633815 -1.271682 -0.2281576 +1.80487 -1.072045 0.2110439 +1.858089 -0.5927339 0.04986086 +2.076583 -1.110725 -0.061326 +1.941966 -0.8520235 -0.2938562 +2.36904 -0.4226727 -0.2658689 +2.474947 -0.2207618 0.1058706 +2.448738 -0.5965812 -0.04580876 +3 47 1 44 +3 44 1 45 +3 1 3 45 +3 45 3 5 +3 46 2 47 +3 47 2 1 +3 42 5 0 +3 0 5 4 +3 0 4 46 +3 46 4 2 +3 3 12 5 +3 5 6 4 +3 2 7 3 +3 2 3 1 +3 4 7 2 +3 7 12 3 +3 5 12 11 +3 7 10 12 +3 12 13 11 +3 5 11 6 +3 6 8 9 +3 6 9 4 +3 11 8 6 +3 4 9 10 +3 4 10 7 +3 8 14 9 +3 10 18 13 +3 10 13 12 +3 13 17 11 +3 11 17 8 +3 9 18 10 +3 18 15 13 +3 13 16 17 +3 17 14 8 +3 14 19 9 +3 9 19 18 +3 18 22 15 +3 13 15 16 +3 14 20 19 +3 16 21 17 +3 17 21 14 +3 14 21 20 +3 20 25 19 +3 18 19 22 +3 15 22 24 +3 19 23 22 +3 22 23 24 +3 15 24 16 +3 19 25 23 +3 24 29 16 +3 16 29 21 +3 25 26 23 +3 26 31 23 +3 23 31 24 +3 24 31 27 +3 24 27 29 +3 21 30 20 +3 20 30 25 +3 21 29 30 +3 30 28 25 +3 25 28 26 +3 28 31 26 +3 27 38 29 +3 29 38 30 +3 28 32 31 +3 31 32 27 +3 30 34 28 +3 27 33 38 +3 38 34 30 +3 28 34 32 +3 32 36 27 +3 32 35 36 +3 27 36 33 +3 34 35 32 +3 38 37 34 +3 34 41 39 +3 34 39 35 +3 35 39 36 +3 36 40 33 +3 38 33 42 +3 38 42 37 +3 34 37 41 +3 36 39 40 +3 39 43 40 +3 40 45 33 +3 42 0 37 +3 37 0 41 +3 39 41 43 +3 45 42 33 +3 43 44 40 +3 40 44 45 +3 41 46 43 +3 43 47 44 +3 0 46 41 +3 46 47 43 +3 45 5 42 +3 52 50 97 +3 97 51 99 +3 99 51 98 +3 98 53 48 +3 51 53 98 +3 50 51 97 +3 53 58 48 +3 48 58 49 +3 58 56 49 +3 49 54 52 +3 56 54 49 +3 52 54 50 +3 50 55 51 +3 51 55 53 +3 53 57 58 +3 56 63 54 +3 55 57 53 +3 54 62 50 +3 50 62 55 +3 57 59 60 +3 57 60 58 +3 62 67 55 +3 55 67 59 +3 55 59 57 +3 60 66 58 +3 58 66 61 +3 58 61 56 +3 56 61 63 +3 54 63 62 +3 59 65 60 +3 60 65 66 +3 59 67 65 +3 61 64 63 +3 63 64 62 +3 62 68 67 +3 67 68 65 +3 66 70 74 +3 66 74 61 +3 61 74 64 +3 64 71 62 +3 62 71 68 +3 68 69 65 +3 65 69 66 +3 69 70 66 +3 64 74 71 +3 71 72 68 +3 69 73 70 +3 68 72 69 +3 73 78 70 +3 74 75 71 +3 72 73 69 +3 70 76 74 +3 74 76 75 +3 71 75 72 +3 72 77 73 +3 77 78 73 +3 75 83 72 +3 72 83 77 +3 78 80 70 +3 70 80 79 +3 70 79 76 +3 75 82 83 +3 77 84 78 +3 84 80 78 +3 76 82 75 +3 83 84 77 +3 83 81 84 +3 80 85 79 +3 83 87 81 +3 84 88 80 +3 80 88 85 +3 82 86 83 +3 83 86 87 +3 81 88 84 +3 79 89 76 +3 76 89 82 +3 81 87 88 +3 82 89 86 +3 88 90 85 +3 85 91 79 +3 79 91 89 +3 87 90 88 +3 85 90 93 +3 85 93 91 +3 86 92 87 +3 87 92 90 +3 91 94 89 +3 89 96 86 +3 86 96 92 +3 89 94 96 +3 92 95 90 +3 90 95 93 +3 92 96 95 +3 95 99 48 +3 95 48 93 +3 93 48 94 +3 93 94 91 +3 96 97 95 +3 94 52 96 +3 95 97 99 +3 99 98 48 +3 96 52 97 +3 48 49 94 +3 49 52 94 diff --git a/HDVF/test/HDVF/data/simple_cubical_complex1.cub b/HDVF/test/HDVF/data/simple_cubical_complex1.cub new file mode 100644 index 00000000000..eeda1f2143d --- /dev/null +++ b/HDVF/test/HDVF/data/simple_cubical_complex1.cub @@ -0,0 +1,7 @@ +2 +5 3 +1 1 +3 0 +3 2 +2 1 +4 1 diff --git a/HDVF/test/HDVF/data/simple_ring.pgm b/HDVF/test/HDVF/data/simple_ring.pgm new file mode 100644 index 00000000000..5264d453901 --- /dev/null +++ b/HDVF/test/HDVF/data/simple_ring.pgm @@ -0,0 +1,7 @@ +P2 +3 4 +255 +1 1 1 +1 0 1 +1 0 1 +1 1 1 diff --git a/HDVF/test/HDVF/data/simple_simplicial_complex.simp b/HDVF/test/HDVF/data/simple_simplicial_complex.simp new file mode 100644 index 00000000000..61c15c12ecf --- /dev/null +++ b/HDVF/test/HDVF/data/simple_simplicial_complex.simp @@ -0,0 +1,5 @@ +3 2 1 +2 4 +4 3 + + diff --git a/HDVF/test/HDVF/data/test_abstract_simplicial_chain_complex/bnd0.osm b/HDVF/test/HDVF/data/test_abstract_simplicial_chain_complex/bnd0.osm new file mode 100644 index 00000000000..8adb4de5850 --- /dev/null +++ b/HDVF/test/HDVF/data/test_abstract_simplicial_chain_complex/bnd0.osm @@ -0,0 +1,3 @@ +0 +0 4 +0 diff --git a/HDVF/test/HDVF/data/test_abstract_simplicial_chain_complex/bnd1.osm b/HDVF/test/HDVF/data/test_abstract_simplicial_chain_complex/bnd1.osm new file mode 100644 index 00000000000..f5013cfc2f6 --- /dev/null +++ b/HDVF/test/HDVF/data/test_abstract_simplicial_chain_complex/bnd1.osm @@ -0,0 +1,13 @@ +0 +4 5 +10 +1 0 1 +0 0 1 +2 1 1 +0 1 1 +2 2 1 +1 2 1 +1 3 1 +3 3 1 +0 4 1 +3 4 1 diff --git a/HDVF/test/HDVF/data/test_abstract_simplicial_chain_complex/bnd2.osm b/HDVF/test/HDVF/data/test_abstract_simplicial_chain_complex/bnd2.osm new file mode 100644 index 00000000000..39b1c4c6df7 --- /dev/null +++ b/HDVF/test/HDVF/data/test_abstract_simplicial_chain_complex/bnd2.osm @@ -0,0 +1,6 @@ +0 +5 1 +3 +2 0 1 +1 0 1 +0 0 1 diff --git a/HDVF/test/HDVF/data/test_hdvf/test_hdvf.hdvf b/HDVF/test/HDVF/data/test_hdvf/test_hdvf.hdvf new file mode 100644 index 00000000000..2f057794a46 --- /dev/null +++ b/HDVF/test/HDVF/data/test_hdvf/test_hdvf.hdvf @@ -0,0 +1,70 @@ +0 +2 +6 9 3 +8 +-1 -1 -1 -1 -1 0 +1 1 -1 1 1 -1 1 0 -1 +1 1 1 +1 +6 6 +5 +5 3 1 +5 4 1 +5 2 1 +5 1 1 +5 0 1 +1 +9 9 +1 +7 8 1 +1 +3 3 +0 +0 +6 6 +0 +0 +9 9 +4 +6 7 4 +3 7 1 +4 7 4 +0 7 4 +0 +3 3 +0 +0 +9 6 +14 +6 0 4 +3 0 1 +4 0 4 +6 1 4 +3 1 1 +4 1 4 +0 1 4 +3 2 1 +4 2 4 +6 2 4 +1 2 4 +6 3 4 +3 3 1 +6 4 4 +0 +3 9 +3 +0 2 1 +1 5 1 +2 8 1 +0 +0 3 +0 +0 +0 6 +0 +0 +6 9 +0 +0 +9 3 +0 diff --git a/HDVF/test/HDVF/data/test_sparse_matrices/ColCol_prod_col.osm b/HDVF/test/HDVF/data/test_sparse_matrices/ColCol_prod_col.osm new file mode 100644 index 00000000000..6d0b58eb0ba --- /dev/null +++ b/HDVF/test/HDVF/data/test_sparse_matrices/ColCol_prod_col.osm @@ -0,0 +1,7 @@ +0 +4 4 +4 +1 0 -6 +3 0 -18 +1 1 9 +3 1 27 diff --git a/HDVF/test/HDVF/data/test_sparse_matrices/ColCol_prod_row.osm b/HDVF/test/HDVF/data/test_sparse_matrices/ColCol_prod_row.osm new file mode 100644 index 00000000000..9b13ab3994c --- /dev/null +++ b/HDVF/test/HDVF/data/test_sparse_matrices/ColCol_prod_row.osm @@ -0,0 +1,7 @@ +1 +4 4 +4 +1 1 9 +1 0 -6 +3 1 27 +3 0 -18 diff --git a/HDVF/test/HDVF/data/test_sparse_matrices/ColCol_sub.osm b/HDVF/test/HDVF/data/test_sparse_matrices/ColCol_sub.osm new file mode 100644 index 00000000000..49b4460f3b5 --- /dev/null +++ b/HDVF/test/HDVF/data/test_sparse_matrices/ColCol_sub.osm @@ -0,0 +1,5 @@ +0 +4 4 +2 +1 0 -2 +2 2 -2 diff --git a/HDVF/test/HDVF/data/test_sparse_matrices/ColCol_sum.osm b/HDVF/test/HDVF/data/test_sparse_matrices/ColCol_sum.osm new file mode 100644 index 00000000000..782c76f7f26 --- /dev/null +++ b/HDVF/test/HDVF/data/test_sparse_matrices/ColCol_sum.osm @@ -0,0 +1,8 @@ +0 +4 4 +5 +1 0 2 +3 0 12 +3 1 -18 +1 1 -6 +2 2 -2 diff --git a/HDVF/test/HDVF/data/test_sparse_matrices/ColRow_prod_col.osm b/HDVF/test/HDVF/data/test_sparse_matrices/ColRow_prod_col.osm new file mode 100644 index 00000000000..6d0b58eb0ba --- /dev/null +++ b/HDVF/test/HDVF/data/test_sparse_matrices/ColRow_prod_col.osm @@ -0,0 +1,7 @@ +0 +4 4 +4 +1 0 -6 +3 0 -18 +1 1 9 +3 1 27 diff --git a/HDVF/test/HDVF/data/test_sparse_matrices/ColRow_prod_row.osm b/HDVF/test/HDVF/data/test_sparse_matrices/ColRow_prod_row.osm new file mode 100644 index 00000000000..9b13ab3994c --- /dev/null +++ b/HDVF/test/HDVF/data/test_sparse_matrices/ColRow_prod_row.osm @@ -0,0 +1,7 @@ +1 +4 4 +4 +1 1 9 +1 0 -6 +3 1 27 +3 0 -18 diff --git a/HDVF/test/HDVF/data/test_sparse_matrices/ColRow_sub.osm b/HDVF/test/HDVF/data/test_sparse_matrices/ColRow_sub.osm new file mode 100644 index 00000000000..49b4460f3b5 --- /dev/null +++ b/HDVF/test/HDVF/data/test_sparse_matrices/ColRow_sub.osm @@ -0,0 +1,5 @@ +0 +4 4 +2 +1 0 -2 +2 2 -2 diff --git a/HDVF/test/HDVF/data/test_sparse_matrices/ColRow_sum.osm b/HDVF/test/HDVF/data/test_sparse_matrices/ColRow_sum.osm new file mode 100644 index 00000000000..782c76f7f26 --- /dev/null +++ b/HDVF/test/HDVF/data/test_sparse_matrices/ColRow_sum.osm @@ -0,0 +1,8 @@ +0 +4 4 +5 +1 0 2 +3 0 12 +3 1 -18 +1 1 -6 +2 2 -2 diff --git a/HDVF/test/HDVF/data/test_sparse_matrices/Col_transpose.osm b/HDVF/test/HDVF/data/test_sparse_matrices/Col_transpose.osm new file mode 100644 index 00000000000..02860054553 --- /dev/null +++ b/HDVF/test/HDVF/data/test_sparse_matrices/Col_transpose.osm @@ -0,0 +1,7 @@ +1 +4 4 +4 +0 3 6 +0 1 2 +1 3 -9 +1 1 -3 diff --git a/HDVF/test/HDVF/data/test_sparse_matrices/RowCol_prod_col.osm b/HDVF/test/HDVF/data/test_sparse_matrices/RowCol_prod_col.osm new file mode 100644 index 00000000000..9ede642cd71 --- /dev/null +++ b/HDVF/test/HDVF/data/test_sparse_matrices/RowCol_prod_col.osm @@ -0,0 +1,7 @@ +0 +4 4 +4 +3 0 -18 +1 0 -6 +3 1 27 +1 1 9 diff --git a/HDVF/test/HDVF/data/test_sparse_matrices/RowCol_prod_row.osm b/HDVF/test/HDVF/data/test_sparse_matrices/RowCol_prod_row.osm new file mode 100644 index 00000000000..9b13ab3994c --- /dev/null +++ b/HDVF/test/HDVF/data/test_sparse_matrices/RowCol_prod_row.osm @@ -0,0 +1,7 @@ +1 +4 4 +4 +1 1 9 +1 0 -6 +3 1 27 +3 0 -18 diff --git a/HDVF/test/HDVF/data/test_sparse_matrices/RowCol_sub.osm b/HDVF/test/HDVF/data/test_sparse_matrices/RowCol_sub.osm new file mode 100644 index 00000000000..ef866007ca1 --- /dev/null +++ b/HDVF/test/HDVF/data/test_sparse_matrices/RowCol_sub.osm @@ -0,0 +1,5 @@ +1 +4 4 +2 +1 0 -2 +2 2 -2 diff --git a/HDVF/test/HDVF/data/test_sparse_matrices/RowCol_sum.osm b/HDVF/test/HDVF/data/test_sparse_matrices/RowCol_sum.osm new file mode 100644 index 00000000000..1876f427428 --- /dev/null +++ b/HDVF/test/HDVF/data/test_sparse_matrices/RowCol_sum.osm @@ -0,0 +1,8 @@ +1 +4 4 +5 +1 1 -6 +1 0 2 +2 2 -2 +3 0 12 +3 1 -18 diff --git a/HDVF/test/HDVF/data/test_sparse_matrices/RowRow_prod_col.osm b/HDVF/test/HDVF/data/test_sparse_matrices/RowRow_prod_col.osm new file mode 100644 index 00000000000..9ede642cd71 --- /dev/null +++ b/HDVF/test/HDVF/data/test_sparse_matrices/RowRow_prod_col.osm @@ -0,0 +1,7 @@ +0 +4 4 +4 +3 0 -18 +1 0 -6 +3 1 27 +1 1 9 diff --git a/HDVF/test/HDVF/data/test_sparse_matrices/RowRow_prod_row.osm b/HDVF/test/HDVF/data/test_sparse_matrices/RowRow_prod_row.osm new file mode 100644 index 00000000000..9b13ab3994c --- /dev/null +++ b/HDVF/test/HDVF/data/test_sparse_matrices/RowRow_prod_row.osm @@ -0,0 +1,7 @@ +1 +4 4 +4 +1 1 9 +1 0 -6 +3 1 27 +3 0 -18 diff --git a/HDVF/test/HDVF/data/test_sparse_matrices/RowRow_sub.osm b/HDVF/test/HDVF/data/test_sparse_matrices/RowRow_sub.osm new file mode 100644 index 00000000000..ef866007ca1 --- /dev/null +++ b/HDVF/test/HDVF/data/test_sparse_matrices/RowRow_sub.osm @@ -0,0 +1,5 @@ +1 +4 4 +2 +1 0 -2 +2 2 -2 diff --git a/HDVF/test/HDVF/data/test_sparse_matrices/RowRow_sum.osm b/HDVF/test/HDVF/data/test_sparse_matrices/RowRow_sum.osm new file mode 100644 index 00000000000..1876f427428 --- /dev/null +++ b/HDVF/test/HDVF/data/test_sparse_matrices/RowRow_sum.osm @@ -0,0 +1,8 @@ +1 +4 4 +5 +1 1 -6 +1 0 2 +2 2 -2 +3 0 12 +3 1 -18 diff --git a/HDVF/test/HDVF/data/test_sparse_matrices/Row_transpose.osm b/HDVF/test/HDVF/data/test_sparse_matrices/Row_transpose.osm new file mode 100644 index 00000000000..c697cbaeb76 --- /dev/null +++ b/HDVF/test/HDVF/data/test_sparse_matrices/Row_transpose.osm @@ -0,0 +1,7 @@ +0 +4 4 +4 +1 1 -3 +0 1 2 +1 3 -9 +0 3 6 diff --git a/HDVF/test/HDVF/data/test_sparse_matrices/columnMajor.osm b/HDVF/test/HDVF/data/test_sparse_matrices/columnMajor.osm new file mode 100644 index 00000000000..88f9b2d31f4 --- /dev/null +++ b/HDVF/test/HDVF/data/test_sparse_matrices/columnMajor.osm @@ -0,0 +1,7 @@ +0 +4 4 +4 +3 0 6 +1 0 2 +3 1 -9 +1 1 -3 diff --git a/HDVF/test/HDVF/data/test_sparse_matrices/columnMajor_del_col.osm b/HDVF/test/HDVF/data/test_sparse_matrices/columnMajor_del_col.osm new file mode 100644 index 00000000000..804e486b035 --- /dev/null +++ b/HDVF/test/HDVF/data/test_sparse_matrices/columnMajor_del_col.osm @@ -0,0 +1,5 @@ +0 +4 4 +2 +3 1 -9 +1 1 -3 diff --git a/HDVF/test/HDVF/data/test_sparse_matrices/columnMajor_del_row.osm b/HDVF/test/HDVF/data/test_sparse_matrices/columnMajor_del_row.osm new file mode 100644 index 00000000000..db4795c9879 --- /dev/null +++ b/HDVF/test/HDVF/data/test_sparse_matrices/columnMajor_del_row.osm @@ -0,0 +1,5 @@ +0 +4 4 +2 +3 0 6 +3 1 -9 diff --git a/HDVF/test/HDVF/data/test_sparse_matrices/rowMajor.osm b/HDVF/test/HDVF/data/test_sparse_matrices/rowMajor.osm new file mode 100644 index 00000000000..44dbd90b8e5 --- /dev/null +++ b/HDVF/test/HDVF/data/test_sparse_matrices/rowMajor.osm @@ -0,0 +1,7 @@ +1 +4 4 +4 +1 1 -3 +1 0 2 +3 1 -9 +3 0 6 diff --git a/HDVF/test/HDVF/data/test_sparse_matrices/rowMajor_del_column.osm b/HDVF/test/HDVF/data/test_sparse_matrices/rowMajor_del_column.osm new file mode 100644 index 00000000000..f5560788841 --- /dev/null +++ b/HDVF/test/HDVF/data/test_sparse_matrices/rowMajor_del_column.osm @@ -0,0 +1,5 @@ +1 +4 4 +2 +1 1 -3 +3 1 -9 diff --git a/HDVF/test/HDVF/data/test_sparse_matrices/rowMajor_del_row.osm b/HDVF/test/HDVF/data/test_sparse_matrices/rowMajor_del_row.osm new file mode 100644 index 00000000000..4675077959e --- /dev/null +++ b/HDVF/test/HDVF/data/test_sparse_matrices/rowMajor_del_row.osm @@ -0,0 +1,5 @@ +1 +4 4 +2 +3 0 6 +3 1 -9 diff --git a/HDVF/test/HDVF/data/three_triangles.off b/HDVF/test/HDVF/data/three_triangles.off new file mode 100644 index 00000000000..31a57658643 --- /dev/null +++ b/HDVF/test/HDVF/data/three_triangles.off @@ -0,0 +1,11 @@ +OFF +6 3 0 +-1 0 1 +1 0 1 +0 0 -1 +2.324733 0.081949 -0.896115 +1.234445 0.248743 -2.933485 +3.339287 -0.081581 1.114902 +3 0 1 2 +3 4 2 3 +3 3 5 1 diff --git a/HDVF/test/HDVF/test.hdvf b/HDVF/test/HDVF/test.hdvf new file mode 100644 index 00000000000..40a356b0c56 --- /dev/null +++ b/HDVF/test/HDVF/test.hdvf @@ -0,0 +1,4987 @@ +0 +2 +100 300 200 +-1 -1 -1 -1 -1 -1 0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 0 -1 +-1 1 -1 1 -1 -1 1 1 -1 -1 1 -1 -1 -1 1 -1 -1 -1 -1 -1 -1 -1 1 1 -1 -1 -1 -1 -1 -1 -1 -1 1 1 1 -1 -1 -1 1 -1 -1 -1 -1 1 -1 1 -1 -1 -1 1 1 -1 1 -1 -1 -1 -1 -1 -1 -1 -1 1 1 -1 -1 -1 1 1 1 -1 1 -1 1 -1 -1 -1 -1 1 -1 -1 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 1 -1 1 1 -1 -1 1 -1 -1 -1 -1 1 -1 -1 1 1 -1 1 1 1 -1 -1 -1 -1 -1 1 -1 -1 -1 1 -1 1 -1 1 -1 -1 -1 -1 1 -1 -1 -1 1 1 -1 1 1 1 0 -1 1 -1 0 -1 1 -1 1 -1 -1 -1 -1 1 -1 -1 -1 -1 -1 -1 -1 1 -1 -1 1 1 -1 1 -1 1 1 1 -1 -1 -1 -1 1 1 -1 -1 -1 1 -1 -1 -1 -1 -1 -1 1 -1 1 1 1 -1 1 -1 1 -1 -1 -1 -1 -1 1 1 -1 1 -1 -1 -1 1 -1 -1 -1 1 1 -1 1 -1 -1 -1 -1 -1 -1 1 -1 1 1 1 -1 -1 -1 -1 -1 -1 1 1 -1 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 1 1 -1 -1 1 1 -1 -1 1 -1 1 -1 1 -1 1 1 -1 -1 -1 -1 -1 -1 -1 -1 -1 1 1 1 -1 -1 1 1 1 -1 -1 -1 1 1 -1 -1 -1 1 -1 -1 -1 -1 0 -1 -1 -1 -1 -1 0 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +1 +100 100 +98 +6 40 1 +6 34 1 +6 12 1 +6 5 1 +6 25 1 +6 3 1 +6 20 1 +6 17 1 +6 22 1 +6 45 1 +6 24 1 +6 1 1 +6 41 1 +6 46 1 +6 7 1 +6 30 1 +6 42 1 +6 19 1 +6 9 1 +6 32 1 +6 31 1 +6 8 1 +6 4 1 +6 27 1 +6 18 1 +6 35 1 +6 47 1 +6 0 1 +6 23 1 +6 11 1 +6 2 1 +6 36 1 +6 13 1 +6 10 1 +6 33 1 +6 29 1 +6 14 1 +6 37 1 +6 38 1 +6 15 1 +6 26 1 +6 43 1 +6 44 1 +6 21 1 +6 16 1 +6 39 1 +6 28 1 +98 99 1 +98 97 1 +98 94 1 +98 96 1 +98 85 1 +98 80 1 +98 91 1 +98 92 1 +98 69 1 +98 70 1 +98 83 1 +98 87 1 +98 76 1 +98 88 1 +98 65 1 +98 84 1 +98 63 1 +98 86 1 +98 71 1 +98 95 1 +98 48 1 +98 58 1 +98 81 1 +98 77 1 +98 54 1 +98 78 1 +98 55 1 +98 49 1 +98 72 1 +98 73 1 +98 50 1 +98 57 1 +98 60 1 +98 61 1 +98 62 1 +98 79 1 +98 56 1 +98 74 1 +98 51 1 +98 75 1 +98 52 1 +98 53 1 +98 64 1 +98 66 1 +98 89 1 +98 67 1 +98 90 1 +98 68 1 +98 93 1 +98 82 1 +98 59 1 +1 +300 300 +148 +139 71 4 +139 30 5 +139 44 4 +139 21 4 +139 132 5 +139 35 5 +139 58 1 +139 2 1 +139 5 1 +139 31 4 +139 4 1 +139 59 5 +139 13 5 +139 46 5 +139 114 5 +139 48 5 +139 0 4 +139 47 5 +139 24 5 +139 16 5 +139 113 1 +139 56 5 +139 79 4 +139 63 5 +139 8 1 +139 55 5 +139 40 5 +139 87 4 +139 39 4 +139 86 4 +139 73 5 +139 41 5 +139 88 4 +139 42 5 +139 64 4 +139 111 1 +139 76 5 +139 123 4 +139 54 5 +139 75 5 +139 36 4 +139 130 1 +139 15 5 +139 37 1 +139 131 4 +139 84 4 +139 74 5 +139 104 4 +143 19 5 +143 18 4 +143 30 5 +143 76 1 +143 44 1 +143 21 1 +143 17 1 +143 64 1 +143 41 5 +143 47 4 +143 0 1 +143 35 5 +143 11 4 +143 58 4 +143 2 4 +143 5 4 +143 74 1 +143 31 1 +143 54 4 +143 8 4 +143 55 5 +143 37 4 +143 4 4 +143 73 1 +143 75 1 +143 42 1 +143 39 1 +143 16 4 +143 63 1 +143 40 4 +143 13 5 +143 36 1 +143 59 5 +143 48 5 +143 15 5 +143 46 4 +143 71 1 +143 24 4 +143 9 1 +143 56 5 +293 289 1 +293 290 5 +293 198 4 +293 295 1 +293 184 4 +293 281 1 +293 144 1 +293 241 4 +293 291 4 +293 194 4 +293 263 4 +293 216 5 +293 205 4 +293 227 5 +293 178 4 +293 275 4 +293 181 4 +293 228 5 +293 206 4 +293 229 5 +293 242 5 +293 146 4 +293 243 5 +293 196 1 +293 240 4 +293 238 4 +293 214 4 +293 264 4 +293 167 4 +293 207 4 +293 280 1 +293 186 4 +293 209 5 +293 223 4 +293 270 1 +293 266 4 +293 219 4 +293 292 4 +293 221 4 +293 210 4 +293 296 1 +293 199 4 +293 218 4 +293 211 4 +293 271 1 +293 177 4 +293 269 1 +293 294 5 +293 200 4 +293 220 4 +293 179 1 +299 297 1 +299 298 1 +299 292 1 +299 294 1 +299 295 4 +299 290 5 +299 289 4 +299 291 1 +299 296 4 +1 +200 200 +0 +0 +100 100 +0 +0 +300 300 +71 +137 139 1 +129 139 5 +14 139 4 +120 139 5 +109 139 5 +136 139 1 +133 139 5 +110 139 5 +134 139 5 +116 139 5 +38 139 5 +61 139 5 +52 139 5 +7 139 5 +122 139 5 +138 139 4 +23 139 5 +3 139 1 +133 143 5 +134 143 5 +38 143 5 +7 143 5 +23 143 5 +136 143 5 +61 143 5 +14 143 5 +129 143 5 +122 143 5 +120 143 1 +110 143 5 +109 143 5 +105 143 1 +43 143 5 +137 143 1 +116 143 5 +22 143 1 +138 143 5 +91 143 1 +49 143 1 +66 143 1 +33 143 5 +10 143 5 +32 143 5 +52 143 5 +6 143 1 +34 143 1 +1 143 4 +284 293 1 +283 293 4 +278 293 4 +273 293 1 +283 299 4 +274 299 4 +252 299 5 +278 299 4 +255 299 4 +257 299 1 +234 299 4 +166 299 5 +212 299 4 +224 299 4 +202 299 4 +248 299 4 +284 299 1 +215 299 4 +273 299 1 +204 299 4 +262 299 5 +193 299 5 +176 299 4 +164 299 4 +0 +200 200 +198 +94 95 1 +5 95 4 +8 95 1 +17 95 1 +91 95 4 +14 95 1 +13 95 1 +24 95 4 +93 95 4 +12 95 4 +9 95 4 +4 95 4 +15 95 1 +92 95 4 +38 95 4 +29 95 1 +40 95 1 +35 95 4 +34 95 1 +57 95 4 +41 95 4 +18 95 1 +28 95 1 +42 95 1 +19 95 4 +16 95 4 +20 95 1 +43 95 1 +2 95 1 +25 95 4 +3 95 1 +0 95 1 +33 95 4 +10 95 4 +56 95 1 +22 95 4 +1 95 4 +90 95 1 +89 95 1 +88 95 4 +87 95 1 +83 95 4 +76 95 4 +75 95 4 +86 95 4 +85 95 1 +84 95 1 +82 95 1 +81 95 1 +80 95 1 +71 95 1 +72 95 4 +65 95 1 +69 95 4 +64 95 4 +63 95 4 +67 95 1 +66 95 1 +60 95 4 +59 95 4 +62 95 4 +61 95 1 +55 95 1 +51 95 1 +48 95 4 +47 95 4 +45 95 4 +39 95 4 +37 95 1 +36 95 1 +78 95 1 +31 95 4 +50 95 4 +74 95 4 +27 95 4 +49 95 1 +73 95 1 +26 95 4 +6 95 4 +53 95 4 +54 95 4 +7 95 4 +77 95 4 +30 95 4 +46 95 1 +70 95 1 +23 95 1 +79 95 4 +32 95 1 +44 95 1 +68 95 4 +21 95 1 +58 95 1 +11 95 1 +52 95 1 +198 199 1 +148 199 4 +184 199 4 +173 199 4 +172 199 1 +182 199 1 +135 199 1 +181 199 4 +147 199 4 +169 199 4 +197 199 4 +100 199 1 +150 199 4 +145 199 1 +192 199 1 +178 199 1 +179 199 4 +132 199 1 +175 199 1 +128 199 4 +176 199 4 +129 199 1 +185 199 1 +162 199 4 +183 199 1 +136 199 4 +180 199 4 +174 199 1 +177 199 4 +171 199 4 +124 199 4 +123 199 1 +170 199 1 +168 199 1 +167 199 4 +156 199 4 +157 199 1 +163 199 1 +159 199 4 +155 199 4 +144 199 1 +153 199 4 +152 199 1 +151 199 1 +149 199 4 +141 199 1 +140 199 4 +139 199 1 +158 199 4 +111 199 1 +134 199 4 +133 199 1 +127 199 4 +126 199 1 +131 199 1 +107 199 1 +154 199 1 +130 199 4 +119 199 4 +166 199 4 +125 199 4 +137 199 1 +113 199 1 +160 199 1 +161 199 1 +114 199 4 +164 199 1 +117 199 1 +115 199 1 +138 199 4 +165 199 1 +118 199 4 +188 199 4 +187 199 4 +106 199 4 +109 199 4 +186 199 1 +104 199 4 +116 199 4 +112 199 1 +108 199 4 +189 199 1 +142 199 4 +143 199 4 +190 199 1 +193 199 1 +96 199 4 +102 199 1 +196 199 4 +99 199 4 +146 199 1 +103 199 4 +98 199 1 +195 199 1 +101 199 1 +121 199 4 +191 199 4 +194 199 4 +97 199 4 +120 199 1 +122 199 1 +110 199 1 +105 199 4 +0 +300 100 +867 +120 0 4 +137 0 4 +22 0 4 +91 0 4 +141 0 4 +49 0 4 +34 0 4 +1 0 1 +66 0 4 +105 0 4 +6 0 4 +141 1 4 +137 1 4 +136 1 4 +138 1 1 +14 1 1 +3 1 4 +120 2 4 +137 2 4 +22 2 4 +91 2 4 +141 2 4 +49 2 4 +34 2 4 +66 2 4 +105 2 4 +6 2 4 +14 3 1 +137 3 4 +141 3 4 +136 3 4 +138 3 1 +120 4 4 +105 4 4 +137 4 4 +91 4 4 +22 4 4 +66 4 4 +141 4 4 +49 4 4 +34 4 4 +141 5 4 +14 5 1 +136 5 4 +138 5 1 +137 5 4 +7 5 4 +120 7 4 +137 7 4 +22 7 4 +91 7 4 +141 7 4 +49 7 4 +10 7 4 +66 7 4 +1 7 1 +105 7 4 +6 7 4 +34 7 4 +141 8 4 +14 8 1 +137 8 4 +136 8 4 +137 9 4 +141 9 4 +136 9 4 +105 10 4 +137 10 4 +91 10 4 +141 10 4 +49 10 4 +34 10 4 +120 10 4 +32 10 4 +43 10 4 +66 10 4 +33 10 0 +141 11 4 +120 11 4 +105 11 4 +91 11 4 +66 11 4 +137 11 4 +49 11 4 +34 11 4 +141 12 4 +14 12 1 +136 12 4 +138 12 1 +23 12 1 +137 12 4 +7 12 4 +105 13 4 +137 13 4 +91 13 4 +33 13 4 +66 13 4 +141 13 4 +49 13 4 +34 13 4 +120 13 4 +32 13 4 +141 14 4 +137 14 4 +120 14 4 +91 14 4 +66 14 4 +105 14 4 +50 14 4 +105 15 4 +137 15 4 +91 15 4 +66 15 4 +141 15 4 +49 15 4 +34 15 4 +120 15 4 +32 15 4 +141 16 4 +120 16 4 +66 16 4 +105 16 4 +91 16 4 +137 16 4 +49 16 4 +141 17 4 +14 17 1 +136 17 4 +138 17 1 +23 17 1 +137 17 4 +38 17 1 +7 17 4 +141 18 4 +137 18 4 +120 18 4 +91 18 4 +68 18 4 +105 18 4 +45 18 4 +141 19 4 +137 19 4 +120 19 4 +105 19 4 +91 19 4 +68 19 4 +141 20 4 +14 20 1 +136 20 4 +138 20 1 +23 20 1 +137 20 4 +38 20 1 +7 20 4 +52 20 1 +141 21 4 +137 21 4 +120 21 4 +105 21 4 +91 21 4 +66 21 4 +141 22 4 +14 22 1 +136 22 4 +138 22 1 +61 22 1 +23 22 1 +137 22 4 +38 22 1 +62 22 4 +7 22 4 +52 22 1 +141 23 4 +137 23 4 +120 23 4 +105 23 4 +91 23 4 +67 23 4 +141 24 4 +14 24 1 +136 24 4 +23 24 1 +137 24 4 +38 24 1 +7 24 4 +52 24 1 +138 24 1 +61 24 1 +72 24 4 +141 25 4 +14 25 1 +136 25 4 +138 25 1 +61 25 1 +137 25 4 +38 25 1 +23 25 1 +52 25 1 +7 25 4 +141 26 4 +120 26 4 +105 26 4 +137 26 4 +93 26 4 +70 26 4 +141 27 4 +137 27 4 +91 27 4 +120 27 4 +105 27 4 +141 28 4 +120 28 4 +105 28 4 +137 28 4 +93 28 4 +141 29 4 +137 29 4 +120 29 4 +94 29 4 +105 29 4 +80 29 4 +141 30 4 +137 30 4 +120 30 4 +105 30 4 +94 30 4 +77 30 4 +80 30 4 +141 31 4 +137 31 4 +120 31 4 +94 31 4 +105 31 4 +137 32 4 +120 32 4 +106 32 4 +141 32 4 +97 32 4 +141 33 4 +137 33 4 +102 33 4 +120 33 4 +110 33 4 +14 34 1 +137 34 4 +136 34 4 +134 34 4 +138 34 1 +141 34 4 +108 34 4 +141 35 4 +137 35 4 +120 35 4 +105 35 4 +141 36 4 +137 36 4 +120 36 4 +106 36 4 +141 37 4 +137 37 4 +109 37 1 +120 37 4 +141 38 4 +137 38 4 +120 38 4 +110 38 4 +141 39 4 +137 39 4 +120 39 4 +141 40 4 +14 40 1 +129 40 0 +137 40 4 +120 40 0 +109 40 0 +136 40 4 +133 40 0 +110 40 0 +134 40 4 +61 40 0 +38 40 0 +7 40 0 +122 40 0 +138 40 1 +23 40 0 +116 40 0 +52 40 0 +141 41 4 +137 41 4 +136 41 4 +138 41 1 +133 41 4 +14 41 1 +124 41 4 +141 42 4 +137 42 4 +120 42 4 +116 42 1 +141 43 4 +137 43 4 +129 43 4 +141 44 4 +137 44 4 +141 45 4 +137 45 4 +120 45 4 +122 45 1 +116 45 1 +141 46 4 +14 46 1 +137 46 4 +136 46 4 +138 46 1 +133 46 4 +141 47 4 +274 48 4 +278 48 4 +255 48 4 +257 48 1 +234 48 4 +224 48 4 +273 48 1 +204 48 4 +189 48 1 +212 48 4 +166 48 1 +176 48 4 +187 48 4 +248 48 4 +202 48 4 +283 48 4 +145 48 1 +191 48 1 +215 48 4 +169 48 4 +180 48 4 +175 48 4 +283 49 4 +274 49 4 +278 49 4 +163 49 4 +255 49 4 +202 49 4 +248 49 4 +215 49 4 +273 49 1 +204 49 4 +257 49 1 +234 49 4 +212 49 4 +224 49 4 +176 49 4 +274 50 4 +278 50 4 +255 50 4 +257 50 1 +234 50 4 +224 50 4 +273 50 1 +204 50 4 +189 50 1 +212 50 4 +166 50 1 +176 50 4 +187 50 4 +248 50 4 +202 50 4 +283 50 4 +191 50 1 +215 50 4 +169 50 4 +180 50 4 +175 50 4 +274 51 4 +278 51 4 +255 51 4 +257 51 1 +234 51 4 +224 51 4 +273 51 1 +204 51 4 +212 51 4 +189 51 1 +166 51 1 +176 51 4 +187 51 4 +248 51 4 +202 51 4 +283 51 4 +145 51 1 +191 51 1 +215 51 4 +169 51 4 +147 51 1 +180 51 4 +175 51 4 +274 52 4 +278 52 4 +255 52 4 +257 52 1 +234 52 4 +224 52 4 +215 52 4 +273 52 1 +204 52 4 +212 52 4 +166 52 1 +189 52 1 +176 52 4 +187 52 4 +248 52 4 +202 52 4 +283 52 4 +168 52 4 +191 52 1 +180 52 4 +175 52 4 +274 53 4 +278 53 4 +255 53 4 +257 53 1 +234 53 4 +224 53 4 +215 53 4 +273 53 1 +204 53 4 +212 53 4 +166 53 1 +189 53 1 +248 53 4 +202 53 4 +283 53 4 +191 53 1 +180 53 4 +152 53 1 +175 53 4 +170 53 4 +176 53 4 +187 53 4 +274 54 4 +278 54 4 +255 54 4 +257 54 1 +234 54 4 +224 54 4 +215 54 4 +273 54 1 +204 54 4 +212 54 4 +166 54 1 +189 54 1 +176 54 4 +187 54 4 +248 54 4 +202 54 4 +283 54 4 +191 54 1 +180 54 4 +175 54 4 +170 54 4 +283 55 4 +274 55 4 +278 55 4 +255 55 4 +257 55 1 +234 55 4 +212 55 4 +224 55 4 +248 55 4 +202 55 4 +176 55 4 +164 55 4 +215 55 4 +273 55 1 +204 55 4 +160 55 4 +274 56 4 +278 56 4 +255 56 4 +215 56 4 +273 56 1 +204 56 4 +257 56 1 +234 56 4 +166 56 1 +189 56 0 +212 56 4 +190 56 4 +224 56 4 +248 56 4 +202 56 4 +283 56 4 +191 56 1 +176 56 4 +283 57 4 +274 57 4 +278 57 4 +255 57 4 +257 57 1 +234 57 4 +212 57 4 +224 57 4 +248 57 4 +202 57 4 +176 57 4 +215 57 4 +273 57 1 +204 57 4 +164 57 4 +283 58 4 +274 58 4 +278 58 4 +255 58 4 +202 58 4 +248 58 4 +215 58 4 +273 58 1 +204 58 4 +257 58 1 +234 58 4 +166 58 1 +212 58 4 +224 58 4 +176 58 4 +283 59 4 +274 59 4 +278 59 4 +255 59 4 +202 59 4 +248 59 4 +215 59 4 +273 59 1 +204 59 4 +257 59 1 +234 59 4 +212 59 4 +224 59 4 +176 59 4 +274 60 4 +278 60 4 +255 60 4 +257 60 1 +234 60 4 +224 60 4 +215 60 4 +273 60 1 +204 60 4 +212 60 4 +166 60 1 +189 60 1 +176 60 4 +187 60 4 +248 60 4 +202 60 4 +283 60 4 +191 60 1 +180 60 4 +175 60 4 +274 61 4 +278 61 4 +255 61 4 +257 61 1 +234 61 4 +215 61 4 +273 61 1 +204 61 4 +189 61 1 +212 61 4 +166 61 1 +187 61 4 +176 61 4 +224 61 4 +248 61 4 +202 61 4 +283 61 4 +191 61 1 +180 61 4 +283 62 4 +274 62 4 +278 62 4 +255 62 4 +202 62 4 +248 62 4 +193 62 1 +215 62 4 +273 62 1 +204 62 4 +257 62 1 +234 62 4 +212 62 4 +224 62 4 +283 63 4 +204 63 4 +273 63 1 +274 63 4 +278 63 4 +255 63 4 +202 63 4 +248 63 4 +215 63 4 +234 63 4 +257 63 1 +224 63 4 +212 63 4 +274 64 4 +278 64 4 +255 64 4 +215 64 4 +273 64 1 +204 64 4 +257 64 1 +234 64 4 +189 64 1 +166 64 1 +212 64 4 +224 64 4 +248 64 4 +202 64 4 +283 64 4 +191 64 1 +187 64 4 +176 64 4 +283 65 4 +204 65 4 +273 65 1 +274 65 4 +278 65 4 +255 65 4 +234 65 4 +257 65 1 +213 65 4 +224 65 4 +248 65 4 +215 65 4 +195 65 4 +283 66 4 +204 66 4 +273 66 1 +274 66 4 +278 66 4 +255 66 4 +248 66 4 +215 66 4 +234 66 4 +257 66 1 +201 66 4 +224 66 4 +212 66 4 +274 67 4 +278 67 4 +255 67 4 +202 67 4 +248 67 4 +215 67 4 +273 67 1 +204 67 4 +257 67 1 +234 67 4 +189 67 1 +166 67 1 +212 67 4 +224 67 4 +283 67 4 +191 67 1 +176 67 4 +274 68 4 +278 68 4 +255 68 4 +202 68 4 +248 68 4 +215 68 4 +273 68 1 +204 68 4 +257 68 1 +234 68 4 +166 68 1 +212 68 4 +224 68 4 +283 68 4 +191 68 1 +176 68 4 +283 69 4 +204 69 4 +273 69 1 +274 69 4 +278 69 4 +255 69 4 +248 69 4 +215 69 4 +234 69 4 +257 69 1 +224 69 4 +213 69 4 +283 70 4 +273 70 1 +255 70 4 +278 70 4 +234 70 4 +257 70 1 +224 70 4 +248 70 4 +274 70 4 +208 70 4 +283 71 4 +204 71 4 +273 71 1 +274 71 4 +278 71 4 +255 71 4 +248 71 4 +215 71 4 +212 71 4 +234 71 4 +257 71 1 +224 71 4 +283 72 4 +273 72 1 +274 72 4 +255 72 4 +248 72 4 +278 72 4 +234 72 4 +257 72 1 +224 72 4 +283 73 4 +273 73 1 +274 73 4 +255 73 4 +204 73 4 +248 73 4 +278 73 4 +234 73 4 +257 73 1 +224 73 4 +283 74 4 +273 74 1 +274 74 4 +257 74 1 +248 74 4 +278 74 4 +234 74 4 +255 74 4 +225 74 4 +283 75 4 +273 75 1 +274 75 4 +255 75 4 +204 75 4 +248 75 4 +215 75 4 +278 75 4 +234 75 4 +257 75 1 +224 75 4 +283 76 4 +278 76 4 +273 76 1 +274 76 4 +257 76 1 +226 76 4 +248 76 4 +255 76 4 +222 76 4 +233 76 1 +236 76 4 +283 77 4 +278 77 4 +273 77 1 +274 77 4 +257 77 1 +222 77 4 +233 77 1 +255 77 4 +236 77 4 +248 77 4 +283 78 4 +278 78 4 +273 78 1 +274 78 4 +257 78 1 +233 78 1 +255 78 4 +236 78 4 +248 78 4 +283 79 4 +273 79 1 +274 79 4 +257 79 1 +255 79 4 +248 79 4 +278 79 4 +234 79 4 +283 80 4 +259 80 4 +277 80 4 +247 80 4 +283 81 4 +278 81 4 +273 81 1 +274 81 4 +257 81 1 +255 81 4 +248 81 4 +236 81 4 +283 82 4 +278 82 4 +274 82 4 +257 82 1 +255 82 4 +273 82 1 +248 82 4 +283 83 4 +278 83 4 +273 83 1 +274 83 4 +251 83 4 +283 84 4 +278 84 4 +273 84 1 +274 84 4 +252 84 4 +283 85 4 +277 85 4 +259 85 4 +283 86 4 +278 86 4 +273 86 1 +274 86 4 +257 86 1 +255 86 4 +278 87 4 +273 87 1 +274 87 4 +257 87 1 +283 87 4 +261 87 4 +283 88 4 +278 88 4 +273 88 1 +274 88 4 +283 89 4 +278 89 4 +272 89 4 +283 90 4 +278 90 4 +273 90 1 +274 90 4 +257 90 1 +283 91 4 +277 91 4 +283 92 4 +278 92 4 +262 92 1 +273 92 1 +274 92 4 +252 92 4 +257 92 0 +283 93 4 +278 93 4 +284 94 4 +279 94 4 +283 95 4 +278 95 4 +273 95 1 +283 96 4 +284 97 4 +288 99 4 +0 +200 300 +3555 +92 0 1 +92 2 4 +0 2 1 +92 4 4 +0 4 1 +1 4 4 +2 5 1 +92 5 4 +0 5 1 +1 5 4 +2 8 1 +3 8 1 +92 8 4 +0 8 1 +1 8 4 +94 9 1 +94 11 4 +4 11 1 +5 12 1 +94 13 4 +5 13 1 +8 13 4 +17 13 4 +91 13 1 +14 13 4 +13 13 4 +24 13 1 +93 13 1 +12 13 1 +9 13 1 +4 13 1 +15 13 4 +92 13 1 +38 13 1 +29 13 4 +40 13 4 +35 13 1 +34 13 4 +57 13 1 +41 13 1 +18 13 4 +28 13 4 +42 13 4 +19 13 1 +16 13 1 +20 13 4 +43 13 4 +2 13 4 +25 13 1 +3 13 4 +0 13 4 +33 13 1 +10 13 1 +56 13 4 +22 13 1 +1 13 1 +90 13 4 +89 13 4 +88 13 1 +87 13 4 +83 13 1 +76 13 1 +75 13 1 +86 13 1 +85 13 4 +84 13 4 +82 13 4 +81 13 4 +80 13 4 +71 13 4 +72 13 1 +65 13 4 +69 13 1 +64 13 1 +63 13 1 +67 13 4 +66 13 4 +60 13 1 +59 13 1 +62 13 1 +61 13 4 +55 13 4 +51 13 4 +48 13 1 +47 13 1 +45 13 1 +39 13 1 +37 13 4 +36 13 4 +78 13 4 +31 13 1 +50 13 1 +74 13 1 +27 13 1 +49 13 4 +73 13 4 +26 13 1 +6 13 1 +53 13 1 +54 13 1 +7 13 1 +77 13 1 +30 13 1 +46 13 4 +70 13 4 +23 13 4 +79 13 1 +32 13 4 +44 13 4 +68 13 1 +21 13 4 +58 13 4 +11 13 4 +52 13 4 +94 15 1 +5 15 4 +8 15 1 +17 15 1 +91 15 4 +14 15 1 +13 15 1 +24 15 4 +93 15 4 +12 15 4 +9 15 4 +4 15 4 +15 15 1 +92 15 4 +38 15 4 +29 15 1 +40 15 1 +35 15 4 +34 15 1 +57 15 4 +41 15 4 +18 15 1 +28 15 1 +42 15 1 +19 15 4 +16 15 4 +20 15 1 +43 15 1 +2 15 1 +25 15 4 +3 15 1 +0 15 1 +33 15 4 +10 15 4 +56 15 1 +22 15 4 +1 15 4 +90 15 1 +89 15 1 +88 15 4 +87 15 1 +83 15 4 +76 15 4 +75 15 4 +86 15 4 +85 15 1 +84 15 1 +82 15 1 +81 15 1 +80 15 1 +71 15 1 +72 15 4 +65 15 1 +69 15 4 +64 15 4 +63 15 4 +67 15 1 +66 15 1 +60 15 4 +59 15 4 +62 15 4 +61 15 1 +55 15 1 +52 15 1 +51 15 1 +48 15 4 +47 15 4 +45 15 4 +39 15 4 +36 15 1 +7 15 4 +54 15 4 +78 15 1 +31 15 4 +50 15 4 +74 15 4 +27 15 4 +37 15 1 +49 15 1 +73 15 1 +26 15 4 +53 15 4 +77 15 4 +30 15 4 +46 15 1 +70 15 1 +23 15 1 +79 15 4 +32 15 1 +44 15 1 +68 15 4 +21 15 1 +58 15 1 +11 15 1 +92 16 4 +38 16 4 +29 16 1 +40 16 1 +35 16 4 +34 16 1 +57 16 4 +41 16 4 +18 16 1 +28 16 1 +42 16 1 +19 16 4 +16 16 4 +20 16 1 +43 16 1 +2 16 1 +25 16 4 +3 16 1 +0 16 1 +33 16 4 +10 16 4 +56 16 1 +22 16 4 +1 16 4 +90 16 1 +89 16 1 +88 16 4 +87 16 1 +83 16 4 +76 16 4 +75 16 4 +86 16 4 +85 16 1 +82 16 1 +81 16 1 +80 16 1 +71 16 1 +72 16 4 +65 16 1 +69 16 4 +64 16 4 +63 16 4 +67 16 1 +66 16 1 +60 16 4 +59 16 4 +62 16 4 +61 16 1 +52 16 1 +51 16 1 +48 16 4 +47 16 4 +45 16 4 +39 16 4 +36 16 1 +54 16 4 +78 16 1 +31 16 4 +50 16 4 +74 16 4 +27 16 4 +53 16 4 +77 16 4 +30 16 4 +46 16 1 +70 16 1 +23 16 1 +84 16 1 +37 16 1 +49 16 1 +73 16 1 +26 16 4 +55 16 1 +79 16 4 +32 16 1 +44 16 1 +68 16 4 +21 16 1 +58 16 1 +11 16 1 +94 17 1 +5 17 4 +8 17 1 +17 17 1 +91 17 4 +14 17 1 +93 17 4 +24 17 4 +13 17 1 +12 17 4 +9 17 4 +4 17 4 +15 17 1 +5 18 1 +94 18 4 +17 18 4 +13 18 4 +24 18 1 +12 18 1 +9 18 1 +4 18 1 +15 18 4 +14 18 4 +93 19 4 +91 19 4 +17 20 1 +14 20 1 +24 20 4 +13 20 1 +12 20 4 +15 20 1 +5 20 4 +2 21 4 +3 21 4 +92 21 1 +0 21 4 +1 21 1 +10 21 1 +92 24 4 +38 24 4 +29 24 1 +40 24 1 +35 24 4 +34 24 1 +57 24 4 +41 24 4 +18 24 1 +28 24 1 +42 24 1 +19 24 4 +16 24 4 +20 24 1 +43 24 1 +2 24 1 +25 24 4 +3 24 1 +0 24 1 +33 24 4 +10 24 4 +56 24 1 +22 24 4 +1 24 4 +90 24 1 +89 24 1 +88 24 4 +87 24 1 +83 24 4 +76 24 4 +75 24 4 +86 24 4 +85 24 1 +82 24 1 +81 24 1 +80 24 1 +71 24 1 +72 24 4 +65 24 1 +69 24 4 +64 24 4 +63 24 4 +67 24 1 +66 24 1 +60 24 4 +59 24 4 +62 24 4 +61 24 1 +58 24 1 +52 24 1 +51 24 1 +48 24 4 +47 24 4 +39 24 4 +36 24 1 +54 24 4 +78 24 1 +31 24 4 +50 24 4 +74 24 4 +27 24 4 +53 24 4 +77 24 4 +30 24 4 +45 24 4 +46 24 1 +70 24 1 +23 24 1 +84 24 1 +37 24 1 +49 24 1 +73 24 1 +26 24 4 +55 24 1 +79 24 4 +32 24 1 +44 24 1 +68 24 4 +21 24 1 +17 25 1 +15 25 1 +13 26 1 +17 26 1 +12 26 4 +15 26 1 +5 26 4 +13 27 1 +5 27 4 +24 28 1 +17 29 4 +28 30 1 +18 30 1 +28 31 4 +16 31 1 +2 31 4 +3 31 4 +92 31 1 +0 31 4 +1 31 1 +18 31 4 +10 31 1 +28 35 1 +28 36 4 +16 36 1 +2 36 4 +3 36 4 +92 36 1 +0 36 4 +1 36 1 +18 36 4 +10 36 1 +19 36 1 +29 37 1 +40 37 1 +28 37 1 +16 37 4 +20 37 1 +2 37 1 +3 37 1 +92 37 4 +0 37 1 +22 37 4 +33 37 4 +10 37 4 +19 37 4 +18 37 1 +1 37 4 +29 39 4 +40 39 4 +28 39 4 +16 39 1 +2 39 4 +3 39 4 +92 39 1 +0 39 4 +33 39 1 +10 39 1 +22 39 1 +18 39 4 +1 39 1 +19 39 1 +92 40 4 +38 40 4 +29 40 1 +40 40 1 +35 40 4 +34 40 1 +57 40 4 +41 40 4 +18 40 1 +28 40 1 +42 40 1 +19 40 4 +16 40 4 +20 40 1 +43 40 1 +2 40 1 +25 40 4 +3 40 1 +0 40 1 +33 40 4 +10 40 4 +56 40 1 +22 40 4 +1 40 4 +90 40 1 +89 40 1 +88 40 4 +87 40 1 +83 40 4 +76 40 4 +75 40 4 +85 40 1 +82 40 1 +81 40 1 +80 40 1 +71 40 1 +72 40 4 +65 40 1 +69 40 4 +64 40 4 +68 40 4 +63 40 4 +67 40 1 +66 40 1 +60 40 4 +59 40 4 +62 40 4 +61 40 1 +58 40 1 +52 40 1 +51 40 1 +48 40 4 +47 40 4 +44 40 1 +70 40 1 +23 40 1 +46 40 1 +86 40 4 +39 40 4 +55 40 1 +79 40 4 +32 40 1 +36 40 1 +50 40 4 +74 40 4 +27 40 4 +54 40 4 +78 40 1 +31 40 4 +84 40 1 +37 40 1 +49 40 1 +73 40 1 +26 40 4 +53 40 4 +77 40 4 +30 40 4 +45 40 4 +33 41 4 +40 41 1 +29 41 1 +92 42 1 +38 42 1 +29 42 4 +40 42 4 +35 42 1 +34 42 4 +57 42 1 +41 42 1 +18 42 4 +28 42 4 +42 42 4 +19 42 1 +16 42 1 +20 42 4 +43 42 4 +2 42 4 +25 42 1 +3 42 4 +0 42 4 +33 42 1 +10 42 1 +56 42 4 +22 42 1 +1 42 1 +90 42 4 +89 42 4 +88 42 1 +87 42 4 +83 42 1 +76 42 1 +75 42 1 +85 42 4 +82 42 4 +81 42 4 +80 42 4 +71 42 4 +72 42 1 +70 42 4 +65 42 4 +69 42 1 +64 42 1 +68 42 1 +63 42 1 +67 42 4 +66 42 4 +60 42 1 +59 42 1 +61 42 4 +58 42 4 +52 42 4 +51 42 4 +48 42 1 +47 42 1 +44 42 4 +46 42 4 +45 42 1 +62 42 1 +86 42 1 +39 42 1 +55 42 4 +79 42 1 +32 42 4 +50 42 1 +74 42 1 +27 42 1 +36 42 4 +54 42 1 +78 42 4 +31 42 1 +84 42 4 +37 42 4 +49 42 4 +73 42 4 +26 42 1 +53 42 1 +77 42 1 +30 42 1 +29 44 4 +40 44 4 +28 44 4 +16 44 1 +20 44 4 +2 44 4 +25 44 1 +3 44 4 +92 44 1 +0 44 4 +10 44 1 +33 44 1 +22 44 1 +19 44 1 +18 44 4 +1 44 1 +92 46 4 +38 46 4 +29 46 1 +40 46 1 +35 46 4 +34 46 1 +57 46 4 +41 46 4 +18 46 1 +28 46 1 +42 46 1 +19 46 4 +16 46 4 +20 46 1 +43 46 1 +2 46 1 +25 46 4 +3 46 1 +0 46 1 +33 46 4 +10 46 4 +56 46 1 +22 46 4 +1 46 4 +90 46 1 +89 46 1 +88 46 4 +87 46 1 +76 46 4 +75 46 4 +85 46 1 +82 46 1 +81 46 1 +74 46 4 +80 46 1 +77 46 4 +71 46 1 +73 46 1 +72 46 4 +70 46 1 +65 46 1 +69 46 4 +64 46 4 +68 46 4 +63 46 4 +67 46 1 +66 46 1 +61 46 1 +58 46 1 +53 46 4 +52 46 1 +51 46 1 +50 46 4 +49 46 1 +44 46 1 +46 46 1 +45 46 4 +62 46 4 +86 46 4 +39 46 4 +48 46 4 +60 46 4 +84 46 1 +37 46 1 +55 46 1 +79 46 4 +32 46 1 +47 46 4 +59 46 4 +83 46 4 +36 46 1 +54 46 4 +78 46 1 +31 46 4 +92 47 4 +38 47 4 +29 47 1 +40 47 1 +35 47 4 +34 47 1 +57 47 4 +41 47 4 +18 47 1 +28 47 1 +42 47 1 +19 47 4 +16 47 4 +20 47 1 +43 47 1 +2 47 1 +25 47 4 +3 47 1 +0 47 1 +33 47 4 +10 47 4 +56 47 1 +22 47 4 +1 47 4 +90 47 1 +89 47 1 +88 47 4 +87 47 1 +83 47 4 +76 47 4 +75 47 4 +85 47 1 +82 47 1 +81 47 1 +80 47 1 +77 47 4 +71 47 1 +72 47 4 +70 47 1 +65 47 1 +69 47 4 +64 47 4 +68 47 4 +63 47 4 +67 47 1 +66 47 1 +60 47 4 +61 47 1 +58 47 1 +53 47 4 +52 47 1 +51 47 1 +48 47 4 +44 47 1 +46 47 1 +45 47 4 +62 47 4 +86 47 4 +39 47 4 +55 47 1 +79 47 4 +32 47 1 +50 47 4 +74 47 4 +27 47 4 +47 47 4 +59 47 4 +36 47 1 +54 47 4 +78 47 1 +31 47 4 +84 47 1 +37 47 1 +49 47 1 +73 47 1 +26 47 4 +27 48 4 +40 51 1 +33 51 4 +36 53 1 +92 54 4 +38 54 4 +29 54 1 +40 54 1 +35 54 4 +34 54 1 +57 54 4 +41 54 4 +18 54 1 +28 54 1 +42 54 1 +19 54 4 +16 54 4 +20 54 1 +43 54 1 +2 54 1 +25 54 4 +3 54 1 +0 54 1 +33 54 4 +10 54 4 +56 54 1 +22 54 4 +1 54 4 +90 54 1 +89 54 1 +88 54 4 +87 54 1 +83 54 4 +76 54 4 +75 54 4 +85 54 1 +82 54 1 +81 54 1 +74 54 4 +80 54 1 +78 54 1 +77 54 4 +71 54 1 +73 54 1 +72 54 4 +70 54 1 +65 54 1 +69 54 4 +64 54 4 +68 54 4 +63 54 4 +66 54 1 +59 54 4 +61 54 1 +58 54 1 +54 54 4 +53 54 4 +52 54 1 +51 54 1 +49 54 1 +47 54 4 +67 54 1 +44 54 1 +46 54 1 +45 54 4 +50 54 4 +62 54 4 +86 54 4 +39 54 4 +48 54 4 +60 54 4 +84 54 1 +37 54 1 +55 54 1 +79 54 4 +32 54 1 +39 55 1 +39 56 4 +32 56 1 +40 57 1 +29 58 1 +40 58 1 +35 58 4 +34 58 1 +28 58 1 +16 58 4 +20 58 1 +43 58 1 +2 58 1 +25 58 4 +3 58 1 +33 58 4 +10 58 4 +92 58 4 +0 58 1 +22 58 4 +19 58 4 +1 58 4 +18 58 1 +43 59 1 +35 59 4 +43 60 1 +92 63 1 +38 63 1 +29 63 4 +40 63 4 +35 63 1 +34 63 4 +57 63 1 +41 63 1 +18 63 4 +28 63 4 +42 63 4 +19 63 1 +16 63 1 +20 63 4 +43 63 4 +2 63 4 +25 63 1 +3 63 4 +0 63 4 +33 63 1 +10 63 1 +56 63 4 +90 63 4 +89 63 4 +88 63 1 +87 63 4 +83 63 1 +76 63 1 +75 63 1 +86 63 1 +85 63 4 +84 63 4 +82 63 4 +81 63 4 +74 63 1 +80 63 4 +79 63 1 +78 63 4 +77 63 1 +71 63 4 +73 63 4 +72 63 1 +70 63 4 +65 63 4 +64 63 1 +63 63 1 +66 63 4 +60 63 1 +59 63 1 +62 63 1 +61 63 4 +58 63 4 +55 63 4 +54 63 1 +53 63 1 +52 63 4 +51 63 4 +50 63 1 +49 63 4 +1 63 1 +48 63 1 +47 63 1 +67 63 4 +44 63 4 +22 63 1 +69 63 1 +46 63 4 +68 63 1 +45 63 1 +38 64 1 +29 64 4 +40 64 4 +35 64 1 +34 64 4 +57 64 1 +41 64 1 +18 64 4 +28 64 4 +16 64 1 +2 64 4 +25 64 1 +3 64 4 +20 64 4 +43 64 4 +33 64 1 +10 64 1 +56 64 4 +1 64 1 +92 64 1 +0 64 4 +22 64 1 +19 64 1 +57 65 1 +56 65 4 +41 65 1 +57 69 4 +56 69 1 +92 71 1 +38 71 1 +29 71 4 +40 71 4 +35 71 1 +10 71 1 +57 71 1 +34 71 4 +18 71 4 +41 71 1 +28 71 4 +19 71 1 +42 71 4 +16 71 1 +43 71 4 +20 71 4 +25 71 1 +2 71 4 +3 71 4 +0 71 4 +56 71 4 +33 71 1 +22 71 1 +1 71 1 +92 73 1 +38 73 1 +29 73 4 +40 73 4 +35 73 1 +34 73 4 +57 73 1 +41 73 1 +18 73 4 +28 73 4 +42 73 4 +19 73 1 +16 73 1 +20 73 4 +43 73 4 +2 73 4 +25 73 1 +3 73 4 +0 73 4 +10 73 1 +56 73 4 +90 73 4 +89 73 4 +88 73 1 +87 73 4 +83 73 1 +76 73 1 +75 73 1 +86 73 1 +85 73 4 +84 73 4 +82 73 4 +81 73 4 +74 73 1 +33 73 1 +80 73 4 +79 73 1 +78 73 4 +77 73 1 +73 73 4 +72 73 1 +70 73 4 +65 73 4 +64 73 1 +63 73 1 +67 73 4 +66 73 4 +60 73 1 +61 73 4 +55 73 4 +54 73 1 +53 73 1 +52 73 4 +59 73 1 +71 73 4 +1 73 1 +48 73 1 +62 73 1 +51 73 4 +22 73 1 +69 73 1 +46 73 4 +68 73 1 +45 73 1 +92 74 1 +38 74 1 +29 74 4 +40 74 4 +35 74 1 +34 74 4 +57 74 1 +41 74 1 +18 74 4 +28 74 4 +42 74 4 +19 74 1 +16 74 1 +20 74 4 +43 74 4 +2 74 4 +25 74 1 +3 74 4 +0 74 4 +10 74 1 +56 74 4 +90 74 4 +89 74 4 +88 74 1 +87 74 4 +83 74 1 +76 74 1 +75 74 1 +86 74 1 +85 74 4 +84 74 4 +82 74 4 +81 74 4 +74 74 1 +33 74 1 +80 74 4 +79 74 1 +78 74 4 +77 74 1 +71 74 4 +73 74 4 +72 74 1 +70 74 4 +65 74 4 +64 74 1 +63 74 1 +66 74 4 +60 74 1 +59 74 1 +61 74 4 +54 74 1 +53 74 1 +52 74 4 +1 74 1 +48 74 1 +55 74 4 +67 74 4 +44 74 4 +62 74 1 +51 74 4 +22 74 1 +69 74 1 +46 74 4 +68 74 1 +45 74 1 +92 75 1 +38 75 1 +29 75 4 +40 75 4 +35 75 1 +34 75 4 +57 75 1 +41 75 1 +18 75 4 +28 75 4 +42 75 4 +19 75 1 +16 75 1 +20 75 4 +43 75 4 +2 75 4 +3 75 4 +0 75 4 +10 75 1 +56 75 4 +90 75 4 +89 75 4 +88 75 1 +87 75 4 +83 75 1 +76 75 1 +75 75 1 +86 75 1 +85 75 4 +84 75 4 +82 75 4 +81 75 4 +74 75 1 +33 75 1 +80 75 4 +79 75 1 +78 75 4 +73 75 4 +25 75 1 +72 75 1 +70 75 4 +65 75 4 +22 75 1 +69 75 1 +64 75 1 +63 75 1 +67 75 4 +66 75 4 +60 75 1 +61 75 4 +55 75 4 +77 75 1 +54 75 1 +52 75 4 +62 75 1 +51 75 4 +53 75 1 +59 75 1 +71 75 4 +1 75 1 +48 75 1 +68 75 1 +45 75 1 +92 76 1 +38 76 1 +29 76 4 +40 76 4 +35 76 1 +34 76 4 +57 76 1 +41 76 1 +18 76 4 +28 76 4 +42 76 4 +19 76 1 +16 76 1 +20 76 4 +43 76 4 +2 76 4 +3 76 4 +0 76 4 +10 76 1 +56 76 4 +90 76 4 +89 76 4 +88 76 1 +87 76 4 +83 76 1 +76 76 1 +75 76 1 +86 76 1 +85 76 4 +84 76 4 +82 76 4 +81 76 4 +74 76 1 +33 76 1 +80 76 4 +79 76 1 +73 76 4 +25 76 1 +72 76 1 +70 76 4 +65 76 4 +22 76 1 +69 76 1 +64 76 1 +68 76 1 +63 76 1 +67 76 4 +60 76 1 +61 76 4 +66 76 4 +78 76 4 +55 76 4 +77 76 1 +54 76 1 +52 76 4 +62 76 1 +51 76 4 +53 76 1 +59 76 1 +71 76 4 +1 76 1 +48 76 1 +58 78 1 +50 78 4 +49 78 1 +90 79 1 +89 79 1 +88 79 4 +87 79 1 +83 79 4 +76 79 4 +75 79 4 +86 79 4 +85 79 1 +84 79 1 +82 79 1 +81 79 1 +74 79 4 +80 79 1 +79 79 4 +71 79 1 +73 79 1 +72 79 4 +70 79 1 +65 79 1 +69 79 4 +64 79 4 +68 79 4 +63 79 4 +67 79 1 +60 79 4 +59 79 4 +61 79 1 +66 79 1 +78 79 1 +55 79 1 +77 79 4 +54 79 4 +53 79 4 +52 79 1 +62 79 4 +51 79 1 +58 81 1 +50 81 4 +58 82 1 +66 83 4 +60 83 1 +59 83 1 +90 84 1 +89 84 1 +88 84 4 +87 84 1 +83 84 4 +76 84 4 +75 84 4 +86 84 4 +85 84 1 +84 84 1 +82 84 1 +81 84 1 +74 84 4 +80 84 1 +79 84 4 +71 84 1 +73 84 1 +72 84 4 +70 84 1 +65 84 1 +69 84 4 +68 84 4 +67 84 1 +62 84 4 +61 84 1 +78 84 1 +55 84 1 +77 84 4 +54 84 4 +64 84 4 +53 84 4 +63 84 4 +52 84 1 +69 85 1 +64 85 1 +61 85 4 +90 86 1 +89 86 1 +88 86 4 +87 86 1 +83 86 4 +76 86 4 +75 86 4 +84 86 1 +82 86 1 +81 86 1 +74 86 4 +80 86 1 +79 86 4 +71 86 1 +72 86 4 +70 86 1 +68 86 4 +86 86 4 +63 86 4 +67 86 1 +73 86 1 +85 86 1 +62 86 4 +78 86 1 +55 86 1 +65 86 1 +77 86 4 +54 86 4 +53 86 4 +90 87 1 +89 87 1 +88 87 4 +87 87 1 +83 87 4 +76 87 4 +75 87 4 +84 87 1 +82 87 1 +81 87 1 +74 87 4 +80 87 1 +79 87 4 +71 87 1 +72 87 4 +70 87 1 +68 87 4 +86 87 4 +63 87 4 +67 87 1 +73 87 1 +85 87 1 +62 87 4 +78 87 1 +55 87 1 +65 87 1 +77 87 4 +54 87 4 +90 88 1 +89 88 1 +87 88 1 +83 88 4 +75 88 4 +78 88 1 +77 88 4 +82 88 1 +71 88 1 +84 88 1 +73 88 1 +72 88 4 +70 88 1 +76 88 4 +65 88 1 +88 89 4 +63 89 4 +86 89 4 +81 89 1 +80 89 1 +79 89 4 +68 89 4 +62 89 4 +85 89 1 +74 89 4 +67 89 1 +55 89 1 +88 90 1 +86 90 1 +81 90 4 +80 90 4 +79 90 1 +68 90 1 +85 90 4 +74 90 1 +63 90 1 +67 90 4 +62 90 1 +57 92 4 +66 95 1 +66 96 4 +59 96 1 +69 98 1 +64 98 1 +68 99 4 +63 99 4 +88 100 1 +86 100 1 +81 100 4 +80 100 4 +85 100 4 +74 100 1 +79 100 1 +67 100 4 +68 101 4 +69 103 1 +90 104 1 +89 104 1 +87 104 1 +76 104 4 +75 104 4 +78 104 1 +82 104 1 +71 104 1 +84 104 1 +73 104 1 +77 104 4 +83 104 4 +72 104 4 +70 104 1 +88 107 1 +86 107 1 +81 107 4 +74 107 1 +85 107 4 +80 107 4 +79 107 1 +90 111 4 +89 111 4 +83 111 1 +87 111 4 +76 111 1 +75 111 1 +71 111 4 +82 111 4 +77 111 1 +73 111 4 +84 112 1 +78 112 1 +72 112 4 +90 113 4 +89 113 4 +83 113 1 +87 113 4 +76 113 1 +75 113 1 +82 113 4 +77 113 1 +73 114 4 +84 115 1 +78 115 1 +81 117 4 +74 117 1 +81 118 1 +76 119 1 +75 119 1 +76 121 4 +90 123 1 +89 123 1 +83 123 4 +87 123 1 +76 123 4 +75 123 4 +82 123 1 +84 125 1 +86 126 4 +81 126 1 +85 126 1 +80 126 1 +74 126 4 +88 127 1 +86 128 4 +85 128 1 +90 130 4 +89 130 4 +76 130 1 +87 130 4 +83 130 1 +75 130 1 +90 131 1 +89 131 1 +87 132 4 +76 132 1 +75 132 1 +86 135 1 +90 140 1 +91 142 4 +192 144 4 +182 144 4 +179 144 1 +176 144 1 +175 144 4 +185 144 4 +184 144 1 +183 144 4 +173 144 1 +181 144 1 +180 144 1 +178 144 4 +174 144 4 +177 144 1 +172 144 4 +171 144 1 +170 144 4 +169 144 1 +168 144 4 +162 144 1 +164 144 4 +167 144 1 +148 144 1 +160 144 4 +156 144 1 +157 144 4 +147 144 1 +163 144 4 +159 144 1 +155 144 1 +144 144 4 +153 144 1 +152 144 4 +151 144 4 +150 144 1 +149 144 1 +146 144 4 +136 144 1 +141 144 4 +145 144 4 +132 144 4 +135 144 4 +124 144 1 +140 144 1 +129 144 4 +128 144 1 +139 144 4 +123 144 4 +158 144 1 +111 144 4 +134 144 1 +133 144 4 +131 144 4 +127 144 1 +126 144 4 +138 144 1 +115 144 4 +154 144 4 +107 144 4 +130 144 1 +142 144 1 +143 144 1 +96 144 1 +166 144 1 +119 144 1 +125 144 1 +137 144 4 +161 144 4 +114 144 1 +113 144 4 +117 144 4 +165 144 4 +118 144 1 +192 146 1 +182 146 1 +179 146 4 +176 146 4 +175 146 1 +185 146 1 +184 146 4 +183 146 1 +173 146 4 +181 146 4 +180 146 4 +178 146 1 +174 146 1 +177 146 4 +172 146 1 +171 146 4 +170 146 1 +169 146 4 +168 146 1 +162 146 4 +164 146 1 +167 146 4 +148 146 4 +160 146 1 +156 146 4 +157 146 1 +147 146 4 +163 146 1 +159 146 4 +155 146 4 +144 146 1 +153 146 4 +152 146 1 +151 146 1 +150 146 4 +149 146 4 +146 146 1 +136 146 4 +141 146 1 +145 146 1 +132 146 1 +143 146 4 +135 146 1 +124 146 4 +129 146 1 +128 146 4 +139 146 1 +123 146 1 +158 146 4 +111 146 1 +134 146 4 +133 146 1 +131 146 1 +127 146 4 +126 146 1 +138 146 4 +115 146 1 +154 146 1 +107 146 1 +130 146 4 +142 146 4 +166 146 4 +119 146 4 +125 146 4 +137 146 1 +161 146 1 +114 146 4 +165 146 1 +118 146 4 +113 146 1 +140 146 4 +117 146 1 +108 148 4 +101 148 1 +97 148 4 +108 149 4 +101 149 1 +101 150 1 +97 150 4 +108 150 4 +109 150 4 +98 150 1 +100 150 1 +109 151 1 +100 151 4 +122 153 1 +104 153 4 +106 153 4 +120 153 1 +116 153 4 +121 153 4 +99 153 4 +110 153 1 +103 153 4 +112 153 1 +102 153 1 +122 154 1 +104 154 4 +106 154 4 +120 154 1 +116 154 4 +121 154 4 +110 154 1 +103 154 4 +112 154 1 +102 154 1 +109 155 1 +108 156 1 +121 157 1 +116 157 1 +112 157 4 +120 157 4 +110 157 4 +122 158 1 +104 158 4 +106 158 4 +103 158 4 +122 159 1 +106 159 4 +104 159 4 +122 161 1 +106 162 4 +105 165 1 +192 167 1 +182 167 1 +179 167 4 +176 167 4 +175 167 1 +185 167 1 +184 167 4 +183 167 1 +173 167 4 +181 167 4 +180 167 4 +178 167 1 +174 167 1 +177 167 4 +172 167 1 +171 167 4 +170 167 1 +169 167 4 +168 167 1 +162 167 4 +164 167 1 +167 167 4 +148 167 4 +160 167 1 +156 167 4 +157 167 1 +147 167 4 +163 167 1 +159 167 4 +155 167 4 +154 167 1 +144 167 1 +153 167 4 +152 167 1 +151 167 1 +150 167 4 +149 167 4 +146 167 1 +136 167 4 +141 167 1 +145 167 1 +132 167 1 +143 167 4 +135 167 1 +124 167 4 +129 167 1 +128 167 4 +139 167 1 +123 167 1 +158 167 4 +111 167 1 +134 167 4 +133 167 1 +131 167 1 +127 167 4 +126 167 1 +138 167 4 +115 167 1 +140 167 4 +117 167 1 +130 167 4 +142 167 4 +166 167 4 +119 167 4 +125 167 4 +137 167 1 +161 167 1 +114 167 4 +165 167 1 +118 167 4 +113 167 1 +120 171 4 +121 171 1 +116 171 1 +112 172 4 +124 173 4 +111 173 1 +124 174 4 +192 177 1 +182 177 1 +179 177 4 +176 177 4 +175 177 1 +185 177 1 +184 177 4 +183 177 1 +173 177 4 +181 177 4 +180 177 4 +178 177 1 +174 177 1 +177 177 4 +172 177 1 +171 177 4 +170 177 1 +169 177 4 +168 177 1 +162 177 4 +164 177 1 +167 177 4 +148 177 4 +160 177 1 +156 177 4 +157 177 1 +147 177 4 +163 177 1 +159 177 4 +155 177 4 +154 177 1 +144 177 1 +153 177 4 +152 177 1 +151 177 1 +150 177 4 +149 177 4 +146 177 1 +136 177 4 +141 177 1 +145 177 1 +132 177 1 +143 177 4 +135 177 1 +124 177 4 +129 177 1 +128 177 4 +139 177 1 +123 177 1 +158 177 4 +111 177 1 +134 177 4 +133 177 1 +131 177 1 +127 177 4 +161 177 1 +114 177 4 +137 177 1 +126 177 1 +138 177 4 +115 177 1 +165 177 1 +118 177 4 +140 177 4 +117 177 1 +130 177 4 +142 177 4 +166 177 4 +119 177 4 +125 177 4 +192 178 1 +182 178 1 +179 178 4 +176 178 4 +175 178 1 +185 178 1 +184 178 4 +183 178 1 +173 178 4 +181 178 4 +180 178 4 +178 178 1 +174 178 1 +177 178 4 +172 178 1 +171 178 4 +170 178 1 +169 178 4 +168 178 1 +162 178 4 +164 178 1 +167 178 4 +148 178 4 +160 178 1 +156 178 4 +157 178 1 +147 178 4 +163 178 1 +161 178 1 +159 178 4 +155 178 4 +154 178 1 +144 178 1 +153 178 4 +152 178 1 +151 178 1 +150 178 4 +149 178 4 +146 178 1 +136 178 4 +141 178 1 +145 178 1 +132 178 1 +143 178 4 +135 178 1 +124 178 4 +129 178 1 +128 178 4 +139 178 1 +123 178 1 +158 178 4 +111 178 1 +134 178 4 +133 178 1 +131 178 1 +115 178 1 +138 178 4 +127 178 4 +125 178 4 +137 178 1 +126 178 1 +130 178 4 +142 178 4 +166 178 4 +119 178 4 +165 178 1 +118 178 4 +140 178 4 +117 178 1 +192 179 4 +182 179 4 +179 179 1 +176 179 1 +175 179 4 +185 179 4 +184 179 1 +183 179 4 +173 179 1 +181 179 1 +180 179 1 +178 179 4 +174 179 4 +177 179 1 +169 179 1 +168 179 4 +162 179 1 +164 179 4 +167 179 1 +148 179 1 +160 179 4 +156 179 1 +157 179 4 +147 179 1 +166 179 1 +165 179 4 +163 179 4 +161 179 4 +159 179 1 +155 179 1 +154 179 4 +144 179 4 +153 179 1 +152 179 4 +150 179 1 +136 179 1 +141 179 4 +145 179 4 +132 179 4 +143 179 1 +135 179 4 +171 179 1 +124 179 1 +140 179 1 +129 179 4 +151 179 4 +128 179 1 +139 179 4 +146 179 4 +170 179 4 +123 179 4 +158 179 1 +111 179 4 +134 179 1 +142 179 1 +133 179 4 +138 179 1 +149 179 1 +126 179 4 +172 179 4 +125 179 1 +192 181 1 +182 181 1 +179 181 4 +176 181 4 +175 181 1 +185 181 1 +184 181 4 +183 181 1 +173 181 4 +181 181 4 +180 181 4 +178 181 1 +174 181 1 +177 181 4 +172 181 1 +169 181 4 +168 181 1 +164 181 1 +167 181 4 +148 181 4 +160 181 1 +156 181 4 +157 181 1 +147 181 4 +166 181 4 +165 181 1 +163 181 1 +161 181 1 +159 181 4 +155 181 4 +154 181 1 +144 181 1 +153 181 4 +152 181 1 +151 181 1 +150 181 4 +136 181 4 +141 181 1 +145 181 1 +132 181 1 +143 181 4 +135 181 1 +171 181 4 +124 181 4 +140 181 4 +129 181 1 +128 181 4 +139 181 1 +146 181 1 +170 181 1 +123 181 1 +134 181 4 +158 181 4 +111 181 1 +133 181 1 +142 181 4 +138 181 4 +162 181 4 +115 181 1 +149 181 4 +126 181 1 +125 181 4 +121 182 4 +120 182 1 +130 183 1 +192 184 1 +182 184 1 +179 184 4 +176 184 4 +175 184 1 +185 184 1 +184 184 4 +183 184 1 +173 184 4 +181 184 4 +180 184 4 +178 184 1 +174 184 1 +177 184 4 +172 184 1 +171 184 4 +170 184 1 +169 184 4 +168 184 1 +162 184 4 +164 184 1 +167 184 4 +148 184 4 +160 184 1 +156 184 4 +157 184 1 +147 184 4 +163 184 1 +161 184 1 +159 184 4 +155 184 4 +154 184 1 +144 184 1 +153 184 4 +152 184 1 +151 184 1 +150 184 4 +149 184 4 +146 184 1 +136 184 4 +141 184 1 +145 184 1 +132 184 1 +143 184 4 +135 184 1 +124 184 4 +140 184 4 +129 184 1 +128 184 4 +139 184 1 +123 184 1 +158 184 4 +111 184 1 +134 184 4 +133 184 1 +166 184 4 +119 184 4 +142 184 4 +131 184 1 +115 184 1 +138 184 4 +127 184 4 +137 184 1 +126 184 1 +125 184 4 +165 184 1 +118 184 4 +131 185 1 +137 185 1 +127 185 4 +192 186 1 +182 186 1 +179 186 4 +176 186 4 +175 186 1 +185 186 1 +184 186 4 +183 186 1 +173 186 4 +181 186 4 +180 186 4 +178 186 1 +174 186 1 +177 186 4 +172 186 1 +171 186 4 +169 186 4 +168 186 1 +164 186 1 +167 186 4 +148 186 4 +160 186 1 +156 186 4 +157 186 1 +147 186 4 +165 186 1 +163 186 1 +161 186 1 +159 186 4 +155 186 4 +154 186 1 +144 186 1 +153 186 4 +152 186 1 +151 186 1 +150 186 4 +136 186 4 +141 186 1 +145 186 1 +132 186 1 +143 186 4 +135 186 1 +124 186 4 +140 186 4 +129 186 1 +128 186 4 +139 186 1 +146 186 1 +170 186 1 +123 186 1 +134 186 4 +158 186 4 +111 186 1 +133 186 1 +166 186 4 +119 186 4 +142 186 4 +138 186 4 +162 186 4 +115 186 1 +149 186 4 +126 186 1 +125 186 4 +121 188 4 +124 192 4 +123 192 1 +111 192 1 +192 194 1 +182 194 1 +179 194 4 +176 194 4 +175 194 1 +185 194 1 +184 194 4 +183 194 1 +173 194 4 +181 194 4 +180 194 4 +178 194 1 +174 194 1 +177 194 4 +172 194 1 +169 194 4 +168 194 1 +162 194 4 +164 194 1 +167 194 4 +148 194 4 +160 194 1 +156 194 4 +157 194 1 +147 194 4 +166 194 4 +165 194 1 +163 194 1 +161 194 1 +159 194 4 +155 194 4 +154 194 1 +144 194 1 +153 194 4 +150 194 4 +136 194 4 +141 194 1 +145 194 1 +132 194 1 +143 194 4 +135 194 1 +171 194 4 +124 194 4 +140 194 4 +152 194 1 +129 194 1 +151 194 1 +128 194 4 +139 194 1 +146 194 1 +170 194 1 +123 194 1 +158 194 4 +111 194 1 +134 194 4 +142 194 4 +133 194 1 +138 194 4 +149 194 4 +126 194 1 +192 196 4 +182 196 4 +179 196 1 +176 196 1 +175 196 4 +185 196 4 +184 196 1 +183 196 4 +173 196 1 +181 196 1 +180 196 1 +178 196 4 +174 196 4 +177 196 1 +172 196 4 +169 196 1 +168 196 4 +162 196 1 +164 196 4 +167 196 1 +148 196 1 +160 196 4 +156 196 1 +157 196 4 +147 196 1 +166 196 1 +165 196 4 +163 196 4 +161 196 4 +159 196 1 +155 196 1 +154 196 4 +144 196 4 +153 196 1 +150 196 1 +149 196 1 +136 196 1 +141 196 4 +145 196 4 +132 196 4 +143 196 1 +135 196 4 +171 196 1 +124 196 1 +140 196 1 +152 196 4 +129 196 4 +151 196 4 +128 196 1 +139 196 4 +133 196 4 +134 196 1 +158 196 1 +111 196 4 +146 196 4 +170 196 4 +123 196 4 +142 196 1 +138 196 1 +137 197 1 +131 197 1 +192 198 1 +182 198 1 +179 198 4 +175 198 1 +185 198 1 +184 198 4 +173 198 4 +181 198 4 +180 198 4 +178 198 1 +174 198 1 +177 198 4 +172 198 1 +171 198 4 +170 198 1 +169 198 4 +168 198 1 +162 198 4 +164 198 1 +167 198 4 +148 198 4 +160 198 1 +156 198 4 +157 198 1 +147 198 4 +161 198 1 +155 198 4 +154 198 1 +144 198 1 +153 198 4 +151 198 1 +150 198 4 +149 198 4 +159 198 4 +183 198 1 +136 198 4 +141 198 1 +145 198 1 +146 198 1 +158 198 4 +135 198 1 +163 198 1 +140 198 4 +152 198 1 +176 198 4 +129 198 1 +165 198 1 +142 198 4 +166 198 4 +143 198 4 +192 199 1 +182 199 1 +179 199 4 +176 199 4 +175 199 1 +185 199 1 +184 199 4 +173 199 4 +181 199 4 +180 199 4 +178 199 1 +174 199 1 +177 199 4 +172 199 1 +169 199 4 +168 199 1 +162 199 4 +164 199 1 +167 199 4 +148 199 4 +160 199 1 +156 199 4 +157 199 1 +147 199 4 +166 199 4 +165 199 1 +163 199 1 +161 199 1 +155 199 4 +154 199 1 +144 199 1 +153 199 4 +150 199 4 +149 199 4 +159 199 4 +183 199 1 +136 199 4 +141 199 1 +111 199 1 +158 199 4 +135 199 1 +171 199 4 +124 199 4 +140 199 4 +152 199 1 +129 199 1 +151 199 1 +128 199 4 +145 199 1 +146 199 1 +170 199 1 +123 199 1 +142 199 4 +143 199 4 +192 200 1 +182 200 1 +179 200 4 +176 200 4 +175 200 1 +185 200 1 +184 200 4 +173 200 4 +181 200 4 +180 200 4 +178 200 1 +174 200 1 +177 200 4 +172 200 1 +171 200 4 +170 200 1 +169 200 4 +168 200 1 +162 200 4 +164 200 1 +167 200 4 +148 200 4 +160 200 1 +156 200 4 +157 200 1 +147 200 4 +161 200 1 +155 200 4 +154 200 1 +144 200 1 +153 200 4 +152 200 1 +150 200 4 +149 200 4 +159 200 4 +183 200 1 +136 200 4 +141 200 1 +145 200 1 +146 200 1 +158 200 4 +135 200 1 +151 200 1 +163 200 1 +140 200 4 +165 200 1 +142 200 4 +166 200 4 +143 200 4 +137 203 1 +192 205 1 +182 205 1 +179 205 4 +176 205 4 +175 205 1 +185 205 1 +184 205 4 +183 205 1 +173 205 4 +178 205 1 +174 205 1 +177 205 4 +172 205 1 +169 205 4 +168 205 1 +162 205 4 +164 205 1 +167 205 4 +148 205 4 +160 205 1 +156 205 4 +157 205 1 +147 205 4 +166 205 4 +165 205 1 +163 205 1 +161 205 1 +159 205 4 +155 205 4 +154 205 1 +144 205 1 +153 205 4 +150 205 4 +149 205 4 +136 205 4 +141 205 1 +145 205 1 +143 205 4 +135 205 1 +171 205 4 +124 205 4 +140 205 4 +152 205 1 +129 205 1 +151 205 1 +128 205 4 +146 205 1 +170 205 1 +123 205 1 +181 205 4 +134 205 4 +158 205 4 +111 205 1 +180 205 4 +133 205 1 +142 205 4 +192 206 1 +182 206 1 +179 206 4 +176 206 4 +175 206 1 +185 206 1 +184 206 4 +183 206 1 +173 206 4 +181 206 4 +178 206 1 +174 206 1 +177 206 4 +172 206 1 +169 206 4 +168 206 1 +162 206 4 +164 206 1 +167 206 4 +148 206 4 +160 206 1 +156 206 4 +157 206 1 +147 206 4 +166 206 4 +165 206 1 +163 206 1 +161 206 1 +159 206 4 +155 206 4 +154 206 1 +144 206 1 +153 206 4 +150 206 4 +149 206 4 +136 206 4 +141 206 1 +145 206 1 +132 206 1 +143 206 4 +135 206 1 +171 206 4 +124 206 4 +140 206 4 +152 206 1 +129 206 1 +151 206 1 +128 206 4 +146 206 1 +170 206 1 +123 206 1 +134 206 4 +158 206 4 +111 206 1 +180 206 4 +133 206 1 +142 206 4 +192 207 1 +182 207 1 +179 207 4 +176 207 4 +175 207 1 +185 207 1 +184 207 4 +183 207 1 +173 207 4 +180 207 4 +178 207 1 +174 207 1 +177 207 4 +172 207 1 +169 207 4 +168 207 1 +162 207 4 +164 207 1 +167 207 4 +148 207 4 +160 207 1 +156 207 4 +157 207 1 +147 207 4 +166 207 4 +165 207 1 +163 207 1 +161 207 1 +155 207 4 +154 207 1 +144 207 1 +153 207 4 +150 207 4 +149 207 4 +159 207 4 +136 207 4 +141 207 1 +135 207 1 +171 207 4 +124 207 4 +140 207 4 +152 207 1 +129 207 1 +151 207 1 +128 207 4 +181 207 4 +134 207 4 +158 207 4 +111 207 1 +145 207 1 +146 207 1 +170 207 1 +123 207 1 +142 207 4 +143 207 4 +145 209 4 +140 209 1 +192 210 1 +182 210 1 +179 210 4 +176 210 4 +175 210 1 +185 210 1 +184 210 4 +173 210 4 +181 210 4 +180 210 4 +178 210 1 +174 210 1 +177 210 4 +172 210 1 +171 210 4 +170 210 1 +169 210 4 +168 210 1 +162 210 4 +164 210 1 +167 210 4 +148 210 4 +160 210 1 +156 210 4 +157 210 1 +147 210 4 +163 210 1 +161 210 1 +158 210 4 +155 210 4 +154 210 1 +144 210 1 +153 210 4 +152 210 1 +151 210 1 +150 210 4 +149 210 4 +146 210 1 +159 210 4 +183 210 1 +136 210 4 +141 210 1 +166 210 4 +143 210 4 +165 210 1 +142 210 4 +192 211 1 +182 211 1 +179 211 4 +176 211 4 +175 211 1 +185 211 1 +184 211 4 +183 211 1 +173 211 4 +181 211 4 +180 211 4 +178 211 1 +174 211 1 +177 211 4 +172 211 1 +171 211 4 +170 211 1 +169 211 4 +168 211 1 +162 211 4 +164 211 1 +167 211 4 +148 211 4 +160 211 1 +156 211 4 +157 211 1 +147 211 4 +163 211 1 +161 211 1 +158 211 4 +159 211 4 +155 211 4 +154 211 1 +144 211 1 +153 211 4 +152 211 1 +151 211 1 +150 211 4 +149 211 4 +146 211 1 +141 211 1 +166 211 4 +143 211 4 +165 211 1 +142 211 4 +192 214 1 +182 214 1 +179 214 4 +176 214 4 +175 214 1 +185 214 1 +184 214 4 +183 214 1 +173 214 4 +181 214 4 +180 214 4 +178 214 1 +174 214 1 +177 214 4 +172 214 1 +169 214 4 +168 214 1 +162 214 4 +164 214 1 +167 214 4 +148 214 4 +160 214 1 +156 214 4 +157 214 1 +147 214 4 +166 214 4 +165 214 1 +163 214 1 +161 214 1 +159 214 4 +155 214 4 +154 214 1 +144 214 1 +153 214 4 +150 214 4 +149 214 4 +136 214 4 +141 214 1 +145 214 1 +132 214 1 +143 214 4 +171 214 4 +124 214 4 +135 214 1 +152 214 1 +129 214 1 +140 214 4 +139 214 1 +151 214 1 +128 214 4 +146 214 1 +170 214 1 +123 214 1 +134 214 4 +158 214 4 +111 214 1 +133 214 1 +142 214 4 +145 216 1 +153 217 1 +152 217 4 +151 217 4 +150 217 1 +149 217 1 +192 218 1 +182 218 1 +179 218 4 +176 218 4 +175 218 1 +185 218 1 +184 218 4 +183 218 1 +173 218 4 +181 218 4 +180 218 4 +178 218 1 +174 218 1 +177 218 4 +172 218 1 +171 218 4 +168 218 1 +162 218 4 +164 218 1 +148 218 4 +160 218 1 +156 218 4 +157 218 1 +170 218 1 +147 218 4 +163 218 1 +161 218 1 +158 218 4 +159 218 4 +155 218 4 +154 218 1 +167 218 4 +144 218 1 +169 218 4 +146 218 1 +166 218 4 +143 218 4 +165 218 1 +142 218 4 +192 219 1 +182 219 1 +179 219 4 +176 219 4 +175 219 1 +185 219 1 +184 219 4 +183 219 1 +173 219 4 +181 219 4 +180 219 4 +178 219 1 +174 219 1 +172 219 1 +169 219 4 +168 219 1 +162 219 4 +164 219 1 +171 219 4 +148 219 4 +160 219 1 +156 219 4 +157 219 1 +163 219 1 +161 219 1 +170 219 1 +147 219 4 +158 219 4 +166 219 4 +167 219 4 +144 219 1 +155 219 4 +159 219 4 +165 219 1 +177 219 4 +154 219 1 +192 220 1 +182 220 1 +179 220 4 +176 220 4 +175 220 1 +185 220 1 +184 220 4 +183 220 1 +173 220 4 +181 220 4 +180 220 4 +178 220 1 +174 220 1 +177 220 4 +172 220 1 +169 220 4 +168 220 1 +162 220 4 +164 220 1 +171 220 4 +148 220 4 +160 220 1 +156 220 4 +157 220 1 +163 220 1 +161 220 1 +170 220 1 +147 220 4 +158 220 4 +166 220 4 +167 220 4 +144 220 1 +155 220 4 +159 220 4 +142 220 4 +165 220 1 +154 220 1 +192 221 1 +182 221 1 +179 221 4 +176 221 4 +175 221 1 +185 221 1 +184 221 4 +183 221 1 +173 221 4 +181 221 4 +180 221 4 +178 221 1 +174 221 1 +177 221 4 +172 221 1 +168 221 1 +162 221 4 +164 221 1 +171 221 4 +148 221 4 +160 221 1 +156 221 4 +157 221 1 +170 221 1 +147 221 4 +166 221 4 +163 221 1 +161 221 1 +158 221 4 +159 221 4 +155 221 4 +154 221 1 +167 221 4 +144 221 1 +169 221 4 +146 221 1 +165 221 1 +142 221 4 +192 223 1 +182 223 1 +179 223 4 +176 223 4 +175 223 1 +185 223 1 +184 223 4 +183 223 1 +173 223 4 +181 223 4 +180 223 4 +178 223 1 +174 223 1 +172 223 1 +169 223 4 +168 223 1 +162 223 4 +164 223 1 +167 223 4 +171 223 4 +148 223 4 +160 223 1 +156 223 4 +157 223 1 +163 223 1 +161 223 1 +170 223 1 +147 223 4 +158 223 4 +166 223 4 +155 223 4 +159 223 4 +165 223 1 +177 223 4 +154 223 1 +181 227 4 +178 227 1 +174 227 1 +177 227 4 +172 227 1 +169 227 4 +162 227 4 +164 227 1 +148 227 4 +160 227 1 +167 227 4 +168 227 1 +180 227 4 +157 227 1 +181 228 4 +178 228 1 +174 228 1 +177 228 4 +172 228 1 +162 228 4 +164 228 1 +148 228 4 +160 228 1 +167 228 4 +168 228 1 +180 228 4 +157 228 1 +169 228 4 +147 228 4 +181 229 4 +174 229 1 +177 229 4 +172 229 1 +169 229 4 +164 229 1 +160 229 1 +162 229 4 +178 229 1 +167 229 4 +168 229 1 +180 229 4 +157 229 1 +153 230 4 +152 230 1 +151 230 1 +150 230 4 +151 231 1 +151 232 1 +150 232 4 +153 235 4 +151 235 1 +150 235 4 +165 237 4 +166 237 1 +161 237 4 +158 237 1 +159 237 1 +192 238 1 +182 238 1 +179 238 4 +176 238 4 +175 238 1 +185 238 1 +184 238 4 +173 238 4 +181 238 4 +174 238 1 +177 238 4 +172 238 1 +169 238 4 +162 238 4 +164 238 1 +171 238 4 +148 238 4 +183 238 1 +160 238 1 +167 238 4 +156 238 4 +168 238 1 +180 238 4 +157 238 1 +170 238 1 +147 238 4 +163 238 1 +178 238 1 +155 238 4 +163 239 1 +192 240 1 +182 240 1 +179 240 4 +176 240 4 +175 240 1 +184 240 4 +173 240 4 +181 240 4 +178 240 1 +174 240 1 +177 240 4 +172 240 1 +185 240 1 +162 240 4 +164 240 1 +183 240 1 +160 240 1 +147 240 4 +170 240 1 +171 240 4 +148 240 4 +167 240 4 +156 240 4 +169 240 4 +168 240 1 +180 240 4 +157 240 1 +179 241 4 +176 241 4 +175 241 1 +185 241 1 +173 241 4 +184 241 4 +183 241 1 +182 241 1 +171 241 4 +192 241 1 +170 241 1 +181 242 1 +174 242 4 +177 242 1 +172 242 4 +168 242 4 +180 242 1 +169 242 1 +164 242 4 +162 242 1 +178 242 4 +167 242 1 +181 243 4 +174 243 1 +177 243 4 +172 243 1 +157 243 1 +180 243 4 +169 243 4 +168 243 1 +164 243 1 +162 243 4 +178 243 1 +167 243 4 +161 244 4 +158 244 1 +161 245 4 +166 246 4 +165 246 1 +169 249 1 +169 250 1 +162 250 1 +178 253 1 +174 253 1 +177 253 4 +172 253 1 +162 253 4 +169 253 4 +164 253 1 +178 254 1 +174 254 1 +177 254 4 +172 254 1 +166 256 4 +181 258 4 +178 258 1 +174 258 1 +177 258 4 +172 258 1 +162 258 4 +168 258 1 +180 258 4 +169 258 4 +164 258 1 +181 260 1 +180 260 1 +192 263 1 +179 263 4 +176 263 4 +175 263 1 +185 263 1 +173 263 4 +184 263 4 +183 263 1 +182 263 1 +171 263 4 +192 264 1 +182 264 1 +179 264 4 +176 264 4 +175 264 1 +185 264 1 +173 264 4 +184 264 4 +183 264 1 +178 265 4 +174 265 4 +177 265 1 +192 266 1 +182 266 1 +179 266 4 +176 266 4 +175 266 1 +185 266 1 +184 266 4 +183 266 1 +178 267 1 +174 267 1 +178 268 1 +192 269 4 +179 269 1 +192 270 4 +179 270 1 +175 270 4 +192 271 4 +179 271 1 +176 271 1 +175 271 4 +192 275 1 +181 276 4 +192 280 4 +182 280 4 +179 280 1 +176 280 1 +175 280 4 +192 281 4 +182 281 4 +179 281 1 +176 281 1 +185 281 4 +175 281 4 +184 282 4 +188 285 1 +188 286 1 +186 286 4 +188 287 1 +187 287 1 +186 287 4 +188 289 4 +187 289 4 +106 289 4 +109 289 4 +186 289 1 +104 289 4 +116 289 4 +112 289 1 +108 289 4 +189 289 1 +190 289 1 +102 289 1 +99 289 4 +196 289 4 +103 289 4 +198 289 1 +101 289 1 +120 289 1 +191 289 4 +97 289 4 +122 289 1 +121 289 4 +110 289 1 +100 289 1 +98 289 1 +106 290 4 +109 290 4 +196 290 4 +104 290 4 +116 290 4 +112 290 1 +101 290 1 +103 290 4 +108 290 4 +102 290 1 +97 290 4 +120 290 1 +99 290 4 +122 290 1 +100 290 1 +98 290 1 +121 290 4 +110 290 1 +198 291 4 +188 291 1 +187 291 1 +186 291 4 +190 291 4 +191 291 1 +198 292 4 +191 292 1 +198 294 4 +188 295 4 +187 295 4 +106 295 4 +109 295 4 +186 295 1 +104 295 4 +116 295 4 +112 295 1 +108 295 4 +189 295 1 +190 295 1 +102 295 1 +99 295 4 +196 295 4 +103 295 4 +195 295 1 +98 295 1 +198 295 1 +101 295 1 +120 295 1 +191 295 4 +97 295 4 +122 295 1 +121 295 4 +110 295 1 +100 295 1 +193 295 1 +188 296 4 +187 296 4 +106 296 4 +109 296 4 +186 296 1 +104 296 4 +116 296 4 +112 296 1 +108 296 4 +189 296 1 +190 296 1 +102 296 1 +99 296 4 +196 296 4 +103 296 4 +195 296 1 +98 296 1 +198 296 1 +101 296 1 +120 296 1 +191 296 4 +97 296 4 +122 296 1 +121 296 4 +110 296 1 +100 296 1 +198 297 4 +172 297 4 +148 297 1 +184 297 1 +173 297 1 +150 297 1 +182 297 4 +135 297 4 +181 297 1 +169 297 1 +147 297 1 +197 297 1 +100 297 4 +145 297 4 +192 297 4 +178 297 4 +179 297 1 +132 297 4 +175 297 4 +128 297 1 +176 297 1 +129 297 4 +162 297 1 +185 297 4 +136 297 1 +183 297 4 +180 297 1 +174 297 4 +177 297 1 +124 297 1 +171 297 1 +170 297 4 +123 297 4 +168 297 4 +167 297 1 +156 297 1 +157 297 4 +163 297 4 +159 297 1 +155 297 1 +144 297 4 +153 297 1 +152 297 4 +151 297 4 +149 297 1 +141 297 4 +140 297 1 +139 297 4 +158 297 1 +111 297 4 +134 297 1 +133 297 4 +127 297 1 +126 297 4 +131 297 4 +107 297 4 +154 297 4 +130 297 1 +119 297 1 +166 297 1 +125 297 1 +137 297 4 +113 297 4 +160 297 4 +161 297 4 +114 297 1 +164 297 4 +117 297 4 +115 297 4 +138 297 1 +165 297 4 +118 297 1 +188 297 1 +187 297 1 +106 297 1 +109 297 1 +186 297 4 +104 297 1 +116 297 1 +112 297 4 +108 297 1 +189 297 4 +142 297 1 +190 297 4 +143 297 1 +193 297 4 +96 297 1 +102 297 4 +196 297 1 +99 297 1 +146 297 4 +103 297 1 +98 297 4 +195 297 4 +101 297 4 +120 297 4 +191 297 1 +97 297 1 +122 297 4 +121 297 1 +110 297 4 +198 298 4 +148 298 1 +184 298 1 +173 298 1 +172 298 4 +182 298 4 +135 298 4 +181 298 1 +147 298 1 +169 298 1 +197 298 1 +100 298 4 +150 298 1 +145 298 4 +192 298 4 +178 298 4 +179 298 1 +132 298 4 +175 298 4 +128 298 1 +176 298 1 +129 298 4 +185 298 4 +162 298 1 +183 298 4 +136 298 1 +180 298 1 +174 298 4 +177 298 1 +171 298 1 +124 298 1 +123 298 4 +170 298 4 +168 298 4 +167 298 1 +156 298 1 +157 298 4 +163 298 4 +159 298 1 +155 298 1 +144 298 4 +153 298 1 +152 298 4 +151 298 4 +149 298 1 +141 298 4 +140 298 1 +139 298 4 +158 298 1 +111 298 4 +134 298 1 +133 298 4 +127 298 1 +126 298 4 +131 298 4 +107 298 4 +154 298 4 +130 298 1 +119 298 1 +166 298 1 +125 298 1 +137 298 4 +113 298 4 +160 298 4 +161 298 4 +114 298 1 +164 298 4 +117 298 4 +115 298 4 +138 298 1 +165 298 4 +118 298 1 +188 298 1 +187 298 1 +106 298 1 +109 298 1 +186 298 4 +104 298 1 +116 298 1 +112 298 4 +108 298 1 +189 298 4 +142 298 1 +143 298 1 +190 298 4 +193 298 4 +96 298 1 +102 298 4 +196 298 1 +99 298 1 +146 298 4 +103 298 1 +98 298 4 +195 298 4 +101 298 4 +121 298 1 +191 298 1 +194 298 1 +97 298 1 +120 298 4 +122 298 4 +110 298 4 +0 +0 200 +0 +0 +0 100 +0 +0 +100 300 +4 +6 139 5 +6 143 5 +98 293 5 +98 299 5 +0 +300 200 +4 +139 95 5 +143 95 5 +293 199 5 +299 199 5 diff --git a/HDVF/test/HDVF/test_abstract_simplicial_chain_complex.cpp b/HDVF/test/HDVF/test_abstract_simplicial_chain_complex.cpp new file mode 100644 index 00000000000..785b1a4fcc7 --- /dev/null +++ b/HDVF/test/HDVF/test_abstract_simplicial_chain_complex.cpp @@ -0,0 +1,126 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace HDVF = CGAL::Homological_discrete_vector_field; + +typedef CGAL::Simple_cartesian Kernel; +typedef HDVF::Hdvf_traits_3 Traits; + +typedef CGAL::Z2 CoefficientType; +typedef HDVF::Abstract_simplicial_chain_complex ChainComplexType; +typedef CGAL::OSM::Sparse_matrix Column_matrix; +typedef CGAL::OSM::Sparse_chain Column_chain; +typedef CGAL::OSM::Sparse_chain Row_chain; + +int main() { + // Test constructor (from simple_simplicial_complex.simp) + std::cerr << "Test constructor (from simple_simplicial_complex.simp)" << std::endl ; + HDVF::Mesh_object_io mesh; + mesh.read_simp("data/simple_simplicial_complex.simp"); + + ChainComplexType complex(mesh); + + std::cerr << "-- Check number_of_cells" << std::endl ; + assert(complex.number_of_cells(0) == 4); + assert(complex.number_of_cells(1) == 5); + assert(complex.number_of_cells(2) == 1); + + std::vector bnds(complex.boundary_matrices()); + +// Generate data +// CGAL::OSM::write_matrix(bnd.at(0), "data/test_abstract_simplicial_chain_complex/bnd0.osm"); +// CGAL::OSM::write_matrix(bnd.at(1), "data/test_abstract_simplicial_chain_complex/bnd1.osm"); +// CGAL::OSM::write_matrix(bnd.at(2), "data/test_abstract_simplicial_chain_complex/bnd2.osm"); + + std::cerr << "-- Check boundary matrices" << std::endl ; + Column_matrix bnd; + bool test; + + // Dim 0 + CGAL::OSM::read_matrix(bnd, "data/test_abstract_simplicial_chain_complex/bnd0.osm"); + test = (bnd == bnds.at(0)); + std::cerr << "--- Dimension 0: " << test << std::endl ; + assert(test) ; + + // Dim 1 + CGAL::OSM::read_matrix(bnd, "data/test_abstract_simplicial_chain_complex/bnd1.osm"); + test = (bnd == bnds.at(1)); + std::cerr << "--- Dimension 1: " << test << std::endl ; + assert(test) ; + + // Dim 2 + CGAL::OSM::read_matrix(bnd, "data/test_abstract_simplicial_chain_complex/bnd2.osm"); + test = (bnd == bnds.at(2)); + std::cerr << "--- Dimension 2: " << test << std::endl ; + assert(test) ; + + std::cerr << "-- Check d and cod" << std::endl ; + + Column_chain c1, c2; + Row_chain r1, r2; + // Dim 0 + CGAL::OSM::read_matrix(bnd, "data/test_abstract_simplicial_chain_complex/bnd0.osm"); + c1 = CGAL::OSM::cget_column(bnd, 0); + c2 = complex.d(0,0); + test = (c1 == c2); + std::cerr << "--- Dimension 0 (d(0)): " << test << std::endl ; + assert(test) ; + r1 = CGAL::OSM::get_row(bnd, 0); + r2 = complex.cod(0,0); + test = (c1 == c2); + std::cerr << "--- Dimension 0 (cod(0))): " << test << std::endl ; + assert(test) ; + + // Dim 0 + CGAL::OSM::read_matrix(bnd, "data/test_abstract_simplicial_chain_complex/bnd0.osm"); + c1 = CGAL::OSM::cget_column(bnd, 0); + c2 = complex.d(0,0); + test = (c1 == c2); + std::cerr << "--- Dimension 0 (d(0)): " << test << std::endl ; + assert(test) ; + r1 = CGAL::OSM::get_row(bnd, 0); + r2 = complex.cod(0,0); + test = (c1 == c2); + std::cerr << "--- Dimension 0 (cod(0))): " << test << std::endl ; + assert(test) ; + + // Dim 1 + CGAL::OSM::read_matrix(bnd, "data/test_abstract_simplicial_chain_complex/bnd1.osm"); + c1 = CGAL::OSM::cget_column(bnd, 0); + c2 = complex.d(0,1); + test = (c1 == c2); + std::cerr << "--- Dimension 1 (d(0)): " << test << std::endl ; + assert(test) ; + r1 = CGAL::OSM::get_row(bnd, 0); + r2 = complex.cod(0,1); + test = (c1 == c2); + std::cerr << "--- Dimension 1 (cod(0))): " << test << std::endl ; + assert(test) ; + + // Dim 2 + CGAL::OSM::read_matrix(bnd, "data/test_abstract_simplicial_chain_complex/bnd2.osm"); + c1 = CGAL::OSM::cget_column(bnd, 0); + c2 = complex.d(0,2); + test = (c1 == c2); + std::cerr << "--- Dimension 2 (d(0)): " << test << std::endl ; + assert(test) ; + r1 = CGAL::OSM::get_row(bnd, 0); + r2 = complex.cod(0,2); + test = (c1 == c2); + std::cerr << "--- Dimension 2 (cod(0))): " << test << std::endl ; + assert(test) ; + + return 0; +} + + diff --git a/HDVF/test/HDVF/test_hdvf.cpp b/HDVF/test/HDVF/test_hdvf.cpp new file mode 100644 index 00000000000..c8cfd24ac6d --- /dev/null +++ b/HDVF/test/HDVF/test_hdvf.cpp @@ -0,0 +1,75 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#define BUILD_TEST_DATA + +namespace HDVF = CGAL::Homological_discrete_vector_field; + +//typedef int Coefficient_ring; +//typedef CGAL::Z2 Coefficient_ring; +typedef CGAL::Zp<5, char, true> Coefficient_ring; + +typedef CGAL::Simple_cartesian Kernel; +typedef HDVF::Hdvf_traits_3 Traits; + +using Complex = HDVF::Simplicial_chain_complex ; +using HDVF_type = HDVF::Hdvf ; + +int main(int argc, char **argv) { + std::string filename; + if (argc > 2) { + std::cerr << "usage: test_hdvf_core [off_file]" << std::endl; + } + else if (argc == 1) filename = "data/three_triangles.off" ; + else filename = argv[1] ; + + // Load off into Mesh_object_io + HDVF::Mesh_object_io mesh ; + mesh.read_off(filename); + +// mesh.print_infos(); + + // Build simplicial chain complex + Complex complex(mesh); + + std::cout << complex; + + // Build empty HDVF + HDVF_type hdvf(complex, HDVF::OPT_FULL) ; + + // Compute a perfect HDVF + hdvf.compute_perfect_hdvf(); + // hdvf.compute_rand_perfect_hdvf(); + std::cerr << std::endl; + +#ifdef BUILD_TEST_DATA + // Save HDVF to .hdvf file + hdvf.write_hdvf_reduction("data/test_hdvf/test_hdvf.hdvf") ; +#endif + + // Read HDVF from .hdvf + HDVF_type hdvf2(complex, HDVF::OPT_FULL); + hdvf2.read_hdvf_reduction("data/test_hdvf/test_hdvf.hdvf"); + + // Compare + bool test_true(hdvf.compare(hdvf2)); + std::cerr << "-- Test HDVF built: " << test_true << std::endl; + assert(test_true); + + return 0; +} + + diff --git a/HDVF/test/HDVF/test_mesh_object_io.cpp b/HDVF/test/HDVF/test_mesh_object_io.cpp new file mode 100644 index 00000000000..b37737038ef --- /dev/null +++ b/HDVF/test/HDVF/test_mesh_object_io.cpp @@ -0,0 +1,96 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace HDVF = CGAL::Homological_discrete_vector_field; +typedef CGAL::Simple_cartesian Kernel; +typedef HDVF::Hdvf_traits_3 Traits; +typedef HDVF::Mesh_object_io::Point Point; + +int main() { + // ------ Test constructor from vectors of nodes and Io_cell_types + + std::vector nodes1, nodes2; + std::vector cells1, cells2; + + // (nodes1, cells1) : "pure" mesh + // (nodes2, cells2) : heterogeneous simplicial complex + nodes1.push_back(Point(0,0,0)); + nodes1.push_back(Point(0,1,0)); + nodes1.push_back(Point(0,0,1)); + nodes2 = nodes1; + nodes2.push_back(Point(0,1,1)); + + cells1.push_back(HDVF::Io_cell_type({1,2,3})); + cells1.push_back(HDVF::Io_cell_type({1,2,3})); + cells1.push_back(HDVF::Io_cell_type({1,2,3})); + cells2 = cells1; + cells2.push_back(HDVF::Io_cell_type({1,4})); + + std::cerr << "Test Mesh_object_io() with d>0" << std::endl; + std::cerr << "Construct an object with d = 2 on a 'pure' mesh" << std::endl ; + // The constructor should raise no exception + try { + HDVF::Mesh_object_io mio1(2,nodes1,cells1); + assert(mio1.nodes.size() == 3); + assert(mio1.cells.size() == 3); + } catch (...) { + std::cerr << "-- check_dimension() failed" << std::endl; + assert(false); + } + std::cerr << "Construct an object with d = 2 on a non homogeneous mesh" << std::endl ; + // The constructor *should* raise an exception + try { + HDVF::Mesh_object_io mio1(2,nodes2,cells2); + assert(mio1.nodes.size() == 3); + assert(mio1.cells.size() == 3); + } catch (...) { + std::cerr << "-- check_dimension() properly detected the dimension changes" << std::endl; + assert(true); + } + + std::cerr << "Construct an object with d = -2 on a non homogeneous mesh" << std::endl ; + // The constructor should not raise an exception + try { + HDVF::Mesh_object_io mio1(-2,nodes2,cells2); + assert(mio1.nodes.size() == 4); + assert(mio1.cells.size() == 4); + } catch (...) { + std::cerr << "-- check_dimension() failed" << std::endl; + assert(false); + } + + // Test read_off() + { + std::cerr << "Test read_off with data/three_triangles.off (with ordering of vertices indices)" << std::endl; + HDVF::Mesh_object_io mio2; + mio2.read_off("data/three_triangles.off") ; + assert(mio2.nvertices == 6); + assert(mio2.ncells == 3); + assert(mio2.cells.at(0) == HDVF::Io_cell_type({0,1,2})); + assert(mio2.cells.at(1) == HDVF::Io_cell_type({2,3,4})); + assert(mio2.cells.at(2) == HDVF::Io_cell_type({1,3,5})); + } + + // Test read_simp() + { + std::cerr << "Test read_simp with data/simple_simplicial_complex.simp (with ordering of vertices indices)" << std::endl; + HDVF::Mesh_object_io mio2; + mio2.read_simp("data/simple_simplicial_complex.simp") ; + assert(mio2.nvertices == 0); + assert(mio2.ncells == 3); + assert(mio2.cells.at(0) == HDVF::Io_cell_type({1,2,3})); + assert(mio2.cells.at(1) == HDVF::Io_cell_type({2,4})); + assert(mio2.cells.at(2) == HDVF::Io_cell_type({3,4})); + } + + return 0; +} + + diff --git a/HDVF/test/HDVF/test_simplex.cpp b/HDVF/test/HDVF/test_simplex.cpp new file mode 100644 index 00000000000..d7d01dce5b8 --- /dev/null +++ b/HDVF/test/HDVF/test_simplex.cpp @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include +#include +#include + +namespace HDVF = CGAL::Homological_discrete_vector_field; + +int main() { + HDVF::Simplex s1({1,2}), s2({1,2,3}); + std::vector bnd, bnd2 ; + + // Check s1 + std::cerr << "Check dimension s1" << std::endl ; + assert(s1.dimension() == 1); + std::cerr << "Check boundary s1" << std::endl ; + bnd = s1.boundary() ; + bnd2.push_back(HDVF::Simplex({2})) ; + bnd2.push_back(HDVF::Simplex({1})) ; + assert(bnd.size() == bnd2.size()); + for (int i=0; i +#include +#include +#include +#include +#include +#include + +namespace HDVF = CGAL::Homological_discrete_vector_field; + +int main() { + HDVF::Simplex s1({1,2}), s2({1,2,3}); + std::vector bnd, bnd2 ; + + // Check s1 + std::cerr << "Check dimension s1" << std::endl ; + assert(s1.dimension() == 1); + std::cerr << "Check boundary s1" << std::endl ; + bnd = s1.boundary() ; + bnd2.push_back(HDVF::Simplex({2})) ; + bnd2.push_back(HDVF::Simplex({1})) ; + assert(bnd.size() == bnd2.size()); + for (int i=0; i +#include +#include +#include +#include +#include +#include + +typedef int Coefficient_ring; +typedef CGAL::OSM::Sparse_chain Column_chain; +typedef CGAL::OSM::Sparse_chain Row_chain ; +typedef CGAL::OSM::Sparse_matrix Column_matrix; +typedef CGAL::OSM::Sparse_matrix Row_matrix; + + +int main(int argc, char **argv) +{ + Column_chain c1(5), c2(5), c3(5); + Row_chain r1(5), r2(5), r3(5) ; + + c1.set_coefficient(0, 1); + c1.set_coefficient(2, -1); + c2.set_coefficient(0, -1); + c2.set_coefficient(1, 1); + c3.set_coefficient(2, -1); + c3.set_coefficient(0, 1); + + r1.set_coefficient(0, 1); + r1.set_coefficient(2, -1); + r2.set_coefficient(0, -1); + r2.set_coefficient(1, 1); + r3.set_coefficient(2, -1); + r3.set_coefficient(0, 1); + + std::cerr << "-- Test Sparse_chain operator==" << std::endl; + + { + std::cerr << "----> Column chains" << std::endl; + bool comp_true(c1==c3) ; + std::cerr << "Compare similar column chains: " << comp_true << std::endl ; + assert(comp_true) ; + bool comp_false(c1==c2) ; + std::cerr << "Compare different column chains: " << comp_false << std::endl ; + assert(!comp_false) ; + } + { + std::cerr << "----> Row chains" << std::endl; + bool comp_true(r1==r3) ; + std::cerr << "Compare similar row chains: " << comp_true << std::endl ; + assert(comp_true) ; + bool comp_false(r1==r2) ; + std::cerr << "Compare different row chains: " << comp_false << std::endl ; + assert(!comp_false) ; + } + + std::cerr << "-- Test Sparse_chain operator+" << std::endl; + { + std::cerr << "----> Column chains" << std::endl; + Column_chain res(5), res2(4); + res.set_coefficient(2, -1); + res.set_coefficient(1, 1); + bool comp_true(c1+c2==res) ; + std::cerr << "Test c1+c2: " << comp_true << std::endl ; + assert(comp_true) ; + res2.set_coefficient(2, -1); + res2.set_coefficient(1, 1); + bool comp_false(c1+c2==res2) ; + std::cerr << "Compare c1+c2 with invalid size chain: " << comp_false << std::endl ; + assert(!comp_false) ; + } + { + std::cerr << "----> Row chains" << std::endl; + Row_chain res(5), res2(4); + res.set_coefficient(2, -1); + res.set_coefficient(1, 1); + bool comp_true(r1+r2==res) ; + std::cerr << "Test r1+r2: " << comp_true << std::endl ; + assert(comp_true) ; + res2.set_coefficient(2, -1); + res2.set_coefficient(1, 1); + bool comp_false(r1+r2==res2) ; + std::cerr << "Compare r1+r2 with invalid size chain: " << comp_false << std::endl ; + assert(!comp_false) ; + } + + std::cerr << "-- Test Sparse_chain operator-" << std::endl; + { + std::cerr << "----> Column chains" << std::endl; + Column_chain res(5); + res.set_coefficient(0, 2); + res.set_coefficient(2, -1); + res.set_coefficient(1, -1); + bool comp_true(c1-c2==res) ; + std::cerr << "Test c1-c2: " << comp_true << std::endl ; + assert(comp_true) ; + } + { + std::cerr << "----> Row chains" << std::endl; + Row_chain res(5); + res.set_coefficient(0, 2); + res.set_coefficient(2, -1); + res.set_coefficient(1, -1); + bool comp_true(r1-r2==res) ; + std::cerr << "Test r1-r2: " << comp_true << std::endl ; + assert(comp_true) ; + } + + std::cerr << "-- Test Sparse_chain operator* (coefficient*chain)" << std::endl; + { + std::cerr << "----> Column chains" << std::endl; + Column_chain res(5); + res.set_coefficient(0, 3); + res.set_coefficient(2, -3); + bool comp_true(3*c1==res) ; + std::cerr << "Test 3*c1: " << comp_true << std::endl ; + assert(comp_true) ; + } + { + std::cerr << "----> Row chains" << std::endl; + Row_chain res(5); + res.set_coefficient(0, 3); + res.set_coefficient(2, -3); + bool comp_true(3*r1==res) ; + std::cerr << "Test 3*r1: " << comp_true << std::endl ; + assert(comp_true) ; + } + + std::cerr << "-- Test Sparse_chain operator* (chain*coefficient)" << std::endl; + { + std::cerr << "----> Column chains" << std::endl; + Column_chain res(5); + res.set_coefficient(0, 3); + res.set_coefficient(2, -3); + bool comp_true(c1*3==res) ; + std::cerr << "Test c1*3: " << comp_true << std::endl ; + assert(comp_true) ; + } + { + std::cerr << "----> Row chains" << std::endl; + Row_chain res(5); + res.set_coefficient(0, 3); + res.set_coefficient(2, -3); + bool comp_true(r1*3==res) ; + std::cerr << "Test r1*3: " << comp_true << std::endl ; + assert(comp_true) ; + } + + std::cerr << "-- Test Sparse_chain operator/" << std::endl; + { + std::cerr << "----> Column chains" << std::endl; + Column_chain res(c1), res2(5); + res.set_coefficient(1, 3); + bool comp_true(c1==(res/1)) ; + std::cerr << "Test c1 == res/1: " << comp_true << std::endl ; + assert(comp_true) ; + res2.set_coefficient(0, 1); + bool comp_true2(res2==(res/std::vector({1,2}))) ; + std::cerr << "Test res2 == res/{1,2}: " << comp_true2 << std::endl ; + assert(comp_true2) ; + } + + { + std::cerr << "----> Row chains" << std::endl; + Row_chain res(r1), res2(5); + res.set_coefficient(1, 3); + bool comp_true(r1==(res/1)) ; + std::cerr << "Test r1 == res/1: " << comp_true << std::endl ; + assert(comp_true) ; + res2.set_coefficient(0, 1); + bool comp_true2(res2==(res/std::vector({1,2}))) ; + std::cerr << "Test res2 == res/{1,2}: " << comp_true2 << std::endl ; + assert(comp_true2) ; + } + + +// Row_matrix MR_rw(3,4); +// CGAL::OSM::set_coefficient(MR_rw, 0, 1, 1) ; +// CGAL::OSM::set_coefficient(MR_rw, 0, 2, -1) ; +// CGAL::OSM::set_coefficient(MR_rw, 1, 0, 2) ; +// CGAL::OSM::set_coefficient(MR_rw, 1, 2, -2) ; +// +// write_matrix(MR_rw, "test_row_mat.osm"); +// +// Row_matrix MR_rw2 ; +// +// CGAL::OSM::read_matrix(MR_rw2, "test_row_mat.osm"); +// +// bool comp_row(MR_rw == MR_rw2) ; +// std::cerr << "saved and loaded matrices comparison: " << comp_row << std::endl ; +// assert(comp_row) ; +// } +// +// std::cerr << "-- Tests Sparse_matrices operations" << std::endl ; +// +// Column_chain a(4); +// Row_chain b(4); +// a.set_coefficient(1,1); +// a.set_coefficient(3,3); +// b.set_coefficient(0,2); +// b.set_coefficient(1,-3); +// +// std::cerr << "---- Test column chain * row chain -> columnMajor matrix" << std::endl ; +// +// Column_matrix columnMajor = a * b, tmp = columnMajor; +// +// { +// Column_matrix tmp ; +// CGAL::OSM::read_matrix(tmp, "data/test_sparse_matrices/columnMajor.osm") ; +// bool bres(tmp == columnMajor); +// std::cerr << "column chain * row chain: " << bres << std::endl; +// assert(bres) ; +// } +// +// std::cerr << "---- Test column chain % row chain -> rowMajor matrix" << std::endl ; +// +// Row_matrix rowMajor = a % b; +// +// { +// Row_matrix tmp ; +// CGAL::OSM::read_matrix(tmp, "data/test_sparse_matrices/rowMajor.osm") ; +// bool bres(tmp == rowMajor); +// std::cerr << "column chain % row chain: " << bres << std::endl; +// assert(bres) ; +// } +// +// std::cerr << "---- Test column/row get" << std::endl ; +// +// std::cerr << "------ In Column_matrix" << std::endl ; +// +// { +// Column_chain res(CGAL::OSM::get_column(columnMajor, 0)), tmp(4) ; +// tmp.set_coefficient(1, 2) ; +// tmp.set_coefficient(3, 6) ; +// +// bool bres (tmp == res) ; +// std::cerr << "get_column 0 in columnMajor: " << bres << std::endl; +// assert(bres) ; +// } +// +// { +// Row_chain res2(CGAL::OSM::get_row(columnMajor, 1)), tmp2(4) ; +// tmp2.set_coefficient(0, 2) ; +// tmp2.set_coefficient(1, -3) ; +// +// bool bres (tmp2 == res2) ; +// std::cerr << "get_row 1 in columnMajor: " << bres << std::endl; +// assert(bres); +// } +// +// std::cerr << "------ In Row_matrix" << std::endl ; +// +// { +// Column_chain res(CGAL::OSM::get_column(rowMajor, 0)), tmp(4) ; +// tmp.set_coefficient(1, 2) ; +// tmp.set_coefficient(3, 6) ; +// +// bool bres(tmp == res) ; +// std::cerr << "get_column 0 in rowMajor: " << bres << std::endl; +// assert(bres); +// } +// +// { +// Row_chain res2(CGAL::OSM::get_row(rowMajor, 1)), tmp2(4) ; +// tmp2.set_coefficient(0, 2) ; +// tmp2.set_coefficient(1, -3) ; +// +// bool bres(tmp2==res2); +// std::cerr << "get_row 1 in columnMajor: " << bres << std::endl; +// assert(bres); +// } +// +// std::cerr << "---- Test column/row deletion" << std::endl; +// +// std::cerr << "------ In Column_matrix" << std::endl ; +// +// { +// Column_matrix tmp(columnMajor), tmp2; +// CGAL::OSM::remove_column(tmp, 0) ; +// CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/columnMajor_del_col.osm"); +// +// bool bres(tmp == tmp2); +// std::cerr << "deletion column 0 in columnMajor: " << bres << std::endl ; +// assert(bres); +// } +// +// { +// Column_matrix tmp(columnMajor), tmp2; +// CGAL::OSM::remove_row(tmp, 1) ; +// CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/columnMajor_del_row.osm"); +// bool bres(tmp == tmp2); +// std::cerr << "deletion row 1 in columnMajor: " << bres << std::endl ; +// assert(bres); +// } +// +// std::cerr << "------ In Row_matrix" << std::endl ; +// +// { +// Row_matrix tmp(rowMajor), tmp2; +// CGAL::OSM::remove_row(tmp, 1) ; +// CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/rowMajor_del_row.osm"); +// bool bres(tmp==tmp2); +// std::cerr << "deletion row 1 in rowMajor: " << bres << std::endl ; +// assert(bres); +// } +// +// { +// Row_matrix tmp(rowMajor), tmp2; +// CGAL::OSM::remove_column(tmp, 0) ; +// CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/rowMajor_del_column.osm"); +// bool bres(tmp == tmp2); +// std::cerr << "deletion column 0 in rowMajor: " << bres << std::endl ; +// assert(bres); +// } +// +// // //////// Sum +// std::cerr << "---- Test matrices sum" << std::endl ; +// +// // COL + COL +// std::cerr << "------ Test Column_matrix + Column_matrix" << std::endl ; +// +// { +// Column_matrix tmp(columnMajor), res, tmp2 ; +// +// CGAL::OSM::remove_coefficient(tmp, 1, 0); +// CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; +// +// res = tmp + columnMajor ; +// Column_matrix res2(tmp); +// res2 += columnMajor ; +// +// CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/ColCol_sum.osm"); +// bool bres(res == tmp2) ; +// std::cerr << "sum columnMajor + otherColumnMajor: " << bres << std::endl ; +// assert(bres); +// bool bres2(res2 == tmp2) ; +// std::cerr << "sum columnMajor += otherColumnMajor: " << bres2 << std::endl ; +// assert(bres2); +// } +// +// // COL + ROW +// std::cerr << "------ Test Column_matrix + Row_matrix" << std::endl ; +// +// { +// Column_matrix tmp(columnMajor), res, tmp2 ; +// +// CGAL::OSM::remove_coefficient(tmp, 1, 0); +// CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; +// +// res = tmp + rowMajor ; +// Column_matrix res2(tmp); +// res2 += rowMajor ; +// CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/ColRow_sum.osm"); +// bool bres(tmp2 == res); +// std::cerr << "sum otherColumnMajor + rowMajor: " << bres << std::endl ; +// assert(bres); +// bool bres2(tmp2 == res2); +// std::cerr << "sum otherColumnMajor += rowMajor: " << bres2 << std::endl ; +// assert(bres2); +// } +// +// // ROW + COL +// std::cerr << "------ Test Row_matrix + Column_matrix" << std::endl ; +// +// { +// Row_matrix tmp(rowMajor), res, tmp2 ; +// +// CGAL::OSM::remove_coefficient(tmp, 1, 0); +// CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; +// +// res = tmp + columnMajor ; +// Row_matrix res2(tmp); +// res2 += columnMajor ; +// CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/RowCol_sum.osm"); +// bool bres(tmp2 == res); +// std::cerr << "sum otherRowMajor + columnMajor: " << bres << std::endl ; +// assert(bres); +// bool bres2(tmp2 == res2); +// std::cerr << "sum otherRowMajor += columnMajor: " << bres2 << std::endl ; +// assert(bres2); +// } +// +// +// // ROW + ROW +// std::cerr << "------ Test Row_matrix + Row_matrix" << std::endl ; +// +// { +// Row_matrix tmp(rowMajor), res, tmp2 ; +// +// CGAL::OSM::remove_coefficient(tmp, 1, 0); +// CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; +// +// res = tmp + rowMajor ; +// Row_matrix res2(tmp); +// res2 += rowMajor; +// CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/RowRow_sum.osm"); +// bool bres(tmp2 == res); +// std::cerr << "sum otherRowMajor + rowMajor: " << bres << std::endl ; +// assert(bres); +// bool bres2(tmp2 == res2); +// std::cerr << "sum otherRowMajor += rowMajor: " << bres2 << std::endl ; +// assert(bres2); +// } +// +// // //////// Subtract +// std::cerr << "---- Test matrices subtract" << std::endl ; +// +// // COL - COL +// std::cerr << "------ Test Column_matrix - Column_matrix" << std::endl ; +// +// { +// Column_matrix tmp(columnMajor), res, tmp2 ; +// +// CGAL::OSM::remove_coefficient(tmp, 1, 0); +// CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; +// +// res = tmp - columnMajor ; +// Column_matrix res2(tmp); +// res2 -= columnMajor; +// CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/ColCol_sub.osm"); +// bool bres(res == tmp2) ; +// std::cerr << "sub columnMajor - otherColumnMajor: " << bres << std::endl ; +// assert(bres); +// bool bres2(res2 == tmp2) ; +// std::cerr << "sub columnMajor -= otherColumnMajor: " << bres2 << std::endl ; +// assert(bres2); +// } +// +// +// // COL - ROW +// std::cerr << "------ Test Column_matrix - Row_matrix" << std::endl ; +// +// { +// Column_matrix tmp(columnMajor), res, tmp2 ; +// +// CGAL::OSM::remove_coefficient(tmp, 1, 0); +// CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; +// +// res = tmp - rowMajor ; +// Column_matrix res2(tmp); +// res2 -= rowMajor; +// CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/ColRow_sub.osm"); +// bool bres(res == tmp2) ; +// std::cerr << "sub otherColumnMajor - rowMajor: " << bres << std::endl ; +// assert(bres); +// bool bres2(res2 == tmp2) ; +// std::cerr << "sub otherColumnMajor -= rowMajor: " << bres2 << std::endl ; +// assert(bres2); +// } +// +// // ROW - COL +// std::cerr << "------ Test Row_matrix - Column_matrix" << std::endl ; +// +// { +// Row_matrix tmp(rowMajor), res, tmp2 ; +// +// CGAL::OSM::remove_coefficient(tmp, 1, 0); +// CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; +// +// res = tmp - columnMajor ; +// Row_matrix res2(tmp); +// res2 -= columnMajor; +// CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/RowCol_sub.osm"); +// bool bres(res == tmp2); +// std::cerr << "sub otherRowMajor - columnMajor: " << bres << std::endl ; +// assert(bres); +// bool bres2(res2 == tmp2); +// std::cerr << "sub otherRowMajor -= columnMajor: " << bres2 << std::endl ; +// assert(bres2); +// } +// +// // ROW - ROW +// std::cerr << "------ Test Row_matrix - Row_matrix" << std::endl ; +// +// { +// Row_matrix tmp(rowMajor), res, tmp2 ; +// +// CGAL::OSM::remove_coefficient(tmp, 1, 0); +// CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; +// +// res = tmp - rowMajor ; +// Row_matrix res2(tmp); +// res2 -= rowMajor; +// CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/RowRow_sub.osm"); +// bool bres(res == tmp2); +// std::cerr << "sub otherRowMajor - rowMajor: " << bres << std::endl ; +// assert(bres); +// bool bres2(res2 == tmp2); +// std::cerr << "sub otherRowMajor -= rowMajor: " << bres2 << std::endl ; +// assert(bres2); +// } +// +// // //////// Product * (column result) +// std::cerr << "---- Test matrices product *" << std::endl ; +// +// // COL * COL +// std::cerr << "------ Test Column_matrix * Column_matrix" << std::endl ; +// +// { +// Column_matrix res, tmp2 ; +// Column_matrix tmp(columnMajor) ; +// +// CGAL::OSM::remove_coefficient(tmp, 1, 0); +// CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; +// +// res = tmp * columnMajor ; +// Column_matrix res2(tmp) ; +// res2 *= columnMajor; +// CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/ColCol_prod_col.osm"); +// bool bres(res == tmp2); +// std::cerr << "prod columnMajor * otherColumnMajor: " << bres << std::endl ; +// assert(bres); +// bool bres2(res2 == tmp2); +// std::cerr << "prod columnMajor *= otherColumnMajor: " << bres2 << std::endl ; +// assert(bres2); +// } +// +// // COL * ROW +// std::cerr << "------ Test Column_matrix * Row_matrix" << std::endl ; +// +// { +// Column_matrix res, tmp2 ; +// Column_matrix tmp(columnMajor) ; +// +// CGAL::OSM::remove_coefficient(tmp, 1, 0); +// CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; +// +// res = tmp * rowMajor ; +// Column_matrix res2(tmp); +// res2 *= rowMajor; +// CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/ColRow_prod_col.osm"); +// bool bres(res == tmp2); +// std::cerr << "prod otherColumnMajor * rowMajor: " << bres << std::endl ; +// assert(bres); +// bool bres2(res2 == tmp2); +// std::cerr << "prod otherColumnMajor *= rowMajor: " << bres2 << std::endl ; +// assert(bres2); +// } +// +// // ROW * COL +// std::cerr << "------ Test Row_matrix * Column_matrix" << std::endl ; +// +// { +// Column_matrix res, tmp2 ; +// Row_matrix tmp(rowMajor) ; +// +// CGAL::OSM::remove_coefficient(tmp, 1, 0); +// CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; +// +// res = tmp * columnMajor ; +// Column_matrix res2(tmp); +// res2 *= columnMajor; +// CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/RowCol_prod_col.osm"); +// bool bres(res == tmp2); +// std::cerr << "prod otherRowMajor * columnMajor: " << bres << std::endl ; +// assert(bres); +// bool bres2(res2 == tmp2); +// std::cerr << "prod otherRowMajor *= columnMajor: " << bres2 << std::endl ; +// assert(bres2); +// } +// +// +// // ROW * ROW +// std::cerr << "------ Test Row_matrix * Row_matrix" << std::endl ; +// +// { +// Column_matrix res, tmp2 ; +// Row_matrix tmp(rowMajor) ; +// +// CGAL::OSM::remove_coefficient(tmp, 1, 0); +// CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; +// +// res = tmp * rowMajor ; +// Column_matrix res2(tmp) ; +// res2 *= rowMajor; +// CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/RowRow_prod_col.osm"); +// bool bres(res == tmp2); +// std::cerr << "prod otherRowMajor * rowMajor: " << bres << std::endl; +// assert(bres); +// bool bres2(res2 == tmp2); +// std::cerr << "prod otherRowMajor *= rowMajor: " << bres2 << std::endl; +// assert(bres2); +// } +// +// // //////// Product % (row result) +// std::cerr << "---- Test matrices product %" << std::endl ; +// +// // COL % COL +// std::cerr << "------ Test Column_matrix % Column_matrix" << std::endl ; +// +// { +// Row_matrix res, tmp2 ; +// Column_matrix tmp(columnMajor) ; +// +// CGAL::OSM::remove_coefficient(tmp, 1, 0); +// CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; +// +// res = tmp % columnMajor ; +// CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/ColCol_prod_row.osm"); +// bool bres(res == tmp2); +// std::cerr << "prod columnMajor % otherColumnMajor: " << bres << std::endl ; +// assert(bres); +// } +// +// +// // COL % ROW +// std::cerr << "------ Test Column_matrix % Row_matrix" << std::endl ; +// +// { +// Row_matrix res, tmp2 ; +// Column_matrix tmp(columnMajor) ; +// +// CGAL::OSM::remove_coefficient(tmp, 1, 0); +// CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; +// +// res = tmp % rowMajor ; +// CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/ColRow_prod_row.osm"); +// bool bres(res == tmp2); +// std::cerr << "prod otherColumnMajor % rowMajor: " << bres << std::endl ; +// assert(bres); +// } +// +// // ROW % COL +// std::cerr << "------ Test Row_matrix % Column_matrix" << std::endl ; +// +// { +// Row_matrix res, tmp2; +// Row_matrix tmp(rowMajor); +// +// CGAL::OSM::remove_coefficient(tmp, 1, 0); +// CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; +// +// res = tmp % columnMajor ; +// CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/RowCol_prod_row.osm"); +// bool bres(res == tmp2); +// std::cerr << "prod otherRowMajor % columnMajor: " << bres << std::endl ; +// assert(bres); +// } +// +// +// // ROW % ROW +// std::cerr << "------ Test Row_matrix % Row_matrix" << std::endl ; +// +// { +// Row_matrix res, tmp2; +// Row_matrix tmp(rowMajor); +// +// CGAL::OSM::remove_coefficient(tmp, 1, 0); +// CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; +// +// res = tmp % rowMajor ; +// CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/RowRow_prod_row.osm"); +// bool bres(res == tmp2); +// std::cerr << "prod otherRowMajor % rowMajor: " << bres << std::endl ; +// assert(bres); +// +// } +// +// // //////// Transpose +// std::cerr << "---- Test matrices transpose" << std::endl ; +// +// // COL^t +// std::cerr << "------ Test Column_matrix transpose" << std::endl ; +// +// { +// Row_matrix res = columnMajor.transpose(), tmp2 ; +// CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/Col_transpose.osm"); +// bool bres(res == tmp2); +// std::cerr << "transpose columnMajor: " << bres << std::endl ; +// assert(bres); +// } +// +// // ROW^t +// std::cerr << "------ Test Row_matrix transpose" << std::endl ; +// +// { +// Column_matrix res = rowMajor.transpose(), tmp2 ; +// CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/Row_transpose.osm"); +// bool bres(res == tmp2); +// std::cerr << "transpose rowMajor: " << bres << std::endl ; +// assert(bres); +// } +// +// // //////// Product scalar * matrix +// std::cerr << "---- Test scalar - matrix and matrix - scalar product" << std::endl ; +// +// // COL +// std::cerr << "------ Test 3 * Column_matrix and Column_matrix * 3" << std::endl ; +// +// { +// Row_matrix tmp(rowMajor), tmp2(rowMajor), res1, res2, res3 ; +// +// // Build expected result +// for (CGAL::OSM::Bitboard::iterator it = tmp2.begin(); it != tmp2.end(); ++it) +// { +// const Row_chain& tmp_row(CGAL::OSM::cget_row(tmp2, *it)); +// for (Row_chain::const_iterator it2=tmp_row.cbegin(); it2 != tmp_row.cend(); ++it2) +// { +// CGAL::OSM::set_coefficient(tmp2, *it, it2->first, 3*it2->second); +// } +// } +// +// res1 = 3*tmp ; +// res2 = tmp*3 ; +// res3 = tmp ; +// res3 *= 3 ; +// bool bres1(res1 == tmp2); +// std::cerr << "prod 3*rowMajor: " << bres1 << std::endl ; +// assert(bres1); +// bool bres2(res2 == tmp2); +// std::cerr << "prod rowMajor*3: " << bres2 << std::endl ; +// assert(bres2); +// bool bres3(res3 == tmp2); +// std::cerr << "prod rowMajor *= 3: " << bres3 << std::endl ; +// assert(bres3); +// } +// +// // ROW +// std::cerr << "------ Test 3 * Row_matrix and Row_matrix * 3" << std::endl ; +// +// { +// Column_matrix tmp(columnMajor), tmp2(columnMajor), res1, res2, res3 ; +// +// // Build expected result +// for (CGAL::OSM::Bitboard::iterator it = tmp2.begin(); it != tmp2.end(); ++it) +// { +// const Column_chain& tmp_col(CGAL::OSM::cget_column(tmp2, *it)); +// for (Column_chain::const_iterator it2=tmp_col.cbegin(); it2 != tmp_col.cend(); ++it2) +// { +// CGAL::OSM::set_coefficient(tmp2, it2->first, *it, 3*it2->second); +// } +// } +// +// res1 = 3*tmp ; +// res2 = tmp*3 ; +// res3 = tmp ; +// res3 *= 3 ; +// bool bres1(res1 == tmp2); +// std::cerr << "prod 3*columnMajor: " << bres1 << std::endl ; +// assert(bres1); +// bool bres2(res2 == tmp2); +// std::cerr << "prod columnMajor*3: " << bres2 << std::endl ; +// assert(bres2); +// bool bres3(res3 == tmp2); +// std::cerr << "prod columnMajor *= 3: " << bres3 << std::endl ; +// assert(bres3); +// } +// +//// std::cout << "Multiplication de colonne (col/row dom):" << std::endl << colCol << std::endl << colRow << std::endl << std::endl; +//// std::cout << "Multiplication de ligne (col/row dom):" << std::endl << rowCol << std::endl << rowRow << std::endl << std::endl; +//// +//// cout << "-------- Test parcours (init par set_coefficient)" << endl ; +//// OSM::SparseMatrix Mtest(10, 10) ; +//// Mtest.set_coefficient(1, 2, 1) ; +//// Mtest.set_coefficient(3, 2, 1) ; +//// Mtest.set_coefficient(1, 4, 1) ; +//// Mtest.set_coefficient(2, 5, 1) ; +//// for (OSM::Bitboard::iterator it = Mtest.begin(); it != Mtest.end(); ++it) +//// { +//// cout << "col : " << *it << endl ; +//// } +//// +// return 0; +} + diff --git a/HDVF/test/HDVF/test_sparse_matrix.cpp b/HDVF/test/HDVF/test_sparse_matrix.cpp new file mode 100644 index 00000000000..701c167932b --- /dev/null +++ b/HDVF/test/HDVF/test_sparse_matrix.cpp @@ -0,0 +1,629 @@ +#include +#include +#include +#include +#include +#include +#include + +typedef int Coefficient_ring; +typedef CGAL::OSM::Sparse_chain Column_chain; +typedef CGAL::OSM::Sparse_chain Row_chain ; +typedef CGAL::OSM::Sparse_matrix Column_matrix; +typedef CGAL::OSM::Sparse_matrix Row_matrix; + + +int main(int argc, char **argv) +{ + std::cerr << "-- Test Sparse_matrices read/write" << std::endl; + + { + std::cerr << "----> Column matrices" << std::endl; + + Column_matrix MC_rw(3,4); + CGAL::OSM::set_coefficient(MC_rw, 0, 1, 1) ; + CGAL::OSM::set_coefficient(MC_rw, 0, 2, -1) ; + CGAL::OSM::set_coefficient(MC_rw, 1, 0, 2) ; + CGAL::OSM::set_coefficient(MC_rw, 1, 2, -2) ; + + CGAL::OSM::write_matrix(MC_rw, "tmp/test_col_mat.osm"); + + Column_matrix MC_rw2 ; + + CGAL::OSM::read_matrix(MC_rw2, "tmp/test_col_mat.osm"); + + bool comp_col(MC_rw == MC_rw2) ; + std::cerr << "saved and loaded matrices comparison: " << comp_col << std::endl ; + assert(comp_col) ; + + std::cerr << "----> Row matrices" << std::endl; + + Row_matrix MR_rw(3,4); + CGAL::OSM::set_coefficient(MR_rw, 0, 1, 1) ; + CGAL::OSM::set_coefficient(MR_rw, 0, 2, -1) ; + CGAL::OSM::set_coefficient(MR_rw, 1, 0, 2) ; + CGAL::OSM::set_coefficient(MR_rw, 1, 2, -2) ; + + write_matrix(MR_rw, "tmp/test_row_mat.osm"); + + Row_matrix MR_rw2 ; + + CGAL::OSM::read_matrix(MR_rw2, "tmp/test_row_mat.osm"); + + bool comp_row(MR_rw == MR_rw2) ; + std::cerr << "saved and loaded matrices comparison: " << comp_row << std::endl ; + assert(comp_row) ; + } + + std::cerr << "-- Tests Sparse_matrices operations" << std::endl ; + + Column_chain a(4); + Row_chain b(4); + a.set_coefficient(1,1); + a.set_coefficient(3,3); + b.set_coefficient(0,2); + b.set_coefficient(1,-3); + + std::cerr << "---- Test column chain * row chain -> columnMajor matrix" << std::endl ; + + Column_matrix columnMajor = a * b, tmp = columnMajor; + + { + Column_matrix tmp ; + CGAL::OSM::read_matrix(tmp, "data/test_sparse_matrices/columnMajor.osm") ; + bool bres(tmp == columnMajor); + std::cerr << "column chain * row chain: " << bres << std::endl; + assert(bres) ; + } + + std::cerr << "---- Test column chain % row chain -> rowMajor matrix" << std::endl ; + + Row_matrix rowMajor = a % b; + + { + Row_matrix tmp ; + CGAL::OSM::read_matrix(tmp, "data/test_sparse_matrices/rowMajor.osm") ; + bool bres(tmp == rowMajor); + std::cerr << "column chain % row chain: " << bres << std::endl; + assert(bres) ; + } + + std::cerr << "---- Test column/row get" << std::endl ; + + std::cerr << "------ In Column_matrix" << std::endl ; + + { + Column_chain res(CGAL::OSM::get_column(columnMajor, 0)), tmp(4) ; + tmp.set_coefficient(1, 2) ; + tmp.set_coefficient(3, 6) ; + + bool bres (tmp == res) ; + std::cerr << "get_column 0 in columnMajor: " << bres << std::endl; + assert(bres) ; + } + + { + Row_chain res2(CGAL::OSM::get_row(columnMajor, 1)), tmp2(4) ; + tmp2.set_coefficient(0, 2) ; + tmp2.set_coefficient(1, -3) ; + + bool bres (tmp2 == res2) ; + std::cerr << "get_row 1 in columnMajor: " << bres << std::endl; + assert(bres); + } + + std::cerr << "------ In Row_matrix" << std::endl ; + + { + Column_chain res(CGAL::OSM::get_column(rowMajor, 0)), tmp(4) ; + tmp.set_coefficient(1, 2) ; + tmp.set_coefficient(3, 6) ; + + bool bres(tmp == res) ; + std::cerr << "get_column 0 in rowMajor: " << bres << std::endl; + assert(bres); + } + + { + Row_chain res2(CGAL::OSM::get_row(rowMajor, 1)), tmp2(4) ; + tmp2.set_coefficient(0, 2) ; + tmp2.set_coefficient(1, -3) ; + + bool bres(tmp2==res2); + std::cerr << "get_row 1 in columnMajor: " << bres << std::endl; + assert(bres); + } + + std::cerr << "---- Test column/row deletion" << std::endl; + + std::cerr << "------ In Column_matrix" << std::endl ; + + { + Column_matrix tmp(columnMajor), tmp2; + CGAL::OSM::remove_column(tmp, 0) ; + CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/columnMajor_del_col.osm"); + + bool bres(tmp == tmp2); + std::cerr << "deletion column 0 in columnMajor: " << bres << std::endl ; + assert(bres); + } + + { + Column_matrix tmp(columnMajor), tmp2; + CGAL::OSM::remove_row(tmp, 1) ; + CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/columnMajor_del_row.osm"); + bool bres(tmp == tmp2); + std::cerr << "deletion row 1 in columnMajor: " << bres << std::endl ; + assert(bres); + } + + std::cerr << "------ In Row_matrix" << std::endl ; + + { + Row_matrix tmp(rowMajor), tmp2; + CGAL::OSM::remove_row(tmp, 1) ; + CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/rowMajor_del_row.osm"); + bool bres(tmp==tmp2); + std::cerr << "deletion row 1 in rowMajor: " << bres << std::endl ; + assert(bres); + } + + { + Row_matrix tmp(rowMajor), tmp2; + CGAL::OSM::remove_column(tmp, 0) ; + CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/rowMajor_del_column.osm"); + bool bres(tmp == tmp2); + std::cerr << "deletion column 0 in rowMajor: " << bres << std::endl ; + assert(bres); + } + + // //////// Sum + std::cerr << "---- Test matrices sum" << std::endl ; + + // COL + COL + std::cerr << "------ Test Column_matrix + Column_matrix" << std::endl ; + + { + Column_matrix tmp(columnMajor), res, tmp2 ; + + CGAL::OSM::remove_coefficient(tmp, 1, 0); + CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; + + res = tmp + columnMajor ; + Column_matrix res2(tmp); + res2 += columnMajor ; + + CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/ColCol_sum.osm"); + bool bres(res == tmp2) ; + std::cerr << "sum columnMajor + otherColumnMajor: " << bres << std::endl ; + assert(bres); + bool bres2(res2 == tmp2) ; + std::cerr << "sum columnMajor += otherColumnMajor: " << bres2 << std::endl ; + assert(bres2); + } + + // COL + ROW + std::cerr << "------ Test Column_matrix + Row_matrix" << std::endl ; + + { + Column_matrix tmp(columnMajor), res, tmp2 ; + + CGAL::OSM::remove_coefficient(tmp, 1, 0); + CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; + + res = tmp + rowMajor ; + Column_matrix res2(tmp); + res2 += rowMajor ; + CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/ColRow_sum.osm"); + bool bres(tmp2 == res); + std::cerr << "sum otherColumnMajor + rowMajor: " << bres << std::endl ; + assert(bres); + bool bres2(tmp2 == res2); + std::cerr << "sum otherColumnMajor += rowMajor: " << bres2 << std::endl ; + assert(bres2); + } + + // ROW + COL + std::cerr << "------ Test Row_matrix + Column_matrix" << std::endl ; + + { + Row_matrix tmp(rowMajor), res, tmp2 ; + + CGAL::OSM::remove_coefficient(tmp, 1, 0); + CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; + + res = tmp + columnMajor ; + Row_matrix res2(tmp); + res2 += columnMajor ; + CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/RowCol_sum.osm"); + bool bres(tmp2 == res); + std::cerr << "sum otherRowMajor + columnMajor: " << bres << std::endl ; + assert(bres); + bool bres2(tmp2 == res2); + std::cerr << "sum otherRowMajor += columnMajor: " << bres2 << std::endl ; + assert(bres2); + } + + + // ROW + ROW + std::cerr << "------ Test Row_matrix + Row_matrix" << std::endl ; + + { + Row_matrix tmp(rowMajor), res, tmp2 ; + + CGAL::OSM::remove_coefficient(tmp, 1, 0); + CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; + + res = tmp + rowMajor ; + Row_matrix res2(tmp); + res2 += rowMajor; + CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/RowRow_sum.osm"); + bool bres(tmp2 == res); + std::cerr << "sum otherRowMajor + rowMajor: " << bres << std::endl ; + assert(bres); + bool bres2(tmp2 == res2); + std::cerr << "sum otherRowMajor += rowMajor: " << bres2 << std::endl ; + assert(bres2); + } + + // //////// Subtract + std::cerr << "---- Test matrices subtract" << std::endl ; + + // COL - COL + std::cerr << "------ Test Column_matrix - Column_matrix" << std::endl ; + + { + Column_matrix tmp(columnMajor), res, tmp2 ; + + CGAL::OSM::remove_coefficient(tmp, 1, 0); + CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; + + res = tmp - columnMajor ; + Column_matrix res2(tmp); + res2 -= columnMajor; + CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/ColCol_sub.osm"); + bool bres(res == tmp2) ; + std::cerr << "sub columnMajor - otherColumnMajor: " << bres << std::endl ; + assert(bres); + bool bres2(res2 == tmp2) ; + std::cerr << "sub columnMajor -= otherColumnMajor: " << bres2 << std::endl ; + assert(bres2); + } + + + // COL - ROW + std::cerr << "------ Test Column_matrix - Row_matrix" << std::endl ; + + { + Column_matrix tmp(columnMajor), res, tmp2 ; + + CGAL::OSM::remove_coefficient(tmp, 1, 0); + CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; + + res = tmp - rowMajor ; + Column_matrix res2(tmp); + res2 -= rowMajor; + CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/ColRow_sub.osm"); + bool bres(res == tmp2) ; + std::cerr << "sub otherColumnMajor - rowMajor: " << bres << std::endl ; + assert(bres); + bool bres2(res2 == tmp2) ; + std::cerr << "sub otherColumnMajor -= rowMajor: " << bres2 << std::endl ; + assert(bres2); + } + + // ROW - COL + std::cerr << "------ Test Row_matrix - Column_matrix" << std::endl ; + + { + Row_matrix tmp(rowMajor), res, tmp2 ; + + CGAL::OSM::remove_coefficient(tmp, 1, 0); + CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; + + res = tmp - columnMajor ; + Row_matrix res2(tmp); + res2 -= columnMajor; + CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/RowCol_sub.osm"); + bool bres(res == tmp2); + std::cerr << "sub otherRowMajor - columnMajor: " << bres << std::endl ; + assert(bres); + bool bres2(res2 == tmp2); + std::cerr << "sub otherRowMajor -= columnMajor: " << bres2 << std::endl ; + assert(bres2); + } + + // ROW - ROW + std::cerr << "------ Test Row_matrix - Row_matrix" << std::endl ; + + { + Row_matrix tmp(rowMajor), res, tmp2 ; + + CGAL::OSM::remove_coefficient(tmp, 1, 0); + CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; + + res = tmp - rowMajor ; + Row_matrix res2(tmp); + res2 -= rowMajor; + CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/RowRow_sub.osm"); + bool bres(res == tmp2); + std::cerr << "sub otherRowMajor - rowMajor: " << bres << std::endl ; + assert(bres); + bool bres2(res2 == tmp2); + std::cerr << "sub otherRowMajor -= rowMajor: " << bres2 << std::endl ; + assert(bres2); + } + + // //////// Product * (column result) + std::cerr << "---- Test matrices product *" << std::endl ; + + // COL * COL + std::cerr << "------ Test Column_matrix * Column_matrix" << std::endl ; + + { + Column_matrix res, tmp2 ; + Column_matrix tmp(columnMajor) ; + + CGAL::OSM::remove_coefficient(tmp, 1, 0); + CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; + + res = tmp * columnMajor ; + Column_matrix res2(tmp) ; + res2 *= columnMajor; + CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/ColCol_prod_col.osm"); + bool bres(res == tmp2); + std::cerr << "prod columnMajor * otherColumnMajor: " << bres << std::endl ; + assert(bres); + bool bres2(res2 == tmp2); + std::cerr << "prod columnMajor *= otherColumnMajor: " << bres2 << std::endl ; + assert(bres2); + } + + // COL * ROW + std::cerr << "------ Test Column_matrix * Row_matrix" << std::endl ; + + { + Column_matrix res, tmp2 ; + Column_matrix tmp(columnMajor) ; + + CGAL::OSM::remove_coefficient(tmp, 1, 0); + CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; + + res = tmp * rowMajor ; + Column_matrix res2(tmp); + res2 *= rowMajor; + CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/ColRow_prod_col.osm"); + bool bres(res == tmp2); + std::cerr << "prod otherColumnMajor * rowMajor: " << bres << std::endl ; + assert(bres); + bool bres2(res2 == tmp2); + std::cerr << "prod otherColumnMajor *= rowMajor: " << bres2 << std::endl ; + assert(bres2); + } + + // ROW * COL + std::cerr << "------ Test Row_matrix * Column_matrix" << std::endl ; + + { + Column_matrix res, tmp2 ; + Row_matrix tmp(rowMajor) ; + + CGAL::OSM::remove_coefficient(tmp, 1, 0); + CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; + + res = tmp * columnMajor ; + Column_matrix res2(tmp); + res2 *= columnMajor; + CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/RowCol_prod_col.osm"); + bool bres(res == tmp2); + std::cerr << "prod otherRowMajor * columnMajor: " << bres << std::endl ; + assert(bres); + bool bres2(res2 == tmp2); + std::cerr << "prod otherRowMajor *= columnMajor: " << bres2 << std::endl ; + assert(bres2); + } + + + // ROW * ROW + std::cerr << "------ Test Row_matrix * Row_matrix" << std::endl ; + + { + Column_matrix res, tmp2 ; + Row_matrix tmp(rowMajor) ; + + CGAL::OSM::remove_coefficient(tmp, 1, 0); + CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; + + res = tmp * rowMajor ; + Column_matrix res2(tmp) ; + res2 *= rowMajor; + CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/RowRow_prod_col.osm"); + bool bres(res == tmp2); + std::cerr << "prod otherRowMajor * rowMajor: " << bres << std::endl; + assert(bres); + bool bres2(res2 == tmp2); + std::cerr << "prod otherRowMajor *= rowMajor: " << bres2 << std::endl; + assert(bres2); + } + + // //////// Product % (row result) + std::cerr << "---- Test matrices product %" << std::endl ; + + // COL % COL + std::cerr << "------ Test Column_matrix % Column_matrix" << std::endl ; + + { + Row_matrix res, tmp2 ; + Column_matrix tmp(columnMajor) ; + + CGAL::OSM::remove_coefficient(tmp, 1, 0); + CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; + + res = tmp % columnMajor ; + CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/ColCol_prod_row.osm"); + bool bres(res == tmp2); + std::cerr << "prod columnMajor % otherColumnMajor: " << bres << std::endl ; + assert(bres); + } + + + // COL % ROW + std::cerr << "------ Test Column_matrix % Row_matrix" << std::endl ; + + { + Row_matrix res, tmp2 ; + Column_matrix tmp(columnMajor) ; + + CGAL::OSM::remove_coefficient(tmp, 1, 0); + CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; + + res = tmp % rowMajor ; + CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/ColRow_prod_row.osm"); + bool bres(res == tmp2); + std::cerr << "prod otherColumnMajor % rowMajor: " << bres << std::endl ; + assert(bres); + } + + // ROW % COL + std::cerr << "------ Test Row_matrix % Column_matrix" << std::endl ; + + { + Row_matrix res, tmp2; + Row_matrix tmp(rowMajor); + + CGAL::OSM::remove_coefficient(tmp, 1, 0); + CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; + + res = tmp % columnMajor ; + CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/RowCol_prod_row.osm"); + bool bres(res == tmp2); + std::cerr << "prod otherRowMajor % columnMajor: " << bres << std::endl ; + assert(bres); + } + + + // ROW % ROW + std::cerr << "------ Test Row_matrix % Row_matrix" << std::endl ; + + { + Row_matrix res, tmp2; + Row_matrix tmp(rowMajor); + + CGAL::OSM::remove_coefficient(tmp, 1, 0); + CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; + + res = tmp % rowMajor ; + CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/RowRow_prod_row.osm"); + bool bres(res == tmp2); + std::cerr << "prod otherRowMajor % rowMajor: " << bres << std::endl ; + assert(bres); + + } + + // //////// Transpose + std::cerr << "---- Test matrices transpose" << std::endl ; + + // COL^t + std::cerr << "------ Test Column_matrix transpose" << std::endl ; + + { + Row_matrix res = columnMajor.transpose(), tmp2 ; + CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/Col_transpose.osm"); + bool bres(res == tmp2); + std::cerr << "transpose columnMajor: " << bres << std::endl ; + assert(bres); + } + + // ROW^t + std::cerr << "------ Test Row_matrix transpose" << std::endl ; + + { + Column_matrix res = rowMajor.transpose(), tmp2 ; + CGAL::OSM::read_matrix(tmp2, "data/test_sparse_matrices/Row_transpose.osm"); + bool bres(res == tmp2); + std::cerr << "transpose rowMajor: " << bres << std::endl ; + assert(bres); + } + + // //////// Product scalar * matrix + std::cerr << "---- Test scalar - matrix and matrix - scalar product" << std::endl ; + + // COL + std::cerr << "------ Test 3 * Column_matrix and Column_matrix * 3" << std::endl ; + + { + Row_matrix tmp(rowMajor), tmp2(rowMajor), res1, res2, res3 ; + + // Build expected result + for (CGAL::OSM::Bitboard::iterator it = tmp2.begin(); it != tmp2.end(); ++it) + { + const Row_chain& tmp_row(CGAL::OSM::cget_row(tmp2, *it)); + for (Row_chain::const_iterator it2=tmp_row.cbegin(); it2 != tmp_row.cend(); ++it2) + { + CGAL::OSM::set_coefficient(tmp2, *it, it2->first, 3*it2->second); + } + } + + res1 = 3*tmp ; + res2 = tmp*3 ; + res3 = tmp ; + res3 *= 3 ; + bool bres1(res1 == tmp2); + std::cerr << "prod 3*rowMajor: " << bres1 << std::endl ; + assert(bres1); + bool bres2(res2 == tmp2); + std::cerr << "prod rowMajor*3: " << bres2 << std::endl ; + assert(bres2); + bool bres3(res3 == tmp2); + std::cerr << "prod rowMajor *= 3: " << bres3 << std::endl ; + assert(bres3); + } + + // ROW + std::cerr << "------ Test 3 * Row_matrix and Row_matrix * 3" << std::endl ; + + { + Column_matrix tmp(columnMajor), tmp2(columnMajor), res1, res2, res3 ; + + // Build expected result + for (CGAL::OSM::Bitboard::iterator it = tmp2.begin(); it != tmp2.end(); ++it) + { + const Column_chain& tmp_col(CGAL::OSM::cget_column(tmp2, *it)); + for (Column_chain::const_iterator it2=tmp_col.cbegin(); it2 != tmp_col.cend(); ++it2) + { + CGAL::OSM::set_coefficient(tmp2, it2->first, *it, 3*it2->second); + } + } + + res1 = 3*tmp ; + res2 = tmp*3 ; + res3 = tmp ; + res3 *= 3 ; + bool bres1(res1 == tmp2); + std::cerr << "prod 3*columnMajor: " << bres1 << std::endl ; + assert(bres1); + bool bres2(res2 == tmp2); + std::cerr << "prod columnMajor*3: " << bres2 << std::endl ; + assert(bres2); + bool bres3(res3 == tmp2); + std::cerr << "prod columnMajor *= 3: " << bres3 << std::endl ; + assert(bres3); + } + +// std::cout << "Multiplication de colonne (col/row dom):" << std::endl << colCol << std::endl << colRow << std::endl << std::endl; +// std::cout << "Multiplication de ligne (col/row dom):" << std::endl << rowCol << std::endl << rowRow << std::endl << std::endl; +// +// cout << "-------- Test parcours (init par set_coefficient)" << endl ; +// OSM::SparseMatrix Mtest(10, 10) ; +// Mtest.set_coefficient(1, 2, 1) ; +// Mtest.set_coefficient(3, 2, 1) ; +// Mtest.set_coefficient(1, 4, 1) ; +// Mtest.set_coefficient(2, 5, 1) ; +// for (OSM::Bitboard::iterator it = Mtest.begin(); it != Mtest.end(); ++it) +// { +// cout << "col : " << *it << endl ; +// } +// + return 0; +} + diff --git a/HDVF/test/HDVF/test_sparse_matrix_generate.cpp b/HDVF/test/HDVF/test_sparse_matrix_generate.cpp new file mode 100644 index 00000000000..f619631de97 --- /dev/null +++ b/HDVF/test/HDVF/test_sparse_matrix_generate.cpp @@ -0,0 +1,381 @@ +#include +#include +#include +#include +#include +#include +#include + +// TODO: use also results to check += -= *= ..... + +typedef int Coefficient_ring; +typedef CGAL::OSM::Sparse_chain Column_chain; +typedef CGAL::OSM::Sparse_chain Row_chain ; +typedef CGAL::OSM::Sparse_matrix Column_matrix; +typedef CGAL::OSM::Sparse_matrix Row_matrix; + + +int main(int argc, char **argv) +{ + + std::cerr << "-- Tests Sparse_matrices operations" << std::endl ; + + std::cerr << "---- Test column chain * row chain -> columnMajor matrix" << std::endl ; + + Column_chain a(4); + Row_chain b(4); + a.set_coefficient(1,1); + a.set_coefficient(3,3); + b.set_coefficient(0,2); + b.set_coefficient(1,-3); + + std::cerr << "---- Test column chain * row chain -> columnMajor matrix" << std::endl ; + + Column_matrix columnMajor = a * b; + + CGAL::OSM::write_matrix(columnMajor, "data/test_sparse_matrices/columnMajor.osm"); + + std::cout << "a * b: " << columnMajor << std::endl ; + + std::cerr << "---- Test column chain % row chain -> rowMajor matrix" << std::endl ; + + Row_matrix rowMajor = a % b; + + std::cout << "a % b: " << rowMajor << std::endl ; + + CGAL::OSM::write_matrix(rowMajor, "data/test_sparse_matrices/rowMajor.osm"); + + std::cerr << "---- Test column/row deletion" << std::endl; + + std::cerr << "------ In Column_matrix" << std::endl ; + + { + Column_matrix tmp(columnMajor); + CGAL::OSM::remove_column(tmp, 0) ; + std::cout << "deletion column 0 in columnMajor: " << tmp << std::endl ; + + CGAL::OSM::write_matrix(tmp, "data/test_sparse_matrices/columnMajor_del_col.osm"); + } + + { + Column_matrix tmp(columnMajor); + CGAL::OSM::remove_row(tmp, 1) ; + std::cout << "deletion row 1 in columnMajor: " << tmp << std::endl ; + + CGAL::OSM::write_matrix(tmp, "data/test_sparse_matrices/columnMajor_del_row.osm"); + } + + std::cerr << "------ In Row_matrix" << std::endl ; + + { + Row_matrix tmp(rowMajor); + CGAL::OSM::remove_row(tmp, 1) ; + std::cout << "deletion row 1 in rowMajor: " << tmp << std::endl ; + + CGAL::OSM::write_matrix(tmp, "data/test_sparse_matrices/rowMajor_del_row.osm"); + } + + { + Row_matrix tmp(rowMajor); + CGAL::OSM::remove_column(tmp, 0) ; + std::cout << "deletion column 0 in rowMajor: " << tmp << std::endl ; + + CGAL::OSM::write_matrix(tmp, "data/test_sparse_matrices/rowMajor_del_column.osm"); + } + + // //////// Sum + std::cerr << "---- Test matrices sum" << std::endl ; + + // COL + COL + std::cerr << "------ Test Column_matrix + Column_matrix" << std::endl ; + + { + Column_matrix tmp(columnMajor), res ; + + CGAL::OSM::remove_coefficient(tmp, 1, 0); + CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; + + res = tmp + columnMajor ; + std::cout << "sum columnMajor + otherColumnMajor: " << res << std::endl ; + + CGAL::OSM::write_matrix(res, "data/test_sparse_matrices/ColCol_sum.osm"); + } + + + // COL + ROW + std::cerr << "------ Test Column_matrix + Row_matrix" << std::endl ; + + { + Column_matrix tmp(columnMajor), res ; + + CGAL::OSM::remove_coefficient(tmp, 1, 0); + CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; + + res = tmp + rowMajor ; + std::cout << "sum otherColumnMajor + rowMajor: " << res << std::endl ; + + CGAL::OSM::write_matrix(res, "data/test_sparse_matrices/ColRow_sum.osm"); + } + + // ROW + COL + std::cerr << "------ Test Row_matrix + Column_matrix" << std::endl ; + + { + Row_matrix tmp(rowMajor), res ; + + CGAL::OSM::remove_coefficient(tmp, 1, 0); + CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; + + res = tmp + columnMajor ; + std::cout << "sum otherRowMajor + columnMajor: " << res << std::endl ; + + CGAL::OSM::write_matrix(res, "data/test_sparse_matrices/RowCol_sum.osm"); + } + + + // ROW + ROW + std::cerr << "------ Test Row_matrix + Row_matrix" << std::endl ; + + { + Row_matrix tmp(rowMajor), res ; + + CGAL::OSM::remove_coefficient(tmp, 1, 0); + CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; + + res = tmp + rowMajor ; + std::cout << "sum otherRowMajor + rowMajor: " << res << std::endl ; + + CGAL::OSM::write_matrix(res, "data/test_sparse_matrices/RowRow_sum.osm"); + } + + // //////// Subtract + std::cerr << "---- Test matrices subtract" << std::endl ; + + // COL - COL + std::cerr << "------ Test Column_matrix - Column_matrix" << std::endl ; + + { + Column_matrix tmp(columnMajor), res ; + + CGAL::OSM::remove_coefficient(tmp, 1, 0); + CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; + + res = tmp - columnMajor ; + std::cout << "sub columnMajor - otherColumnMajor: " << res << std::endl ; + + CGAL::OSM::write_matrix(res, "data/test_sparse_matrices/ColCol_sub.osm"); + } + + + // COL - ROW + std::cerr << "------ Test Column_matrix - Row_matrix" << std::endl ; + + { + Column_matrix tmp(columnMajor), res ; + + CGAL::OSM::remove_coefficient(tmp, 1, 0); + CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; + + res = tmp - rowMajor ; + std::cout << "sub otherColumnMajor - rowMajor: " << res << std::endl ; + + CGAL::OSM::write_matrix(res, "data/test_sparse_matrices/ColRow_sub.osm"); + } + + // ROW - COL + std::cerr << "------ Test Row_matrix - Column_matrix" << std::endl ; + + { + Row_matrix tmp(rowMajor), res ; + + CGAL::OSM::remove_coefficient(tmp, 1, 0); + CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; + + res = tmp - columnMajor ; + std::cout << "sub otherRowMajor - columnMajor: " << res << std::endl ; + + CGAL::OSM::write_matrix(res, "data/test_sparse_matrices/RowCol_sub.osm"); + } + + + // ROW - ROW + std::cerr << "------ Test Row_matrix - Row_matrix" << std::endl ; + + { + Row_matrix tmp(rowMajor), res ; + + CGAL::OSM::remove_coefficient(tmp, 1, 0); + CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; + + res = tmp - rowMajor ; + std::cout << "sub otherRowMajor - rowMajor: " << res << std::endl ; + + CGAL::OSM::write_matrix(res, "data/test_sparse_matrices/RowRow_sub.osm"); + } + + // //////// Product * (column result) + std::cerr << "---- Test matrices product *" << std::endl ; + + // COL * COL + std::cerr << "------ Test Column_matrix * Column_matrix" << std::endl ; + + { + Column_matrix res ; + Column_matrix tmp(columnMajor) ; + + CGAL::OSM::remove_coefficient(tmp, 1, 0); + CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; + + res = tmp * columnMajor ; + std::cout << "prod columnMajor * otherColumnMajor: " << res << std::endl ; + + CGAL::OSM::write_matrix(res, "data/test_sparse_matrices/ColCol_prod_col.osm"); + } + + + // COL * ROW + std::cerr << "------ Test Column_matrix * Row_matrix" << std::endl ; + + { + Column_matrix res ; + Column_matrix tmp(columnMajor) ; + + CGAL::OSM::remove_coefficient(tmp, 1, 0); + CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; + + res = tmp * rowMajor ; + std::cout << "prod otherColumnMajor * rowMajor: " << res << std::endl ; + + CGAL::OSM::write_matrix(res, "data/test_sparse_matrices/ColRow_prod_col.osm"); + } + + // ROW * COL + std::cerr << "------ Test Row_matrix * Column_matrix" << std::endl ; + + { + Column_matrix res ; + Row_matrix tmp(rowMajor) ; + + CGAL::OSM::remove_coefficient(tmp, 1, 0); + CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; + + res = tmp * columnMajor ; + std::cout << "prod otherRowMajor * columnMajor: " << res << std::endl ; + + CGAL::OSM::write_matrix(res, "data/test_sparse_matrices/RowCol_prod_col.osm"); + } + + + // ROW * ROW + std::cerr << "------ Test Row_matrix * Row_matrix" << std::endl ; + + { + Column_matrix res ; + Row_matrix tmp(rowMajor) ; + + CGAL::OSM::remove_coefficient(tmp, 1, 0); + CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; + + res = tmp * rowMajor ; + std::cout << "prod otherRowMajor * rowMajor: " << res << std::endl ; + + CGAL::OSM::write_matrix(res, "data/test_sparse_matrices/RowRow_prod_col.osm"); + } + + // //////// Product % (row result) + std::cerr << "---- Test matrices product %" << std::endl ; + + // COL % COL + std::cerr << "------ Test Column_matrix % Column_matrix" << std::endl ; + + { + Row_matrix res ; + Column_matrix tmp(columnMajor) ; + + CGAL::OSM::remove_coefficient(tmp, 1, 0); + CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; + + res = tmp % columnMajor ; + std::cout << "prod columnMajor % otherColumnMajor: " << res << std::endl ; + + CGAL::OSM::write_matrix(res, "data/test_sparse_matrices/ColCol_prod_row.osm"); + } + + + // COL % ROW + std::cerr << "------ Test Column_matrix % Row_matrix" << std::endl ; + + { + Row_matrix res ; + Column_matrix tmp(columnMajor) ; + + CGAL::OSM::remove_coefficient(tmp, 1, 0); + CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; + + res = tmp % rowMajor ; + std::cout << "prod otherColumnMajor % rowMajor: " << res << std::endl ; + + CGAL::OSM::write_matrix(res, "data/test_sparse_matrices/ColRow_prod_row.osm"); + } + + // ROW % COL + std::cerr << "------ Test Row_matrix % Column_matrix" << std::endl ; + + { + Row_matrix res; + Row_matrix tmp(rowMajor); + + CGAL::OSM::remove_coefficient(tmp, 1, 0); + CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; + + res = tmp % columnMajor ; + std::cout << "prod otherRowMajor % columnMajor: " << res << std::endl ; + + CGAL::OSM::write_matrix(res, "data/test_sparse_matrices/RowCol_prod_row.osm"); + } + + + // ROW % ROW + std::cerr << "------ Test Row_matrix % Row_matrix" << std::endl ; + + { + Row_matrix res; + Row_matrix tmp(rowMajor); + + CGAL::OSM::remove_coefficient(tmp, 1, 0); + CGAL::OSM::set_coefficient(tmp, 2, 2, -2) ; + + res = tmp % rowMajor ; + std::cout << "prod otherRowMajor % rowMajor: " << res << std::endl ; + + CGAL::OSM::write_matrix(res, "data/test_sparse_matrices/RowRow_prod_row.osm"); + } + + // //////// Transpose + std::cerr << "---- Test matrices transpose" << std::endl ; + + // COL^t + std::cerr << "------ Test Column_matrix transpose" << std::endl ; + + { + Row_matrix res = columnMajor.transpose() ; + + std::cout << "transpose columnMajor: " << res << std::endl ; + + CGAL::OSM::write_matrix(res, "data/test_sparse_matrices/Col_transpose.osm"); + } + + // ROW^t + std::cerr << "------ Test Row_matrix transpose" << std::endl ; + + { + Column_matrix res = rowMajor.transpose() ; + + std::cout << "transpose rowMajor: " << res << std::endl ; + + CGAL::OSM::write_matrix(res, "data/test_sparse_matrices/Row_transpose.osm"); + } + + return 0; +} + diff --git a/HDVF/test/HDVF/test_surface_mesh_io.cpp b/HDVF/test/HDVF/test_surface_mesh_io.cpp new file mode 100644 index 00000000000..297153e3ed8 --- /dev/null +++ b/HDVF/test/HDVF/test_surface_mesh_io.cpp @@ -0,0 +1,31 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace HDVF = CGAL::Homological_discrete_vector_field; + +typedef CGAL::Simple_cartesian K; +typedef CGAL::Surface_mesh SurfaceMesh; +typedef HDVF::Hdvf_traits_3 Traits; +typedef CGAL::Homological_discrete_vector_field::Surface_mesh_io SurfaceMeshIO; + +int main() { + SurfaceMesh mesh; + std::string filename("data/three_triangles.off"); + + CGAL::IO::read_polygon_mesh(filename, mesh); + + SurfaceMeshIO mesh_io(mesh); + + return 0; +} + + diff --git a/HDVF/test/HDVF/test_triangulation_3_io.cpp b/HDVF/test/HDVF/test_triangulation_3_io.cpp new file mode 100644 index 00000000000..2e895d52f93 --- /dev/null +++ b/HDVF/test/HDVF/test_triangulation_3_io.cpp @@ -0,0 +1,53 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace HDVF = CGAL::Homological_discrete_vector_field; + +typedef int Coefficient_ring; + +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; + +typedef CGAL::Triangulation_3 Triangulation; + +typedef Triangulation::Cell_handle Cell_handle; +typedef Triangulation::Vertex_handle Vertex_handle; +typedef Triangulation::Locate_type Locate_type; +typedef Triangulation::Point Point; + +typedef HDVF::Hdvf_traits_3 Traits; +typedef HDVF::Triangulation_3_io Triangulation_3_io; +typedef HDVF::Simplicial_chain_complex Complex; + +int main() { + // construction from a list of points : + std::list L; + L.push_front(Point(0,0,0)); + L.push_front(Point(1,0,0)); + L.push_front(Point(0,1,0)); + L.push_front(Point(0,0,1)); + L.push_front(Point(1,1,1)); + L.push_front(Point(2,2,2)); + + Triangulation T(L.begin(), L.end()); + + Triangulation_3_io tri3_io(T); + Complex complex(tri3_io); + + std::cout << "Complex built: " << complex << std::endl; + Complex::chain_complex_to_vtk(complex, "tmp/complex.vtk"); + + return 0; +} + + diff --git a/Installation/include/CGAL/license/HDVF.h b/Installation/include/CGAL/license/HDVF.h new file mode 100644 index 00000000000..66225bf20f3 --- /dev/null +++ b/Installation/include/CGAL/license/HDVF.h @@ -0,0 +1,54 @@ +// Copyright (c) 2016 GeometryFactory SARL (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org) +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Andreas Fabri +// +// Warning: this file is generated, see include/CGAL/license/README.md + +#ifndef CGAL_LICENSE_HDVF_H +#define CGAL_LICENSE_HDVF_H + +#include +#include + +#ifdef CGAL_HDVF_COMMERCIAL_LICENSE + +# if CGAL_HDVF_COMMERCIAL_LICENSE < CGAL_RELEASE_DATE + +# if defined(CGAL_LICENSE_WARNING) + + CGAL_pragma_warning("Your commercial license for CGAL does not cover " + "this release of the Homological Discrete Vector Fields package.") +# endif + +# ifdef CGAL_LICENSE_ERROR +# error "Your commercial license for CGAL does not cover this release \ + of the Homological Discrete Vector Fields package. \ + You get this error, as you defined CGAL_LICENSE_ERROR." +# endif // CGAL_LICENSE_ERROR + +# endif // CGAL_HDVF_COMMERCIAL_LICENSE < CGAL_RELEASE_DATE + +#else // no CGAL_HDVF_COMMERCIAL_LICENSE + +# if defined(CGAL_LICENSE_WARNING) + CGAL_pragma_warning("\nThe macro CGAL_HDVF_COMMERCIAL_LICENSE is not defined." + "\nYou use the CGAL Homological Discrete Vector Fields package under " + "the terms of the GPLv3+.") +# endif // CGAL_LICENSE_WARNING + +# ifdef CGAL_LICENSE_ERROR +# error "The macro CGAL_HDVF_COMMERCIAL_LICENSE is not defined.\ + You use the CGAL Homological Discrete Vector Fields package under the terms of \ + the GPLv3+. You get this error, as you defined CGAL_LICENSE_ERROR." +# endif // CGAL_LICENSE_ERROR + +#endif // no CGAL_HDVF_COMMERCIAL_LICENSE + +#endif // CGAL_LICENSE_HDVF_H diff --git a/Number_types/doc/Number_types/Doxyfile.in b/Number_types/doc/Number_types/Doxyfile.in index 7a5e80e121f..4086ff6b199 100644 --- a/Number_types/doc/Number_types/Doxyfile.in +++ b/Number_types/doc/Number_types/Doxyfile.in @@ -2,6 +2,8 @@ PROJECT_NAME = "CGAL ${CGAL_DOC_VERSION} - Number Types" INPUT += ${CGAL_PACKAGE_INCLUDE_DIR}/CGAL/Exact_integer.h \ - ${CGAL_PACKAGE_INCLUDE_DIR}/CGAL/Exact_rational.h + ${CGAL_PACKAGE_INCLUDE_DIR}/CGAL/Exact_rational.h \ + ${CGAL_PACKAGE_INCLUDE_DIR}/CGAL/Zp.h \ + ${CGAL_PACKAGE_INCLUDE_DIR}/CGAL/Z2.h SHOW_FILES = YES diff --git a/Number_types/doc/Number_types/PackageDescription.txt b/Number_types/doc/Number_types/PackageDescription.txt index c3a293a5715..3f067792e7d 100644 --- a/Number_types/doc/Number_types/PackageDescription.txt +++ b/Number_types/doc/Number_types/PackageDescription.txt @@ -100,6 +100,8 @@ - `CGAL::Number_type_checker` - `CGAL::Exact_integer` - `CGAL::Exact_rational` +- `CGAL::Z2` +- `CGAL::Zp` \cgalCRPSection{Relates Rational} diff --git a/Number_types/include/CGAL/Z2.h b/Number_types/include/CGAL/Z2.h new file mode 100644 index 00000000000..242e8b0aacf --- /dev/null +++ b/Number_types/include/CGAL/Z2.h @@ -0,0 +1,202 @@ +// Copyright (c) 2025 LIS Marseille (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Alexandra Bac + +#ifndef CGAL_Z2_H +#define CGAL_Z2_H + +#include +#include + +#include +#include + +namespace CGAL { + +/*! + + The class `Z2` implements the concept `IntegralDomainWithoutDivision` with the field \f$\mathbb Z/2\mathbb Z\f$. This implementation is optimized to use bitwise operations and should be prefered to `Zp<2>`. + + \cgalModels{IntegralDomainWithoutDivision} + */ + +class Z2 { + char _i ; +public: + + /** \brief Constructor from a value */ + Z2(char i=0) : _i(i ? 1 : 0 ) {} + + // Copy constructor + Z2(const Z2& a) : _i(a._i) {} + + /** \brief Returns 2 (ie.\ p such that `Z2`=\f$\mathbb Z/2\mathbb Z\f$). */ + static char operator() () { return char(2); } + + bool is_zero() const { return _i == 0 ; } + + /** \brief Unary operator+ */ + friend Z2 operator+ (const Z2& a) + { + return a ; + } + + /** \brief Unary operator-. */ + friend Z2 operator- (const Z2& a) + { + return a ; + } + + /** \brief Operator+. */ + friend Z2 operator+ (const Z2& a, const Z2& b) + { + return Z2((a._i^b._i)) ; + } + + /** \brief Operator-. */ + friend Z2 operator- (const Z2& a, const Z2& b) + { + return Z2(a._i^b._i) ; + } + + /** \brief Operator*. */ + friend Z2 operator* (const Z2& a, const Z2& b) + { + return Z2(a._i & b._i) ; + } + + /** \brief Operator/. */ + friend Z2 operator/ (const Z2& a, const Z2& b) + { + return Z2(a._i / b._i) ; + } + + /** \brief Operator+=. */ + Z2 & operator+= (const Z2& a) + { + _i ^= a._i ; + return *this ; + } + + /** \brief Operator-=. */ + Z2 & operator-= (const Z2& a) + { + _i ^= a._i ; + return *this ; + } + + /** \brief Operator*=. */ + Z2 & operator*= (const Z2& a) + { + _i &= a._i ; + return *this ; + } + + /** \brief Operator/=. */ + Z2 & operator/= (const Z2& a) + { + _i /= a._i ; + return *this ; + } + + /** \brief Operator==. */ + friend bool operator== (const Z2& a, const Z2& b) + { + return (a._i == b._i) ; + } + + /** \brief Operator!=. */ + friend bool operator!= (const Z2& a, const Z2& b) + { + return (a._i != b._i); + } +#if 0 + /** \brief Absolute value. */ + friend Z2 abs(const Z2& a) + { + return a ; + } +#endif + /** \brief Operator<<. */ + friend std::ostream& operator<<(std::ostream& out, const Z2& a) + { + return (out << (a._i ? 1 : 0)) ; + } + + /** \brief Operator>>. */ + friend std::istream& operator>>(std::istream& in, Z2& a) + { + int tmp ; + in >> tmp ; + a = Z2(tmp) ; + return (in) ; + } +}; + + + + + +template <> class Algebraic_structure_traits< Z2 > + : public Algebraic_structure_traits_base< Z2, Integral_domain_without_division_tag > { + public: + typedef Tag_true Is_exact; + typedef Tag_false Is_numerical_sensitive; + + class Is_square + : public CGAL::cpp98::unary_function< Type, bool > { + public: + bool operator()( const Type& ) const { + return true; + } + }; + }; + + template <> class Real_embeddable_traits< Z2 > + : public INTERN_RET::Real_embeddable_traits_base< Z2 , CGAL::Tag_true > { + public: + + class Is_positive + : public CGAL::cpp98::unary_function< Type, bool > { + public: + bool operator()( const Type& x_) const { + return !x_.is_zero() ; + } + }; + + class Is_negative + : public CGAL::cpp98::unary_function< Type, bool > { + public: + bool operator()( const Type&) const { + return false; + } + }; + + class Sgn + : public CGAL::cpp98::unary_function< Type, ::CGAL::Sign > { + public: + ::CGAL::Sign operator()( const Type& x ) const { + if(x.is_zero()) return ZERO ; + else return POSITIVE ; + } + }; + + class Abs + : public CGAL::cpp98::unary_function< Type, Type > { + public: + Type operator()( const Type& x ) const { + return x ; + } + }; + }; + +} /* end namespace CGAL */ + +#endif //CGAL_Z2_H diff --git a/Number_types/include/CGAL/Zp.h b/Number_types/include/CGAL/Zp.h new file mode 100644 index 00000000000..c0e5f107171 --- /dev/null +++ b/Number_types/include/CGAL/Zp.h @@ -0,0 +1,259 @@ +// Copyright (c) 2025 LIS Marseille (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Alexandra Bac + + +#ifndef CGAL_HDVF_ZP_H +#define CGAL_HDVF_ZP_H + + +#include +#include + +#include + +namespace CGAL { + +/*! + \ingroup PkgHDVFAlgorithmClasses + + The class `Zp` implements the concept `IntegralDomainWithoutDivision` with the ring \f$\mathbb Z/p\mathbb Z\f$ (which is a field when `p`is prime). This is a "lightweight" implementation which aims at providing fast operations and constructors. + + According to the value of `p`, users can chose the size of the representation used to store values (default size: `int`). + + \warning For \f$\mathbb Z/2\mathbb Z\f$, prefer the class `Z2` which is optimized. + + \cgalModels{IntegralDomainWithoutDivision} + + \tparam p a positive integer. + \tparam T a type used for the inner storage of the values (default: `int`). + \tparam IsPrime a `bool` encoding wether `p` is a prime number or not. + */ + +template +class Zp { + T _i ; +public: + + /** \brief Constructor from a value */ + Zp(T i=0) : _i( (i>=0)?(i % T(p)):((i % T(p)) + T(p)) ) { } + + // Copy constructor + Zp(const Zp& a) : _i(a._i) {} + + /** \brief Returns the value of p. */ + static T operator() () { return T(p); } + + /** \brief Tests if the element is 0. */ + bool is_zero() const { return _i == 0 ; } + + /** \brief Operator=. */ + Zp& operator= (const Zp& a) { + _i = a._i; + return *this; + } + + /** \brief Unary operator+ */ + friend Zp operator+ (const Zp& a) + { + return Zp(a) ; + } + + /** \brief Unary operator-. */ + friend Zp operator- (const Zp& a) + { + return Zp(T(p) - a._i) ; + } + + /** \brief Operator+. */ + friend Zp operator+ (const Zp& a, const Zp& b) + { + return Zp(a._i + b._i) ; + } + + /** \brief Operator-. */ + friend Zp operator- (const Zp& a, const Zp& b) { + if (a._i >= b._i) + return Zp(a._i - b._i) ; + else + return Zp((T(p) - b._i) + a._i); + } + + /** \brief Operator*. */ + friend Zp operator* (const Zp& a, const Zp& b) + { + return Zp(a._i * b._i) ; + } + + /** \brief Operator/. */ + friend Zp operator/ (const Zp& a, const Zp& b) + { + return Zp(a._i / b._i) ; + } + + /** \brief Operator+=. */ + Zp & operator+= (const Zp& a) + { + _i += a._i; + _i %= T(p) ; + return *this ; + } + + /** \brief Operator-=. */ + Zp & operator-= (const Zp& a) + { + if (_i >= a._i) + _i -= a._i ; + else + { + _i += (T(p) - a._i) ; + } + return *this ; + } + + /** \brief Operator*=. */ + Zp & operator*= (const Zp& a) + { + _i *= a._i ; + _i %= T(p) ; + return *this ; + } + + /** \brief Operator/=. */ + Zp & operator/= (const Zp& a) + { + _i /= a._i ; + _i %= T(p) ; + return *this ; + } + + /** \brief Operator==. */ + friend bool operator== (const Zp& a, const Zp& b) + { + return (a._i == b._i) ; + } + + /** \brief Operator!=. */ + friend bool operator!= (const Zp& a, const Zp& b) + { + return (a._i != b._i); + } + + /** \brief Absolute value. */ + friend Zp abs(const Zp& a) + { + return Zp(a) ; + } + + /** \brief Operator<<. */ + friend std::ostream& operator<<(std::ostream& out, const Zp& a) + { + return (out << int(a._i)) ; + } + + /** \brief Operator>>. */ + friend std::istream& operator>>(std::istream& in, Zp& a) + { + int tmp ; + in >> tmp ; + a = Zp(tmp) ; + return (in) ; + } + + /** \brief Returns the invertibility of the element. */ + bool is_invertible() { + if (IsPrime) + return (_i != 0); + else + { + // TODO: optimize with static data + return (std::gcd(_i,p) == 1); + } + } +}; + + +// Specialization for p being not a prime number +template class Algebraic_structure_traits< Zp > + : public Algebraic_structure_traits_base< Zp, Integral_domain_without_division_tag > { + public: + typedef Tag_true Is_exact; + typedef Tag_false Is_numerical_sensitive; + typedef Zp Type; + + class Is_invertible // AF: Does not yet exist in Number_types and Algebraic_foundations + : public CGAL::cpp98::unary_function< Type, bool > { + public: + bool operator()( const Type& t) const { + return t.is_invertible() ; + } + }; + }; + + + // Specialization for p being not a prime number + template class Algebraic_structure_traits< Zp > + : public Algebraic_structure_traits_base< Zp, Field_tag > { + public: + typedef Tag_true Is_exact; + typedef Tag_false Is_numerical_sensitive; + typedef Zp Type; + + class Is_invertible + : public CGAL::cpp98::unary_function< Type, bool > { + public: + bool operator()( const Type& t) const { + return ! t.is_zero() ; + } + }; + }; + +template class Real_embeddable_traits< Zp > + : public INTERN_RET::Real_embeddable_traits_base< Zp , CGAL::Tag_true > { + typedef Zp Type; + public: + + class Is_positive + : public CGAL::cpp98::unary_function< Type, bool > { + public: + bool operator()( const Type& t) const { + return ! t.is_zero() ; + } + }; + + class Is_negative + : public CGAL::cpp98::unary_function< Type, bool > { + public: + bool operator()( const Type&) const { + return false; + } + }; + + class Sgn + : public CGAL::cpp98::unary_function< Type, ::CGAL::Sign > { + public: + ::CGAL::Sign operator()( const Type& t ) const { + if(t.is_zero()) return ZERO ; + else return POSITIVE ; + } + }; + + class Abs + : public CGAL::cpp98::unary_function< Type, Type > { + public: + Type operator()( const Type& t ) const { + return t ; + } + }; + }; + +} /* end namespace CGAL */ + +#endif // CGAL_HDVF_ZP_H diff --git a/Number_types/test/Number_types/CMakeLists.txt b/Number_types/test/Number_types/CMakeLists.txt index de674e51c93..0ef8705378a 100644 --- a/Number_types/test/Number_types/CMakeLists.txt +++ b/Number_types/test/Number_types/CMakeLists.txt @@ -23,6 +23,7 @@ create_single_source_cgal_program("Gmpq.cpp") create_single_source_cgal_program("Gmpq_new.cpp") create_single_source_cgal_program("Gmpz.cpp") create_single_source_cgal_program("Gmpzf_new.cpp") +create_single_source_cgal_program("Zp.cpp") create_single_source_cgal_program("int.cpp") create_single_source_cgal_program("Interval_nt.cpp") create_single_source_cgal_program("Interval_nt_nearest.cpp") diff --git a/Number_types/test/Number_types/Zp.cpp b/Number_types/test/Number_types/Zp.cpp new file mode 100644 index 00000000000..2a165bd66ec --- /dev/null +++ b/Number_types/test/Number_types/Zp.cpp @@ -0,0 +1,261 @@ +#include +#include +#include +#include +#include +#include +#include + + +typedef CGAL::Zp<5, char, true> Coefficient_field; +typedef CGAL::Zp<12, char, false> Coefficient_ring; + +typedef CGAL::Algebraic_structure_traits ATCoefs_field; +typedef CGAL::Algebraic_structure_traits ATCoefs_ring; + +int main() { + Coefficient_field f1(1), f2(3), f0(0), fp((Coefficient_field())); + Coefficient_ring r1(1), r2(3), r0(0), rp((Coefficient_ring())); + + std::cerr << "-- Test Zp ==" << std::endl; + { + std::cerr << "----> Field Z5" << std::endl; + bool comp_true(Coefficient_field(2)==Coefficient_field(7)) ; + std::cerr << "Test 2==7: " << comp_true << std::endl ; + assert(comp_true) ; + bool comp_false(Coefficient_field(2)==Coefficient_field(-2)) ; + std::cerr << "Test 2==-2: " << comp_false << std::endl ; + assert(!comp_false) ; + } + { + std::cerr << "----> Ring Z12" << std::endl; + bool comp_true(Coefficient_ring(2) == Coefficient_ring(14)) ; + std::cerr << "Test 2==14: " << comp_true << std::endl ; + assert(comp_true) ; + bool comp_false(Coefficient_ring(2) == Coefficient_ring(-2)) ; + std::cerr << "Test 2==-2: " << comp_false << std::endl ; + assert(!comp_false) ; + } + + std::cerr << "-- Test Zp !=" << std::endl; + { + std::cerr << "----> Field Z5" << std::endl; + bool comp_true(Coefficient_field(2)!=Coefficient_field(-2)) ; + std::cerr << "Test 2 != -2: " << comp_true << std::endl ; + assert(comp_true) ; + bool comp_false(Coefficient_field(2)!=Coefficient_field(7)) ; + std::cerr << "Test 2 != 7: " << comp_false << std::endl ; + assert(!comp_false) ; + } + { + std::cerr << "----> Ring Z12" << std::endl; + bool comp_true(Coefficient_ring(2) != Coefficient_ring(-2)) ; + std::cerr << "Test 2!=-2: " << comp_true << std::endl ; + assert(comp_true) ; + bool comp_false(Coefficient_ring(2) != Coefficient_ring(14)) ; + std::cerr << "Test 2!=14: " << comp_false << std::endl ; + assert(!comp_false) ; + } + + std::cerr << "-- Test Zp is_zero" << std::endl; + { + std::cerr << "----> Field Z5" << std::endl; + bool comp_true(fp.is_zero()) ; + std::cerr << "Test zero element: " << comp_true << std::endl ; + assert(comp_true) ; + bool comp_false(f1.is_zero()) ; + std::cerr << "Test non zero element: " << comp_false << std::endl ; + assert(!comp_false) ; + } + { + std::cerr << "----> Ring Z12" << std::endl; + bool comp_true(rp.is_zero()) ; + std::cerr << "Test zero element: " << comp_true << std::endl ; + assert(comp_true) ; + bool comp_false(r1.is_zero()) ; + std::cerr << "Test non zero element: " << comp_false << std::endl ; + assert(!comp_false) ; + } + + std::cerr << "-- Test Zp unary operator+" << std::endl; + { + std::cerr << "----> Field Z5" << std::endl; + bool comp_true((+Coefficient_field(2)) == Coefficient_field(2)) ; + std::cerr << "Test +2 == 2: " << comp_true << std::endl ; + assert(comp_true) ; + } + { + std::cerr << "----> Ring Z12" << std::endl; + bool comp_true((+Coefficient_ring(6)) == Coefficient_ring(6)) ; + std::cerr << "Test +6 == 6: " << comp_true << std::endl ; + assert(comp_true) ; + } + + std::cerr << "-- Test Zp unary operator-" << std::endl; + { + std::cerr << "----> Field Z5" << std::endl; + bool comp_true((-Coefficient_field(2)) == Coefficient_field(3)) ; + std::cerr << "Test -2 == 3: " << comp_true << std::endl ; + assert(comp_true) ; + } + { + std::cerr << "----> Ring Z12" << std::endl; + bool comp_true((-Coefficient_ring(5)) == Coefficient_ring(7)) ; + std::cerr << "Test -5 == 7: " << comp_true << std::endl ; + assert(comp_true) ; + } + + std::cerr << "-- Test Zp operator+" << std::endl; + { + std::cerr << "----> Field Z5" << std::endl; + bool comp_true((Coefficient_field(2)+Coefficient_field(4)) == Coefficient_field(1)) ; + std::cerr << "Test 2+4 == 1: " << comp_true << std::endl ; + assert(comp_true) ; + } + { + std::cerr << "----> Ring Z12" << std::endl; + bool comp_true((Coefficient_ring(6)+Coefficient_ring(8)) == Coefficient_ring(2)) ; + std::cerr << "Test 6+8 == 2: " << comp_true << std::endl ; + assert(comp_true) ; + } + + std::cerr << "-- Test Zp operator-" << std::endl; + { + std::cerr << "----> Field Z5" << std::endl; + bool comp_true((Coefficient_field(2)-Coefficient_field(4)) == Coefficient_field(-2)) ; + std::cerr << "Test 2-4 == -2: " << comp_true << std::endl ; + assert(comp_true) ; + } + { + std::cerr << "----> Ring Z12" << std::endl; + bool comp_true((Coefficient_ring(6)-Coefficient_ring(8)) == Coefficient_ring(-2)) ; + std::cerr << "Test 6-8 == -2: " << comp_true << std::endl ; + assert(comp_true) ; + } + + std::cerr << "-- Test Zp operator*" << std::endl; + { + std::cerr << "----> Field Z5" << std::endl; + bool comp_true((Coefficient_field(2)*Coefficient_field(4)) == Coefficient_field(3)) ; + std::cerr << "Test 2*4 == 3: " << comp_true << std::endl ; + assert(comp_true) ; + } + { + std::cerr << "----> Ring Z12" << std::endl; + bool comp_true((Coefficient_ring(6)*Coefficient_ring(7)) == Coefficient_ring(6)) ; + std::cerr << "Test 6*7 == 6: " << comp_true << std::endl ; + assert(comp_true) ; + } + + std::cerr << "-- Test Zp operator/" << std::endl; + { + std::cerr << "----> Field Z5" << std::endl; + bool comp_true((Coefficient_field(4)/Coefficient_field(3)) == Coefficient_field(1)) ; + std::cerr << "Test 4/3 == 1: " << comp_true << std::endl ; + assert(comp_true) ; + } + { + std::cerr << "----> Ring Z12" << std::endl; + bool comp_true((Coefficient_ring(8)/Coefficient_ring(3)) == Coefficient_ring(2)) ; + std::cerr << "Test 8/3 == 2: " << comp_true << std::endl ; + assert(comp_true) ; + } + + std::cerr << "-- Test Zp operator+=" << std::endl; + { + std::cerr << "----> Field Z5" << std::endl; + Coefficient_field tmp(2); + tmp += Coefficient_field(4); + bool comp_true(tmp == Coefficient_field(1)) ; + std::cerr << "Test 2+=4 == 1: " << comp_true << std::endl ; + assert(comp_true) ; + } + { + std::cerr << "----> Ring Z12" << std::endl; + Coefficient_ring tmp(6); + tmp += Coefficient_ring(8); + bool comp_true(tmp == Coefficient_ring(2)) ; + std::cerr << "Test 6+=8 == 2: " << comp_true << std::endl ; + assert(comp_true) ; + } + + std::cerr << "-- Test Zp operator-=" << std::endl; + { + Coefficient_field tmp(2); + tmp -= Coefficient_field(4); + std::cerr << "----> Field Z5" << std::endl; + bool comp_true(tmp == Coefficient_field(-2)) ; + std::cerr << "Test 2-=4 == -2: " << comp_true << std::endl ; + assert(comp_true) ; + } + { + std::cerr << "----> Ring Z12" << std::endl; + Coefficient_ring tmp(6); + tmp -= Coefficient_ring(8); + bool comp_true(tmp == Coefficient_ring(-2)) ; + std::cerr << "Test 6-=8 == -2: " << comp_true << std::endl ; + assert(comp_true) ; + } + + std::cerr << "-- Test Zp operator*=" << std::endl; + { + std::cerr << "----> Field Z5" << std::endl; + Coefficient_field tmp(2); + tmp *= Coefficient_field(4); + bool comp_true(tmp == Coefficient_field(3)) ; + std::cerr << "Test 2*=4 == 3: " << comp_true << std::endl ; + assert(comp_true) ; + } + { + std::cerr << "----> Ring Z12" << std::endl; + Coefficient_ring tmp(6); + tmp *= Coefficient_ring(7); + bool comp_true(tmp == Coefficient_ring(6)) ; + std::cerr << "Test 6*=7 == 6: " << comp_true << std::endl ; + assert(comp_true) ; + } + + std::cerr << "-- Test Zp operator/=" << std::endl; + { + std::cerr << "----> Field Z5" << std::endl; + Coefficient_field tmp(4); + tmp /= Coefficient_field(3); + bool comp_true(tmp == Coefficient_field(1)) ; + std::cerr << "Test 4/=3 == 1: " << comp_true << std::endl ; + assert(comp_true) ; + } + { + std::cerr << "----> Ring Z12" << std::endl; + Coefficient_ring tmp(8); + tmp /= Coefficient_ring(3); + bool comp_true(tmp == Coefficient_ring(2)) ; + std::cerr << "Test 8/=3 == 2: " << comp_true << std::endl ; + assert(comp_true) ; + } + + std::cerr << "-- Test Zp is_invertible" << std::endl; + { + std::cerr << "----> Field Z5" << std::endl; + bool comp_true(Coefficient_field(2).is_invertible()) ; + std::cerr << "Test is_invertible(2): " << comp_true << std::endl ; + assert(comp_true) ; + bool comp_false(Coefficient_field(0).is_invertible()) ; + std::cerr << "Test is_invertible(0): " << comp_false << std::endl ; + assert(!comp_false) ; + } + { + std::cerr << "----> Ring Z12" << std::endl; + bool comp_true(Coefficient_ring(5).is_invertible()) ; + std::cerr << "Test is_invertible(5): " << comp_true << std::endl ; + assert(comp_true) ; + bool comp_false(Coefficient_ring(2).is_invertible()) ; + std::cerr << "Test is_invertible(2): " << comp_false << std::endl ; + assert(!comp_false) ; + } + + std::cout << "n1==0 : " << ATCoefs_field::Is_zero()(f1) << std::endl; + + return 0; +} + + diff --git a/SMDS_3/doc/SMDS_3/Concepts/MeshComplexWithFeatures_3InTriangulation_3.h b/SMDS_3/doc/SMDS_3/Concepts/MeshComplexWithFeatures_3InTriangulation_3.h index f436119cf12..e4306ef7aa6 100644 --- a/SMDS_3/doc/SMDS_3/Concepts/MeshComplexWithFeatures_3InTriangulation_3.h +++ b/SMDS_3/doc/SMDS_3/Concepts/MeshComplexWithFeatures_3InTriangulation_3.h @@ -64,14 +64,14 @@ public: /// @{ /*! -A type for indexes of curve. The type must match the type +A type for indices of curve. The type must match the type `MeshDomainWithFeatures_3::Curve_index` when the concept is used for mesh generation. */ typedef unspecified_type Curve_index; /*! -A type for indexes of corners. +A type for indices of corners. The type must match the type `MeshDomainWithFeatures_3::Corner_index` when the concept is used for mesh generation. diff --git a/Triangulation_2/doc/Triangulation_2/CGAL/Triangulation_2.h b/Triangulation_2/doc/Triangulation_2/CGAL/Triangulation_2.h index 8ae0ea8c2fe..e0668aa2038 100644 --- a/Triangulation_2/doc/Triangulation_2/CGAL/Triangulation_2.h +++ b/Triangulation_2/doc/Triangulation_2/CGAL/Triangulation_2.h @@ -1294,7 +1294,7 @@ which, given the index of a vertex in a face, compute the index of the next vertex of the same face in clockwise or counterclockwise order. -This works also for neighbor indexes. +This works also for neighbor indices. Thus, for example the neighbor `neighbor(cw(i))` of a face `f` is the