summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authorGard Spreemann <gspreemann@gmail.com>2017-02-07 17:33:01 +0100
committerGard Spreemann <gspreemann@gmail.com>2017-02-07 17:33:01 +0100
commit55c7181126aa7defce38c9b82872d14223d4c1dd (patch)
tree7c683f014709459f066fd87a21da7f74cfc31a53 /include
Initial import of upstream's 1.3.1.upstream/1.3.1
Diffstat (limited to 'include')
-rw-r--r--include/gudhi/Alpha_complex.h419
-rw-r--r--include/gudhi/Alpha_complex.h~417
-rw-r--r--include/gudhi/Bitmap_cubical_complex.h595
-rw-r--r--include/gudhi/Bitmap_cubical_complex/counter.h144
-rw-r--r--include/gudhi/Bitmap_cubical_complex_base.h817
-rw-r--r--include/gudhi/Bitmap_cubical_complex_periodic_boundary_conditions_base.h308
-rw-r--r--include/gudhi/Clock.h79
-rw-r--r--include/gudhi/Contraction/CGAL_queue/Modifiable_priority_queue.h101
-rw-r--r--include/gudhi/Contraction/Edge_profile.h130
-rw-r--r--include/gudhi/Contraction/policies/Contraction_visitor.h91
-rw-r--r--include/gudhi/Contraction/policies/Cost_policy.h53
-rw-r--r--include/gudhi/Contraction/policies/Dummy_valid_contraction.h49
-rw-r--r--include/gudhi/Contraction/policies/Edge_length_cost.h56
-rw-r--r--include/gudhi/Contraction/policies/First_vertex_placement.h52
-rw-r--r--include/gudhi/Contraction/policies/Link_condition_valid_contraction.h56
-rw-r--r--include/gudhi/Contraction/policies/Middle_placement.h51
-rw-r--r--include/gudhi/Contraction/policies/Placement_policy.h51
-rw-r--r--include/gudhi/Contraction/policies/Valid_contraction_policy.h51
-rw-r--r--include/gudhi/Debug_utils.h55
-rw-r--r--include/gudhi/Edge_contraction.h235
-rw-r--r--include/gudhi/Hasse_complex.h255
-rw-r--r--include/gudhi/Landmark_choice_by_furthest_point.h105
-rw-r--r--include/gudhi/Landmark_choice_by_random_point.h96
-rw-r--r--include/gudhi/Off_reader.h184
-rw-r--r--include/gudhi/Persistent_cohomology.h745
-rw-r--r--include/gudhi/Persistent_cohomology/Field_Zp.h116
-rw-r--r--include/gudhi/Persistent_cohomology/Multi_field.h185
-rw-r--r--include/gudhi/Persistent_cohomology/Persistent_cohomology_column.h140
-rw-r--r--include/gudhi/Point.h170
-rw-r--r--include/gudhi/Points_3D_off_io.h202
-rw-r--r--include/gudhi/Points_off_io.h184
-rw-r--r--include/gudhi/Simple_object_pool.h81
-rw-r--r--include/gudhi/Simplex_tree.h1303
-rw-r--r--include/gudhi/Simplex_tree/Simplex_tree_iterators.h338
-rw-r--r--include/gudhi/Simplex_tree/Simplex_tree_node_explicit_storage.h72
-rw-r--r--include/gudhi/Simplex_tree/Simplex_tree_siblings.h131
-rw-r--r--include/gudhi/Simplex_tree/indexing_tag.h39
-rw-r--r--include/gudhi/Skeleton_blocker.h252
-rw-r--r--include/gudhi/Skeleton_blocker/Skeleton_blocker_complex_visitor.h137
-rw-r--r--include/gudhi/Skeleton_blocker/Skeleton_blocker_link_superior.h79
-rw-r--r--include/gudhi/Skeleton_blocker/Skeleton_blocker_off_io.h202
-rw-r--r--include/gudhi/Skeleton_blocker/Skeleton_blocker_simple_geometric_traits.h96
-rw-r--r--include/gudhi/Skeleton_blocker/Skeleton_blocker_simple_traits.h184
-rw-r--r--include/gudhi/Skeleton_blocker/Skeleton_blocker_simplex.h376
-rw-r--r--include/gudhi/Skeleton_blocker/Skeleton_blocker_sub_complex.h292
-rw-r--r--include/gudhi/Skeleton_blocker/internal/Top_faces.h72
-rw-r--r--include/gudhi/Skeleton_blocker/internal/Trie.h270
-rw-r--r--include/gudhi/Skeleton_blocker/iterators/Skeleton_blockers_blockers_iterators.h133
-rw-r--r--include/gudhi/Skeleton_blocker/iterators/Skeleton_blockers_edges_iterators.h146
-rw-r--r--include/gudhi/Skeleton_blocker/iterators/Skeleton_blockers_iterators.h32
-rw-r--r--include/gudhi/Skeleton_blocker/iterators/Skeleton_blockers_simplices_iterators.h400
-rw-r--r--include/gudhi/Skeleton_blocker/iterators/Skeleton_blockers_triangles_iterators.h222
-rw-r--r--include/gudhi/Skeleton_blocker/iterators/Skeleton_blockers_vertices_iterators.h174
-rw-r--r--include/gudhi/Skeleton_blocker_complex.h1606
-rw-r--r--include/gudhi/Skeleton_blocker_contractor.h582
-rw-r--r--include/gudhi/Skeleton_blocker_geometric_complex.h226
-rw-r--r--include/gudhi/Skeleton_blocker_link_complex.h299
-rw-r--r--include/gudhi/Skeleton_blocker_simplifiable_complex.h468
-rw-r--r--include/gudhi/Test.h105
-rw-r--r--include/gudhi/Witness_complex.h265
-rw-r--r--include/gudhi/allocator.h55
-rw-r--r--include/gudhi/distance_functions.h43
-rw-r--r--include/gudhi/graph_simplicial_complex.h99
-rw-r--r--include/gudhi/reader_utils.h198
64 files changed, 15169 insertions, 0 deletions
diff --git a/include/gudhi/Alpha_complex.h b/include/gudhi/Alpha_complex.h
new file mode 100644
index 00000000..2c95ceb4
--- /dev/null
+++ b/include/gudhi/Alpha_complex.h
@@ -0,0 +1,419 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Vincent Rouvreau
+ *
+ * Copyright (C) 2015 INRIA Saclay (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ALPHA_COMPLEX_H_
+#define ALPHA_COMPLEX_H_
+
+// to construct a simplex_tree from Delaunay_triangulation
+#include <gudhi/graph_simplicial_complex.h>
+#include <gudhi/Simplex_tree.h>
+#include <gudhi/Debug_utils.h>
+// to construct Alpha_complex from a OFF file of points
+#include <gudhi/Points_off_io.h>
+
+#include <stdlib.h>
+#include <math.h> // isnan, fmax
+
+#include <CGAL/Delaunay_triangulation.h>
+#include <CGAL/Epick_d.h>
+#include <CGAL/Spatial_sort_traits_adapter_d.h>
+
+#include <iostream>
+#include <vector>
+#include <string>
+#include <limits> // NaN
+#include <map>
+#include <utility> // std::pair
+#include <stdexcept>
+#include <numeric> // for std::iota
+
+namespace Gudhi {
+
+namespace alpha_complex {
+
+/**
+ * \class Alpha_complex Alpha_complex.h gudhi/Alpha_complex.h
+ * \brief Alpha complex data structure.
+ *
+ * \ingroup alpha_complex
+ *
+ * \details
+ * The data structure can be constructed from a CGAL Delaunay triangulation (for more informations on CGAL Delaunay
+ * triangulation, please refer to the corresponding chapter in page http://doc.cgal.org/latest/Triangulation/) or from
+ * an OFF file (cf. Points_off_reader).
+ *
+ * Please refer to \ref alpha_complex for examples.
+ *
+ * The complex is a template class requiring an Epick_d <a target="_blank"
+ * href="http://doc.cgal.org/latest/Kernel_d/index.html#Chapter_dD_Geometry_Kernel">dD Geometry Kernel</a>
+ * \cite cgal:s-gkd-15b from CGAL as template, default value is <a target="_blank"
+ * href="http://doc.cgal.org/latest/Kernel_d/classCGAL_1_1Epick__d.html">CGAL::Epick_d</a>
+ * < <a target="_blank" href="http://doc.cgal.org/latest/Kernel_23/classCGAL_1_1Dynamic__dimension__tag.html">
+ * CGAL::Dynamic_dimension_tag </a> >
+ *
+ * \remark When Alpha_complex is constructed with an infinite value of alpha, the complex is a Delaunay complex.
+ *
+ */
+template<class Kernel = CGAL::Epick_d<CGAL::Dynamic_dimension_tag>>
+class Alpha_complex : public Simplex_tree<> {
+ public:
+ // Add an int in TDS to save point index in the structure
+ typedef CGAL::Triangulation_data_structure<typename Kernel::Dimension,
+ CGAL::Triangulation_vertex<Kernel, std::ptrdiff_t>,
+ CGAL::Triangulation_full_cell<Kernel> > TDS;
+ /** \brief A Delaunay triangulation of a set of points in \f$ \mathbb{R}^D\f$.*/
+ typedef CGAL::Delaunay_triangulation<Kernel, TDS> Delaunay_triangulation;
+
+ /** \brief A point in Euclidean space.*/
+ typedef typename Kernel::Point_d Point_d;
+ /** \brief Geometric traits class that provides the geometric types and predicates needed by Delaunay
+ * triangulations.*/
+ typedef Kernel Geom_traits;
+
+ private:
+ // From Simplex_tree
+ // Type required to insert into a simplex_tree (with or without subfaces).
+ typedef std::vector<Vertex_handle> Vector_vertex;
+
+ // Simplex_result is the type returned from simplex_tree insert function.
+ typedef typename std::pair<Simplex_handle, bool> Simplex_result;
+
+ typedef typename Kernel::Compute_squared_radius_d Squared_Radius;
+ typedef typename Kernel::Side_of_bounded_sphere_d Is_Gabriel;
+ typedef typename Kernel::Point_dimension_d Point_Dimension;
+
+ // Type required to compute squared radius, or side of bounded sphere on a vector of points.
+ typedef typename std::vector<Point_d> Vector_of_CGAL_points;
+
+ // Vertex_iterator type from CGAL.
+ typedef typename Delaunay_triangulation::Vertex_iterator CGAL_vertex_iterator;
+
+ // size_type type from CGAL.
+ typedef typename Delaunay_triangulation::size_type size_type;
+
+ // Map type to switch from simplex tree vertex handle to CGAL vertex iterator.
+ typedef typename std::map< Vertex_handle, CGAL_vertex_iterator > Vector_vertex_iterator;
+
+ private:
+ /** \brief Vertex iterator vector to switch from simplex tree vertex handle to CGAL vertex iterator.
+ * Vertex handles are inserted sequentially, starting at 0.*/
+ Vector_vertex_iterator vertex_handle_to_iterator_;
+ /** \brief Pointer on the CGAL Delaunay triangulation.*/
+ Delaunay_triangulation* triangulation_;
+ /** \brief Kernel for triangulation_ functions access.*/
+ Kernel kernel_;
+
+ public:
+ /** \brief Alpha_complex constructor from an OFF file name.
+ * Uses the Delaunay_triangulation_off_reader to construct the Delaunay triangulation required to initialize
+ * the Alpha_complex.
+ *
+ * Duplicate points are inserted once in the Alpha_complex. This is the reason why the vertices may be not contiguous.
+ *
+ * @param[in] off_file_name OFF file [path and] name.
+ * @param[in] max_alpha_square maximum for alpha square value. Default value is +\f$\infty\f$.
+ */
+ Alpha_complex(const std::string& off_file_name,
+ Filtration_value max_alpha_square = std::numeric_limits<Filtration_value>::infinity())
+ : triangulation_(nullptr) {
+ Gudhi::Points_off_reader<Point_d> off_reader(off_file_name);
+ if (!off_reader.is_valid()) {
+ std::cerr << "Alpha_complex - Unable to read file " << off_file_name << "\n";
+ exit(-1); // ----- >>
+ }
+
+ init_from_range(off_reader.get_point_cloud(), max_alpha_square);
+ }
+
+ /** \brief Alpha_complex constructor from a list of points.
+ *
+ * Duplicate points are inserted once in the Alpha_complex. This is the reason why the vertices may be not contiguous.
+ *
+ * @param[in] points Range of points to triangulate. Points must be in Kernel::Point_d
+ * @param[in] max_alpha_square maximum for alpha square value. Default value is +\f$\infty\f$.
+ *
+ * The type InputPointRange must be a range for which std::begin and
+ * std::end return input iterators on a Kernel::Point_d.
+ *
+ * @post Compare num_simplices with InputPointRange points number (not the same in case of duplicate points).
+ */
+ template<typename InputPointRange >
+ Alpha_complex(const InputPointRange& points,
+ Filtration_value max_alpha_square = std::numeric_limits<Filtration_value>::infinity())
+ : triangulation_(nullptr) {
+ init_from_range(points, max_alpha_square);
+ }
+
+ /** \brief Alpha_complex destructor.
+ *
+ * @warning Deletes the Delaunay triangulation.
+ */
+ ~Alpha_complex() {
+ delete triangulation_;
+ }
+
+ // Forbid copy/move constructor/assignment operator
+ Alpha_complex(const Alpha_complex& other) = delete;
+ Alpha_complex& operator= (const Alpha_complex& other) = delete;
+ Alpha_complex (Alpha_complex&& other) = delete;
+ Alpha_complex& operator= (Alpha_complex&& other) = delete;
+
+ /** \brief get_point returns the point corresponding to the vertex given as parameter.
+ *
+ * @param[in] vertex Vertex handle of the point to retrieve.
+ * @return The point found.
+ * @exception std::out_of_range In case vertex is not found (cf. std::vector::at).
+ */
+ Point_d get_point(Vertex_handle vertex) const {
+ return vertex_handle_to_iterator_.at(vertex)->point();
+ }
+
+ private:
+ template<typename InputPointRange >
+ void init_from_range(const InputPointRange& points, Filtration_value max_alpha_square) {
+ auto first = std::begin(points);
+ auto last = std::end(points);
+ if (first != last) {
+ // point_dimension function initialization
+ Point_Dimension point_dimension = kernel_.point_dimension_d_object();
+
+ // Delaunay triangulation is point dimension.
+ triangulation_ = new Delaunay_triangulation(point_dimension(*first));
+
+ std::vector<Point_d> points(first, last);
+
+ // Creates a vector {0, 1, ..., N-1}
+ std::vector<std::ptrdiff_t> indices(boost::counting_iterator<std::ptrdiff_t>(0),
+ boost::counting_iterator<std::ptrdiff_t>(points.size()));
+
+ // Sort indices considering CGAL spatial sort
+ typedef CGAL::Spatial_sort_traits_adapter_d<Kernel, Point_d*> Search_traits_d;
+ spatial_sort(indices.begin(), indices.end(), Search_traits_d(&(points[0])));
+
+ typename Delaunay_triangulation::Full_cell_handle hint;
+ for (auto index : indices) {
+ typename Delaunay_triangulation::Vertex_handle pos = triangulation_->insert(points[index], hint);
+ // Save index value as data to retrieve it after insertion
+ pos->data() = index;
+ hint = pos->full_cell();
+ }
+ init(max_alpha_square);
+ }
+ }
+
+ /** \brief Initialize the Alpha_complex from the Delaunay triangulation.
+ *
+ * @param[in] max_alpha_square maximum for alpha square value.
+ *
+ * @warning Delaunay triangulation must be already constructed with at least one vertex and dimension must be more
+ * than 0.
+ *
+ * Initialization can be launched once.
+ */
+ void init(Filtration_value max_alpha_square) {
+ if (triangulation_ == nullptr) {
+ std::cerr << "Alpha_complex init - Cannot init from a NULL triangulation\n";
+ return; // ----- >>
+ }
+ if (triangulation_->number_of_vertices() < 1) {
+ std::cerr << "Alpha_complex init - Cannot init from a triangulation without vertices\n";
+ return; // ----- >>
+ }
+ if (triangulation_->maximal_dimension() < 1) {
+ std::cerr << "Alpha_complex init - Cannot init from a zero-dimension triangulation\n";
+ return; // ----- >>
+ }
+ if (num_vertices() > 0) {
+ std::cerr << "Alpha_complex init - Cannot init twice\n";
+ return; // ----- >>
+ }
+
+ set_dimension(triangulation_->maximal_dimension());
+
+ // --------------------------------------------------------------------------------------------
+ // double map to retrieve simplex tree vertex handles from CGAL vertex iterator and vice versa
+ // Loop on triangulation vertices list
+ for (CGAL_vertex_iterator vit = triangulation_->vertices_begin(); vit != triangulation_->vertices_end(); ++vit) {
+ if (!triangulation_->is_infinite(*vit)) {
+#ifdef DEBUG_TRACES
+ std::cout << "Vertex insertion - " << vit->data() << " -> " << vit->point() << std::endl;
+#endif // DEBUG_TRACES
+ vertex_handle_to_iterator_.emplace(vit->data(), vit);
+ }
+ }
+ // --------------------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------------------
+ // Simplex_tree construction from loop on triangulation finite full cells list
+ for (auto cit = triangulation_->finite_full_cells_begin(); cit != triangulation_->finite_full_cells_end(); ++cit) {
+ Vector_vertex vertexVector;
+#ifdef DEBUG_TRACES
+ std::cout << "Simplex_tree insertion ";
+#endif // DEBUG_TRACES
+ for (auto vit = cit->vertices_begin(); vit != cit->vertices_end(); ++vit) {
+ if (*vit != nullptr) {
+#ifdef DEBUG_TRACES
+ std::cout << " " << (*vit)->data();
+#endif // DEBUG_TRACES
+ // Vector of vertex construction for simplex_tree structure
+ vertexVector.push_back((*vit)->data());
+ }
+ }
+#ifdef DEBUG_TRACES
+ std::cout << std::endl;
+#endif // DEBUG_TRACES
+ // Insert each simplex and its subfaces in the simplex tree - filtration is NaN
+ insert_simplex_and_subfaces(vertexVector, std::numeric_limits<double>::quiet_NaN());
+ }
+ // --------------------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------------------
+ // Will be re-used many times
+ Vector_of_CGAL_points pointVector;
+ // ### For i : d -> 0
+ for (int decr_dim = dimension(); decr_dim >= 0; decr_dim--) {
+ // ### Foreach Sigma of dim i
+ for (auto f_simplex : skeleton_simplex_range(decr_dim)) {
+ int f_simplex_dim = dimension(f_simplex);
+ if (decr_dim == f_simplex_dim) {
+ pointVector.clear();
+#ifdef DEBUG_TRACES
+ std::cout << "Sigma of dim " << decr_dim << " is";
+#endif // DEBUG_TRACES
+ for (auto vertex : simplex_vertex_range(f_simplex)) {
+ pointVector.push_back(get_point(vertex));
+#ifdef DEBUG_TRACES
+ std::cout << " " << vertex;
+#endif // DEBUG_TRACES
+ }
+#ifdef DEBUG_TRACES
+ std::cout << std::endl;
+#endif // DEBUG_TRACES
+ // ### If filt(Sigma) is NaN : filt(Sigma) = alpha(Sigma)
+ if (std::isnan(filtration(f_simplex))) {
+ Filtration_value alpha_complex_filtration = 0.0;
+ // No need to compute squared_radius on a single point - alpha is 0.0
+ if (f_simplex_dim > 0) {
+ // squared_radius function initialization
+ Squared_Radius squared_radius = kernel_.compute_squared_radius_d_object();
+
+ alpha_complex_filtration = squared_radius(pointVector.begin(), pointVector.end());
+ }
+ assign_filtration(f_simplex, alpha_complex_filtration);
+#ifdef DEBUG_TRACES
+ std::cout << "filt(Sigma) is NaN : filt(Sigma) =" << filtration(f_simplex) << std::endl;
+#endif // DEBUG_TRACES
+ }
+ propagate_alpha_filtration(f_simplex, decr_dim);
+ }
+ }
+ }
+ // --------------------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------------------
+ // As Alpha value is an approximation, we have to make filtration non decreasing while increasing the dimension
+ bool modified_filt = make_filtration_non_decreasing();
+ // Remove all simplices that have a filtration value greater than max_alpha_square
+ // Remark: prune_above_filtration does not require initialize_filtration to be done before.
+ modified_filt |= prune_above_filtration(max_alpha_square);
+ if (modified_filt) {
+ initialize_filtration();
+ }
+ // --------------------------------------------------------------------------------------------
+ }
+
+ template<typename Simplex_handle>
+ void propagate_alpha_filtration(Simplex_handle f_simplex, int decr_dim) {
+ // ### Foreach Tau face of Sigma
+ for (auto f_boundary : boundary_simplex_range(f_simplex)) {
+#ifdef DEBUG_TRACES
+ std::cout << " | --------------------------------------------------\n";
+ std::cout << " | Tau ";
+ for (auto vertex : simplex_vertex_range(f_boundary)) {
+ std::cout << vertex << " ";
+ }
+ std::cout << "is a face of Sigma\n";
+ std::cout << " | isnan(filtration(Tau)=" << std::isnan(filtration(f_boundary)) << std::endl;
+#endif // DEBUG_TRACES
+ // ### If filt(Tau) is not NaN
+ if (!std::isnan(filtration(f_boundary))) {
+ // ### filt(Tau) = fmin(filt(Tau), filt(Sigma))
+ Filtration_value alpha_complex_filtration = fmin(filtration(f_boundary), filtration(f_simplex));
+ assign_filtration(f_boundary, alpha_complex_filtration);
+#ifdef DEBUG_TRACES
+ std::cout << " | filt(Tau) = fmin(filt(Tau), filt(Sigma)) = " << filtration(f_boundary) << std::endl;
+#endif // DEBUG_TRACES
+ // ### Else
+ } else {
+ // No need to compute is_gabriel for dimension <= 2
+ // i.e. : Sigma = (3,1) => Tau = 1
+ if (decr_dim > 1) {
+ // insert the Tau points in a vector for is_gabriel function
+ Vector_of_CGAL_points pointVector;
+#ifdef DEBUG_TRACES
+ Vertex_handle vertexForGabriel = Vertex_handle();
+#endif // DEBUG_TRACES
+ for (auto vertex : simplex_vertex_range(f_boundary)) {
+ pointVector.push_back(get_point(vertex));
+ }
+ // Retrieve the Sigma point that is not part of Tau - parameter for is_gabriel function
+ Point_d point_for_gabriel;
+ for (auto vertex : simplex_vertex_range(f_simplex)) {
+ point_for_gabriel = get_point(vertex);
+ if (std::find(pointVector.begin(), pointVector.end(), point_for_gabriel) == pointVector.end()) {
+#ifdef DEBUG_TRACES
+ // vertex is not found in Tau
+ vertexForGabriel = vertex;
+#endif // DEBUG_TRACES
+ // No need to continue loop
+ break;
+ }
+ }
+ // is_gabriel function initialization
+ Is_Gabriel is_gabriel = kernel_.side_of_bounded_sphere_d_object();
+ bool is_gab = is_gabriel(pointVector.begin(), pointVector.end(), point_for_gabriel)
+ != CGAL::ON_BOUNDED_SIDE;
+#ifdef DEBUG_TRACES
+ std::cout << " | Tau is_gabriel(Sigma)=" << is_gab << " - vertexForGabriel=" << vertexForGabriel << std::endl;
+#endif // DEBUG_TRACES
+ // ### If Tau is not Gabriel of Sigma
+ if (false == is_gab) {
+ // ### filt(Tau) = filt(Sigma)
+ Filtration_value alpha_complex_filtration = filtration(f_simplex);
+ assign_filtration(f_boundary, alpha_complex_filtration);
+#ifdef DEBUG_TRACES
+ std::cout << " | filt(Tau) = filt(Sigma) = " << filtration(f_boundary) << std::endl;
+#endif // DEBUG_TRACES
+ }
+ }
+ }
+ }
+ }
+};
+
+} // namespace alpha_complex
+
+namespace alphacomplex = alpha_complex;
+
+} // namespace Gudhi
+
+#endif // ALPHA_COMPLEX_H_
diff --git a/include/gudhi/Alpha_complex.h~ b/include/gudhi/Alpha_complex.h~
new file mode 100644
index 00000000..a1900cb9
--- /dev/null
+++ b/include/gudhi/Alpha_complex.h~
@@ -0,0 +1,417 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Vincent Rouvreau
+ *
+ * Copyright (C) 2015 INRIA Saclay (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ALPHA_COMPLEX_H_
+#define ALPHA_COMPLEX_H_
+
+// to construct a simplex_tree from Delaunay_triangulation
+#include <gudhi/graph_simplicial_complex.h>
+#include <gudhi/Simplex_tree.h>
+#include <gudhi/Debug_utils.h>
+// to construct Alpha_complex from a OFF file of points
+#include <gudhi/Points_off_io.h>
+
+#include <stdlib.h>
+#include <math.h> // isnan, fmax
+
+#include <CGAL/Delaunay_triangulation.h>
+#include <CGAL/Epick_d.h>
+#include <CGAL/Spatial_sort_traits_adapter_d.h>
+
+#include <iostream>
+#include <vector>
+#include <string>
+#include <limits> // NaN
+#include <map>
+#include <utility> // std::pair
+#include <stdexcept>
+#include <numeric> // for std::iota
+
+namespace Gudhi {
+
+namespace alphacomplex {
+
+/**
+ * \class Alpha_complex Alpha_complex.h gudhi/Alpha_complex.h
+ * \brief Alpha complex data structure.
+ *
+ * \ingroup alpha_complex
+ *
+ * \details
+ * The data structure can be constructed from a CGAL Delaunay triangulation (for more informations on CGAL Delaunay
+ * triangulation, please refer to the corresponding chapter in page http://doc.cgal.org/latest/Triangulation/) or from
+ * an OFF file (cf. Points_off_reader).
+ *
+ * Please refer to \ref alpha_complex for examples.
+ *
+ * The complex is a template class requiring an Epick_d <a target="_blank"
+ * href="http://doc.cgal.org/latest/Kernel_d/index.html#Chapter_dD_Geometry_Kernel">dD Geometry Kernel</a>
+ * \cite cgal:s-gkd-15b from CGAL as template, default value is <a target="_blank"
+ * href="http://doc.cgal.org/latest/Kernel_d/classCGAL_1_1Epick__d.html">CGAL::Epick_d</a>
+ * < <a target="_blank" href="http://doc.cgal.org/latest/Kernel_23/classCGAL_1_1Dynamic__dimension__tag.html">
+ * CGAL::Dynamic_dimension_tag </a> >
+ *
+ * \remark When Alpha_complex is constructed with an infinite value of alpha, the complex is a Delaunay complex.
+ *
+ */
+template<class Kernel = CGAL::Epick_d<CGAL::Dynamic_dimension_tag>>
+class Alpha_complex : public Simplex_tree<> {
+ public:
+ // Add an int in TDS to save point index in the structure
+ typedef CGAL::Triangulation_data_structure<typename Kernel::Dimension,
+ CGAL::Triangulation_vertex<Kernel, std::ptrdiff_t>,
+ CGAL::Triangulation_full_cell<Kernel> > TDS;
+ /** \brief A Delaunay triangulation of a set of points in \f$ \mathbb{R}^D\f$.*/
+ typedef CGAL::Delaunay_triangulation<Kernel, TDS> Delaunay_triangulation;
+
+ /** \brief A point in Euclidean space.*/
+ typedef typename Kernel::Point_d Point_d;
+ /** \brief Geometric traits class that provides the geometric types and predicates needed by Delaunay
+ * triangulations.*/
+ typedef Kernel Geom_traits;
+
+ private:
+ // From Simplex_tree
+ // Type required to insert into a simplex_tree (with or without subfaces).
+ typedef std::vector<Vertex_handle> Vector_vertex;
+
+ // Simplex_result is the type returned from simplex_tree insert function.
+ typedef typename std::pair<Simplex_handle, bool> Simplex_result;
+
+ typedef typename Kernel::Compute_squared_radius_d Squared_Radius;
+ typedef typename Kernel::Side_of_bounded_sphere_d Is_Gabriel;
+ typedef typename Kernel::Point_dimension_d Point_Dimension;
+
+ // Type required to compute squared radius, or side of bounded sphere on a vector of points.
+ typedef typename std::vector<Point_d> Vector_of_CGAL_points;
+
+ // Vertex_iterator type from CGAL.
+ typedef typename Delaunay_triangulation::Vertex_iterator CGAL_vertex_iterator;
+
+ // size_type type from CGAL.
+ typedef typename Delaunay_triangulation::size_type size_type;
+
+ // Map type to switch from simplex tree vertex handle to CGAL vertex iterator.
+ typedef typename std::map< Vertex_handle, CGAL_vertex_iterator > Vector_vertex_iterator;
+
+ private:
+ /** \brief Vertex iterator vector to switch from simplex tree vertex handle to CGAL vertex iterator.
+ * Vertex handles are inserted sequentially, starting at 0.*/
+ Vector_vertex_iterator vertex_handle_to_iterator_;
+ /** \brief Pointer on the CGAL Delaunay triangulation.*/
+ Delaunay_triangulation* triangulation_;
+ /** \brief Kernel for triangulation_ functions access.*/
+ Kernel kernel_;
+
+ public:
+ /** \brief Alpha_complex constructor from an OFF file name.
+ * Uses the Delaunay_triangulation_off_reader to construct the Delaunay triangulation required to initialize
+ * the Alpha_complex.
+ *
+ * Duplicate points are inserted once in the Alpha_complex. This is the reason why the vertices may be not contiguous.
+ *
+ * @param[in] off_file_name OFF file [path and] name.
+ * @param[in] max_alpha_square maximum for alpha square value. Default value is +\f$\infty\f$.
+ */
+ Alpha_complex(const std::string& off_file_name,
+ Filtration_value max_alpha_square = std::numeric_limits<Filtration_value>::infinity())
+ : triangulation_(nullptr) {
+ Gudhi::Points_off_reader<Point_d> off_reader(off_file_name);
+ if (!off_reader.is_valid()) {
+ std::cerr << "Alpha_complex - Unable to read file " << off_file_name << "\n";
+ exit(-1); // ----- >>
+ }
+
+ init_from_range(off_reader.get_point_cloud(), max_alpha_square);
+ }
+
+ /** \brief Alpha_complex constructor from a list of points.
+ *
+ * Duplicate points are inserted once in the Alpha_complex. This is the reason why the vertices may be not contiguous.
+ *
+ * @param[in] points Range of points to triangulate. Points must be in Kernel::Point_d
+ * @param[in] max_alpha_square maximum for alpha square value. Default value is +\f$\infty\f$.
+ *
+ * The type InputPointRange must be a range for which std::begin and
+ * std::end return input iterators on a Kernel::Point_d.
+ *
+ * @post Compare num_simplices with InputPointRange points number (not the same in case of duplicate points).
+ */
+ template<typename InputPointRange >
+ Alpha_complex(const InputPointRange& points,
+ Filtration_value max_alpha_square = std::numeric_limits<Filtration_value>::infinity())
+ : triangulation_(nullptr) {
+ init_from_range(points, max_alpha_square);
+ }
+
+ /** \brief Alpha_complex destructor.
+ *
+ * @warning Deletes the Delaunay triangulation.
+ */
+ ~Alpha_complex() {
+ delete triangulation_;
+ }
+
+ // Forbid copy/move constructor/assignment operator
+ Alpha_complex(const Alpha_complex& other) = delete;
+ Alpha_complex& operator= (const Alpha_complex& other) = delete;
+ Alpha_complex (Alpha_complex&& other) = delete;
+ Alpha_complex& operator= (Alpha_complex&& other) = delete;
+
+ /** \brief get_point returns the point corresponding to the vertex given as parameter.
+ *
+ * @param[in] vertex Vertex handle of the point to retrieve.
+ * @return The point found.
+ * @exception std::out_of_range In case vertex is not found (cf. std::vector::at).
+ */
+ Point_d get_point(Vertex_handle vertex) const {
+ return vertex_handle_to_iterator_.at(vertex)->point();
+ }
+
+ private:
+ template<typename InputPointRange >
+ void init_from_range(const InputPointRange& points, Filtration_value max_alpha_square) {
+ auto first = std::begin(points);
+ auto last = std::end(points);
+ if (first != last) {
+ // point_dimension function initialization
+ Point_Dimension point_dimension = kernel_.point_dimension_d_object();
+
+ // Delaunay triangulation is point dimension.
+ triangulation_ = new Delaunay_triangulation(point_dimension(*first));
+
+ std::vector<Point_d> points(first, last);
+
+ // Creates a vector {0, 1, ..., N-1}
+ std::vector<std::ptrdiff_t> indices(boost::counting_iterator<std::ptrdiff_t>(0),
+ boost::counting_iterator<std::ptrdiff_t>(points.size()));
+
+ // Sort indices considering CGAL spatial sort
+ typedef CGAL::Spatial_sort_traits_adapter_d<Kernel, Point_d*> Search_traits_d;
+ spatial_sort(indices.begin(), indices.end(), Search_traits_d(&(points[0])));
+
+ typename Delaunay_triangulation::Full_cell_handle hint;
+ for (auto index : indices) {
+ typename Delaunay_triangulation::Vertex_handle pos = triangulation_->insert(points[index], hint);
+ // Save index value as data to retrieve it after insertion
+ pos->data() = index;
+ hint = pos->full_cell();
+ }
+ init(max_alpha_square);
+ }
+ }
+
+ /** \brief Initialize the Alpha_complex from the Delaunay triangulation.
+ *
+ * @param[in] max_alpha_square maximum for alpha square value.
+ *
+ * @warning Delaunay triangulation must be already constructed with at least one vertex and dimension must be more
+ * than 0.
+ *
+ * Initialization can be launched once.
+ */
+ void init(Filtration_value max_alpha_square) {
+ if (triangulation_ == nullptr) {
+ std::cerr << "Alpha_complex init - Cannot init from a NULL triangulation\n";
+ return; // ----- >>
+ }
+ if (triangulation_->number_of_vertices() < 1) {
+ std::cerr << "Alpha_complex init - Cannot init from a triangulation without vertices\n";
+ return; // ----- >>
+ }
+ if (triangulation_->maximal_dimension() < 1) {
+ std::cerr << "Alpha_complex init - Cannot init from a zero-dimension triangulation\n";
+ return; // ----- >>
+ }
+ if (num_vertices() > 0) {
+ std::cerr << "Alpha_complex init - Cannot init twice\n";
+ return; // ----- >>
+ }
+
+ set_dimension(triangulation_->maximal_dimension());
+
+ // --------------------------------------------------------------------------------------------
+ // double map to retrieve simplex tree vertex handles from CGAL vertex iterator and vice versa
+ // Loop on triangulation vertices list
+ for (CGAL_vertex_iterator vit = triangulation_->vertices_begin(); vit != triangulation_->vertices_end(); ++vit) {
+ if (!triangulation_->is_infinite(*vit)) {
+#ifdef DEBUG_TRACES
+ std::cout << "Vertex insertion - " << vit->data() << " -> " << vit->point() << std::endl;
+#endif // DEBUG_TRACES
+ vertex_handle_to_iterator_.emplace(vit->data(), vit);
+ }
+ }
+ // --------------------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------------------
+ // Simplex_tree construction from loop on triangulation finite full cells list
+ for (auto cit = triangulation_->finite_full_cells_begin(); cit != triangulation_->finite_full_cells_end(); ++cit) {
+ Vector_vertex vertexVector;
+#ifdef DEBUG_TRACES
+ std::cout << "Simplex_tree insertion ";
+#endif // DEBUG_TRACES
+ for (auto vit = cit->vertices_begin(); vit != cit->vertices_end(); ++vit) {
+ if (*vit != nullptr) {
+#ifdef DEBUG_TRACES
+ std::cout << " " << (*vit)->data();
+#endif // DEBUG_TRACES
+ // Vector of vertex construction for simplex_tree structure
+ vertexVector.push_back((*vit)->data());
+ }
+ }
+#ifdef DEBUG_TRACES
+ std::cout << std::endl;
+#endif // DEBUG_TRACES
+ // Insert each simplex and its subfaces in the simplex tree - filtration is NaN
+ insert_simplex_and_subfaces(vertexVector, std::numeric_limits<double>::quiet_NaN());
+ }
+ // --------------------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------------------
+ // Will be re-used many times
+ Vector_of_CGAL_points pointVector;
+ // ### For i : d -> 0
+ for (int decr_dim = dimension(); decr_dim >= 0; decr_dim--) {
+ // ### Foreach Sigma of dim i
+ for (auto f_simplex : skeleton_simplex_range(decr_dim)) {
+ int f_simplex_dim = dimension(f_simplex);
+ if (decr_dim == f_simplex_dim) {
+ pointVector.clear();
+#ifdef DEBUG_TRACES
+ std::cout << "Sigma of dim " << decr_dim << " is";
+#endif // DEBUG_TRACES
+ for (auto vertex : simplex_vertex_range(f_simplex)) {
+ pointVector.push_back(get_point(vertex));
+#ifdef DEBUG_TRACES
+ std::cout << " " << vertex;
+#endif // DEBUG_TRACES
+ }
+#ifdef DEBUG_TRACES
+ std::cout << std::endl;
+#endif // DEBUG_TRACES
+ // ### If filt(Sigma) is NaN : filt(Sigma) = alpha(Sigma)
+ if (isnan(filtration(f_simplex))) {
+ Filtration_value alpha_complex_filtration = 0.0;
+ // No need to compute squared_radius on a single point - alpha is 0.0
+ if (f_simplex_dim > 0) {
+ // squared_radius function initialization
+ Squared_Radius squared_radius = kernel_.compute_squared_radius_d_object();
+
+ alpha_complex_filtration = squared_radius(pointVector.begin(), pointVector.end());
+ }
+ assign_filtration(f_simplex, alpha_complex_filtration);
+#ifdef DEBUG_TRACES
+ std::cout << "filt(Sigma) is NaN : filt(Sigma) =" << filtration(f_simplex) << std::endl;
+#endif // DEBUG_TRACES
+ }
+ propagate_alpha_filtration(f_simplex, decr_dim);
+ }
+ }
+ }
+ // --------------------------------------------------------------------------------------------
+
+ // --------------------------------------------------------------------------------------------
+ // As Alpha value is an approximation, we have to make filtration non decreasing while increasing the dimension
+ bool modified_filt = make_filtration_non_decreasing();
+ // Remove all simplices that have a filtration value greater than max_alpha_square
+ // Remark: prune_above_filtration does not require initialize_filtration to be done before.
+ modified_filt |= prune_above_filtration(max_alpha_square);
+ if (modified_filt) {
+ initialize_filtration();
+ }
+ // --------------------------------------------------------------------------------------------
+ }
+
+ template<typename Simplex_handle>
+ void propagate_alpha_filtration(Simplex_handle f_simplex, int decr_dim) {
+ // ### Foreach Tau face of Sigma
+ for (auto f_boundary : boundary_simplex_range(f_simplex)) {
+#ifdef DEBUG_TRACES
+ std::cout << " | --------------------------------------------------\n";
+ std::cout << " | Tau ";
+ for (auto vertex : simplex_vertex_range(f_boundary)) {
+ std::cout << vertex << " ";
+ }
+ std::cout << "is a face of Sigma\n";
+ std::cout << " | isnan(filtration(Tau)=" << isnan(filtration(f_boundary)) << std::endl;
+#endif // DEBUG_TRACES
+ // ### If filt(Tau) is not NaN
+ if (!isnan(filtration(f_boundary))) {
+ // ### filt(Tau) = fmin(filt(Tau), filt(Sigma))
+ Filtration_value alpha_complex_filtration = fmin(filtration(f_boundary), filtration(f_simplex));
+ assign_filtration(f_boundary, alpha_complex_filtration);
+#ifdef DEBUG_TRACES
+ std::cout << " | filt(Tau) = fmin(filt(Tau), filt(Sigma)) = " << filtration(f_boundary) << std::endl;
+#endif // DEBUG_TRACES
+ // ### Else
+ } else {
+ // No need to compute is_gabriel for dimension <= 2
+ // i.e. : Sigma = (3,1) => Tau = 1
+ if (decr_dim > 1) {
+ // insert the Tau points in a vector for is_gabriel function
+ Vector_of_CGAL_points pointVector;
+#ifdef DEBUG_TRACES
+ Vertex_handle vertexForGabriel = Vertex_handle();
+#endif // DEBUG_TRACES
+ for (auto vertex : simplex_vertex_range(f_boundary)) {
+ pointVector.push_back(get_point(vertex));
+ }
+ // Retrieve the Sigma point that is not part of Tau - parameter for is_gabriel function
+ Point_d point_for_gabriel;
+ for (auto vertex : simplex_vertex_range(f_simplex)) {
+ point_for_gabriel = get_point(vertex);
+ if (std::find(pointVector.begin(), pointVector.end(), point_for_gabriel) == pointVector.end()) {
+#ifdef DEBUG_TRACES
+ // vertex is not found in Tau
+ vertexForGabriel = vertex;
+#endif // DEBUG_TRACES
+ // No need to continue loop
+ break;
+ }
+ }
+ // is_gabriel function initialization
+ Is_Gabriel is_gabriel = kernel_.side_of_bounded_sphere_d_object();
+ bool is_gab = is_gabriel(pointVector.begin(), pointVector.end(), point_for_gabriel)
+ != CGAL::ON_BOUNDED_SIDE;
+#ifdef DEBUG_TRACES
+ std::cout << " | Tau is_gabriel(Sigma)=" << is_gab << " - vertexForGabriel=" << vertexForGabriel << std::endl;
+#endif // DEBUG_TRACES
+ // ### If Tau is not Gabriel of Sigma
+ if (false == is_gab) {
+ // ### filt(Tau) = filt(Sigma)
+ Filtration_value alpha_complex_filtration = filtration(f_simplex);
+ assign_filtration(f_boundary, alpha_complex_filtration);
+#ifdef DEBUG_TRACES
+ std::cout << " | filt(Tau) = filt(Sigma) = " << filtration(f_boundary) << std::endl;
+#endif // DEBUG_TRACES
+ }
+ }
+ }
+ }
+ }
+};
+
+} // namespace alphacomplex
+
+} // namespace Gudhi
+
+#endif // ALPHA_COMPLEX_H_
diff --git a/include/gudhi/Bitmap_cubical_complex.h b/include/gudhi/Bitmap_cubical_complex.h
new file mode 100644
index 00000000..5a87b9b8
--- /dev/null
+++ b/include/gudhi/Bitmap_cubical_complex.h
@@ -0,0 +1,595 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Pawel Dlotko
+ *
+ * Copyright (C) 2015 INRIA Sophia-Saclay (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef BITMAP_CUBICAL_COMPLEX_H_
+#define BITMAP_CUBICAL_COMPLEX_H_
+
+#include <gudhi/Bitmap_cubical_complex_base.h>
+#include <gudhi/Bitmap_cubical_complex_periodic_boundary_conditions_base.h>
+
+#ifdef GUDHI_USE_TBB
+#include <tbb/parallel_sort.h>
+#endif
+
+#include <limits>
+#include <utility> // for pair<>
+#include <algorithm> // for sort
+#include <vector>
+#include <numeric> // for iota
+
+namespace Gudhi {
+
+namespace cubical_complex {
+
+// global variable, was used just for debugging.
+const bool globalDbg = false;
+
+template <typename T> class is_before_in_filtration;
+
+/**
+ * @brief Cubical complex represented as a bitmap.
+ * @ingroup cubical_complex
+ * @details This is a Bitmap_cubical_complex class. It joints a functionalities of Bitmap_cubical_complex_base and
+ * Bitmap_cubical_complex_periodic_boundary_conditions_base classes into
+ * Gudhi persistent homology engine. It is a template class that inherit from its template parameter. The template
+ * parameter is supposed to be either Bitmap_cubical_complex_base or
+ * Bitmap_cubical_complex_periodic_boundary_conditions_base class.
+ **/
+template <typename T>
+class Bitmap_cubical_complex : public T {
+ public:
+ //*********************************************//
+ // Typedefs and typenames
+ //*********************************************//
+ typedef size_t Simplex_key;
+ typedef typename T::filtration_type Filtration_value;
+ typedef Simplex_key Simplex_handle;
+
+
+ //*********************************************//
+ // Constructors
+ //*********************************************//
+ // Over here we need to define various input types. I am proposing the following ones:
+ // Perseus style
+ // TODO(PD) H5 files?
+ // TODO(PD) binary files with little endiangs / big endians ?
+ // TODO(PD) constructor from a vector of elements of a type T. ?
+
+ /**
+ * Constructor form a Perseus-style file.
+ **/
+ Bitmap_cubical_complex(const char* perseus_style_file) :
+ T(perseus_style_file), key_associated_to_simplex(this->total_number_of_cells + 1) {
+ if (globalDbg) {
+ std::cerr << "Bitmap_cubical_complex( const char* perseus_style_file )\n";
+ }
+ for (size_t i = 0; i != this->total_number_of_cells; ++i) {
+ this->key_associated_to_simplex[i] = i;
+ }
+ // we initialize this only once, in each constructor, when the bitmap is constructed.
+ // If the user decide to change some elements of the bitmap, then this procedure need
+ // to be called again.
+ this->initialize_simplex_associated_to_key();
+ }
+
+ /**
+ * Constructor that requires vector of elements of type unsigned, which gives number of top dimensional cells
+ * in the following directions and vector of element of a type T
+ * with filtration on top dimensional cells.
+ **/
+ Bitmap_cubical_complex(const std::vector<unsigned>& dimensions,
+ const std::vector<typename T::filtration_type>& top_dimensional_cells) :
+ T(dimensions, top_dimensional_cells),
+ key_associated_to_simplex(this->total_number_of_cells + 1) {
+ for (size_t i = 0; i != this->total_number_of_cells; ++i) {
+ this->key_associated_to_simplex[i] = i;
+ }
+ // we initialize this only once, in each constructor, when the bitmap is constructed.
+ // If the user decide to change some elements of the bitmap, then this procedure need
+ // to be called again.
+ this->initialize_simplex_associated_to_key();
+ }
+
+ /**
+ * Constructor that requires vector of elements of type unsigned, which gives number of top dimensional cells
+ * in the following directions and vector of element of a type T::filtration_type
+ * with filtration on top dimensional cells. The last parameter of the constructor is a vector of boolean of a length
+ * equal to the dimension of cubical complex.
+ * If the position i on this vector is true, then we impose periodic boundary conditions in this direction.
+ **/
+ Bitmap_cubical_complex(const std::vector<unsigned>& dimensions,
+ const std::vector<typename T::filtration_type>& top_dimensional_cells,
+ std::vector< bool > directions_in_which_periodic_b_cond_are_to_be_imposed) :
+ T(dimensions, top_dimensional_cells, directions_in_which_periodic_b_cond_are_to_be_imposed),
+ key_associated_to_simplex(this->total_number_of_cells + 1) {
+ for (size_t i = 0; i != this->total_number_of_cells; ++i) {
+ this->key_associated_to_simplex[i] = i;
+ }
+ // we initialize this only once, in each constructor, when the bitmap is constructed.
+ // If the user decide to change some elements of the bitmap, then this procedure need
+ // to be called again.
+ this->initialize_simplex_associated_to_key();
+ }
+
+ /**
+ * Destructor of the Bitmap_cubical_complex class.
+ **/
+ virtual ~Bitmap_cubical_complex() {}
+
+ //*********************************************//
+ // Other 'easy' functions
+ //*********************************************//
+
+ /**
+ * Returns number of all cubes in the complex.
+ **/
+ size_t num_simplices()const {
+ return this->total_number_of_cells;
+ }
+
+ /**
+ * Returns a Simplex_handle to a cube that do not exist in this complex.
+ **/
+ static Simplex_handle null_simplex() {
+ if (globalDbg) {
+ std::cerr << "Simplex_handle null_simplex()\n";
+ }
+ return std::numeric_limits<Simplex_handle>::max();
+ }
+
+ /**
+ * Returns dimension of the complex.
+ **/
+ inline size_t dimension()const {
+ return this->sizes.size();
+ }
+
+ /**
+ * Return dimension of a cell pointed by the Simplex_handle.
+ **/
+ inline unsigned dimension(Simplex_handle sh)const {
+ if (globalDbg) {
+ std::cerr << "unsigned dimension(const Simplex_handle& sh)\n";
+ }
+ if (sh != std::numeric_limits<Simplex_handle>::max()) return this->get_dimension_of_a_cell(sh);
+ return -1;
+ }
+
+ /**
+ * Return the filtration of a cell pointed by the Simplex_handle.
+ **/
+ typename T::filtration_type filtration(Simplex_handle sh) {
+ if (globalDbg) {
+ std::cerr << "T::filtration_type filtration(const Simplex_handle& sh)\n";
+ }
+ // Returns the filtration value of a simplex.
+ if (sh != std::numeric_limits<Simplex_handle>::max()) return this->data[sh];
+ return std::numeric_limits<Simplex_handle>::max();
+ }
+
+ /**
+ * Return a key which is not a key of any cube in the considered data structure.
+ **/
+ static Simplex_key null_key() {
+ if (globalDbg) {
+ std::cerr << "Simplex_key null_key()\n";
+ }
+ return std::numeric_limits<Simplex_handle>::max();
+ }
+
+ /**
+ * Return the key of a cube pointed by the Simplex_handle.
+ **/
+ Simplex_key key(Simplex_handle sh)const {
+ if (globalDbg) {
+ std::cerr << "Simplex_key key(const Simplex_handle& sh)\n";
+ }
+ if (sh != std::numeric_limits<Simplex_handle>::max()) {
+ return this->key_associated_to_simplex[sh];
+ }
+ return this->null_key();
+ }
+
+ /**
+ * Return the Simplex_handle given the key of the cube.
+ **/
+ Simplex_handle simplex(Simplex_key key) {
+ if (globalDbg) {
+ std::cerr << "Simplex_handle simplex(Simplex_key key)\n";
+ }
+ if (key != std::numeric_limits<Simplex_handle>::max()) {
+ return this->simplex_associated_to_key[ key ];
+ }
+ return null_simplex();
+ }
+
+ /**
+ * Assign key to a cube pointed by the Simplex_handle
+ **/
+ void assign_key(Simplex_handle sh, Simplex_key key) {
+ if (globalDbg) {
+ std::cerr << "void assign_key(Simplex_handle& sh, Simplex_key key)\n";
+ }
+ if (key == std::numeric_limits<Simplex_handle>::max()) return;
+ this->key_associated_to_simplex[sh] = key;
+ this->simplex_associated_to_key[key] = sh;
+ }
+
+ /**
+ * Function called from a constructor. It is needed for Filtration_simplex_iterator to work.
+ **/
+ void initialize_simplex_associated_to_key();
+
+ //*********************************************//
+ // Iterators
+ //*********************************************//
+
+ /**
+ * Boundary_simplex_range class provides ranges for boundary iterators.
+ **/
+ typedef typename std::vector< Simplex_handle >::iterator Boundary_simplex_iterator;
+ typedef typename std::vector< Simplex_handle > Boundary_simplex_range;
+
+ /**
+ * Filtration_simplex_iterator class provides an iterator though the whole structure in the order of filtration.
+ * Secondary criteria for filtration are:
+ * (1) Dimension of a cube (lower dimensional comes first).
+ * (2) Position in the data structure (the ones that are earlies in the data structure comes first).
+ **/
+ class Filtration_simplex_range;
+
+ class Filtration_simplex_iterator : std::iterator< std::input_iterator_tag, Simplex_handle > {
+ // Iterator over all simplices of the complex in the order of the indexing scheme.
+ // 'value_type' must be 'Simplex_handle'.
+ public:
+ Filtration_simplex_iterator(Bitmap_cubical_complex* b) : b(b), position(0) { }
+
+ Filtration_simplex_iterator() : b(NULL), position(0) { }
+
+ Filtration_simplex_iterator operator++() {
+ if (globalDbg) {
+ std::cerr << "Filtration_simplex_iterator operator++\n";
+ }
+ ++this->position;
+ return (*this);
+ }
+
+ Filtration_simplex_iterator operator++(int) {
+ Filtration_simplex_iterator result = *this;
+ ++(*this);
+ return result;
+ }
+
+ Filtration_simplex_iterator& operator=(const Filtration_simplex_iterator& rhs) {
+ if (globalDbg) {
+ std::cerr << "Filtration_simplex_iterator operator =\n";
+ }
+ this->b = rhs.b;
+ this->position = rhs.position;
+ return (*this);
+ }
+
+ bool operator==(const Filtration_simplex_iterator& rhs)const {
+ if (globalDbg) {
+ std::cerr << "bool operator == ( const Filtration_simplex_iterator& rhs )\n";
+ }
+ return ( this->position == rhs.position);
+ }
+
+ bool operator!=(const Filtration_simplex_iterator& rhs)const {
+ if (globalDbg) {
+ std::cerr << "bool operator != ( const Filtration_simplex_iterator& rhs )\n";
+ }
+ return !(*this == rhs);
+ }
+
+ Simplex_handle operator*() {
+ if (globalDbg) {
+ std::cerr << "Simplex_handle operator*()\n";
+ }
+ return this->b->simplex_associated_to_key[ this->position ];
+ }
+
+ friend class Filtration_simplex_range;
+
+ private:
+ Bitmap_cubical_complex<T>* b;
+ size_t position;
+ };
+
+ /**
+ * @brief Filtration_simplex_range provides the ranges for Filtration_simplex_iterator.
+ **/
+ class Filtration_simplex_range {
+ // Range over the simplices of the complex in the order of the filtration.
+ // .begin() and .end() return type Filtration_simplex_iterator.
+ public:
+ typedef Filtration_simplex_iterator const_iterator;
+ typedef Filtration_simplex_iterator iterator;
+
+ Filtration_simplex_range(Bitmap_cubical_complex<T>* b) : b(b) { }
+
+ Filtration_simplex_iterator begin() {
+ if (globalDbg) {
+ std::cerr << "Filtration_simplex_iterator begin() \n";
+ }
+ return Filtration_simplex_iterator(this->b);
+ }
+
+ Filtration_simplex_iterator end() {
+ if (globalDbg) {
+ std::cerr << "Filtration_simplex_iterator end()\n";
+ }
+ Filtration_simplex_iterator it(this->b);
+ it.position = this->b->simplex_associated_to_key.size();
+ return it;
+ }
+
+ private:
+ Bitmap_cubical_complex<T>* b;
+ };
+
+
+
+ //*********************************************//
+ // Methods to access iterators from the container:
+
+ /**
+ * boundary_simplex_range creates an object of a Boundary_simplex_range class
+ * that provides ranges for the Boundary_simplex_iterator.
+ **/
+ Boundary_simplex_range boundary_simplex_range(Simplex_handle sh) {
+ return this->get_boundary_of_a_cell(sh);
+ }
+
+ /**
+ * filtration_simplex_range creates an object of a Filtration_simplex_range class
+ * that provides ranges for the Filtration_simplex_iterator.
+ **/
+ Filtration_simplex_range filtration_simplex_range() {
+ if (globalDbg) {
+ std::cerr << "Filtration_simplex_range filtration_simplex_range()\n";
+ }
+ // Returns a range over the simplices of the complex in the order of the filtration
+ return Filtration_simplex_range(this);
+ }
+ //*********************************************//
+
+
+
+ //*********************************************//
+ // Elements which are in Gudhi now, but I (and in all the cases I asked also Marc) do not understand why they are
+ // there.
+ // TODO(PD) the file IndexingTag.h in the Gudhi library contains an empty structure, so
+ // I understand that this is something that was planned (for simplicial maps?)
+ // but was never finished. The only idea I have here is to use the same empty structure from
+ // IndexingTag.h file, but only if the compiler needs it. If the compiler
+ // do not need it, then I would rather not add here elements which I do not understand.
+ // typedef Indexing_tag
+
+ /**
+ * Function needed for compatibility with Gudhi. Not useful for other purposes.
+ **/
+ std::pair<Simplex_handle, Simplex_handle> endpoints(Simplex_handle sh) {
+ std::vector< size_t > bdry = this->get_boundary_of_a_cell(sh);
+ if (globalDbg) {
+ std::cerr << "std::pair<Simplex_handle, Simplex_handle> endpoints( Simplex_handle sh )\n";
+ std::cerr << "bdry.size() : " << bdry.size() << std::endl;
+ }
+ // this method returns two first elements from the boundary of sh.
+ if (bdry.size() < 2)
+ throw("Error in endpoints in Bitmap_cubical_complex class. The cell have less than two elements in the "
+ "boundary.");
+ return std::make_pair(bdry[0], bdry[1]);
+ }
+
+
+ /**
+ * Class needed for compatibility with Gudhi. Not useful for other purposes.
+ **/
+ class Skeleton_simplex_range;
+
+ class Skeleton_simplex_iterator : std::iterator< std::input_iterator_tag, Simplex_handle > {
+ // Iterator over all simplices of the complex in the order of the indexing scheme.
+ // 'value_type' must be 'Simplex_handle'.
+ public:
+ Skeleton_simplex_iterator(Bitmap_cubical_complex* b, size_t d) : b(b), dimension(d) {
+ if (globalDbg) {
+ std::cerr << "Skeleton_simplex_iterator ( Bitmap_cubical_complex* b , size_t d )\n";
+ }
+ // find the position of the first simplex of a dimension d
+ this->position = 0;
+ while (
+ (this->position != b->data.size()) &&
+ (this->b->get_dimension_of_a_cell(this->position) != this->dimension)
+ ) {
+ ++this->position;
+ }
+ }
+
+ Skeleton_simplex_iterator() : b(NULL), position(0), dimension(0) { }
+
+ Skeleton_simplex_iterator operator++() {
+ if (globalDbg) {
+ std::cerr << "Skeleton_simplex_iterator operator++()\n";
+ }
+ // increment the position as long as you did not get to the next element of the dimension dimension.
+ ++this->position;
+ while (
+ (this->position != this->b->data.size()) &&
+ (this->b->get_dimension_of_a_cell(this->position) != this->dimension)
+ ) {
+ ++this->position;
+ }
+ return (*this);
+ }
+
+ Skeleton_simplex_iterator operator++(int) {
+ Skeleton_simplex_iterator result = *this;
+ ++(*this);
+ return result;
+ }
+
+ Skeleton_simplex_iterator& operator=(const Skeleton_simplex_iterator& rhs) {
+ if (globalDbg) {
+ std::cerr << "Skeleton_simplex_iterator operator =\n";
+ }
+ this->b = rhs.b;
+ this->position = rhs.position;
+ this->dimension = rhs.dimension;
+ return (*this);
+ }
+
+ bool operator==(const Skeleton_simplex_iterator& rhs)const {
+ if (globalDbg) {
+ std::cerr << "bool operator ==\n";
+ }
+ return ( this->position == rhs.position);
+ }
+
+ bool operator!=(const Skeleton_simplex_iterator& rhs)const {
+ if (globalDbg) {
+ std::cerr << "bool operator != ( const Skeleton_simplex_iterator& rhs )\n";
+ }
+ return !(*this == rhs);
+ }
+
+ Simplex_handle operator*() {
+ if (globalDbg) {
+ std::cerr << "Simplex_handle operator*() \n";
+ }
+ return this->position;
+ }
+
+ friend class Skeleton_simplex_range;
+ private:
+ Bitmap_cubical_complex<T>* b;
+ size_t position;
+ unsigned dimension;
+ };
+
+ /**
+ * @brief Class needed for compatibility with Gudhi. Not useful for other purposes.
+ **/
+ class Skeleton_simplex_range {
+ // Range over the simplices of the complex in the order of the filtration.
+ // .begin() and .end() return type Filtration_simplex_iterator.
+ public:
+ typedef Skeleton_simplex_iterator const_iterator;
+ typedef Skeleton_simplex_iterator iterator;
+
+ Skeleton_simplex_range(Bitmap_cubical_complex<T>* b, unsigned dimension) : b(b), dimension(dimension) { }
+
+ Skeleton_simplex_iterator begin() {
+ if (globalDbg) {
+ std::cerr << "Skeleton_simplex_iterator begin()\n";
+ }
+ return Skeleton_simplex_iterator(this->b, this->dimension);
+ }
+
+ Skeleton_simplex_iterator end() {
+ if (globalDbg) {
+ std::cerr << "Skeleton_simplex_iterator end()\n";
+ }
+ Skeleton_simplex_iterator it(this->b, this->dimension);
+ it.position = this->b->data.size();
+ return it;
+ }
+
+ private:
+ Bitmap_cubical_complex<T>* b;
+ unsigned dimension;
+ };
+
+ /**
+ * Function needed for compatibility with Gudhi. Not useful for other purposes.
+ **/
+ Skeleton_simplex_range skeleton_simplex_range(unsigned dimension) {
+ if (globalDbg) {
+ std::cerr << "Skeleton_simplex_range skeleton_simplex_range( unsigned dimension )\n";
+ }
+ return Skeleton_simplex_range(this, dimension);
+ }
+
+ friend class is_before_in_filtration<T>;
+
+ protected:
+ std::vector< size_t > key_associated_to_simplex;
+ std::vector< size_t > simplex_associated_to_key;
+}; // Bitmap_cubical_complex
+
+template <typename T>
+void Bitmap_cubical_complex<T>::initialize_simplex_associated_to_key() {
+ if (globalDbg) {
+ std::cerr << "void Bitmap_cubical_complex<T>::initialize_elements_ordered_according_to_filtration() \n";
+ }
+ this->simplex_associated_to_key = std::vector<size_t>(this->data.size());
+ std::iota(std::begin(simplex_associated_to_key), std::end(simplex_associated_to_key), 0);
+#ifdef GUDHI_USE_TBB
+ tbb::parallel_sort(simplex_associated_to_key.begin(), simplex_associated_to_key.end(),
+ is_before_in_filtration<T>(this));
+#else
+ std::sort(simplex_associated_to_key.begin(), simplex_associated_to_key.end(), is_before_in_filtration<T>(this));
+#endif
+
+ // we still need to deal here with a key_associated_to_simplex:
+ for ( size_t i = 0 ; i != simplex_associated_to_key.size() ; ++i ) {
+ this->key_associated_to_simplex[ simplex_associated_to_key[i] ] = i;
+ }
+}
+
+template <typename T>
+class is_before_in_filtration {
+ public:
+ explicit is_before_in_filtration(Bitmap_cubical_complex<T> * CC)
+ : CC_(CC) { }
+
+ bool operator()(const typename Bitmap_cubical_complex<T>::Simplex_handle& sh1,
+ const typename Bitmap_cubical_complex<T>::Simplex_handle& sh2) const {
+ // Not using st_->filtration(sh1) because it uselessly tests for null_simplex.
+ typename T::filtration_type fil1 = CC_->data[sh1];
+ typename T::filtration_type fil2 = CC_->data[sh2];
+ if (fil1 != fil2) {
+ return fil1 < fil2;
+ }
+ // in this case they are on the same filtration level, so the dimension decide.
+ size_t dim1 = CC_->get_dimension_of_a_cell(sh1);
+ size_t dim2 = CC_->get_dimension_of_a_cell(sh2);
+ if (dim1 != dim2) {
+ return dim1 < dim2;
+ }
+ // in this case both filtration and dimensions of the considered cubes are the same. To have stable sort, we simply
+ // compare their positions in the bitmap:
+ return sh1 < sh2;
+ }
+
+ protected:
+ Bitmap_cubical_complex<T>* CC_;
+};
+
+} // namespace cubical_complex
+
+namespace Cubical_complex = cubical_complex;
+
+} // namespace Gudhi
+
+#endif // BITMAP_CUBICAL_COMPLEX_H_
diff --git a/include/gudhi/Bitmap_cubical_complex/counter.h b/include/gudhi/Bitmap_cubical_complex/counter.h
new file mode 100644
index 00000000..4b072f10
--- /dev/null
+++ b/include/gudhi/Bitmap_cubical_complex/counter.h
@@ -0,0 +1,144 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Pawel Dlotko
+ *
+ * Copyright (C) 2015 INRIA Sophia-Saclay (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef BITMAP_CUBICAL_COMPLEX_COUNTER_H_
+#define BITMAP_CUBICAL_COMPLEX_COUNTER_H_
+
+#include <iostream>
+#include <vector>
+
+namespace Gudhi {
+
+namespace cubical_complex {
+
+/**
+ * @brief This is an implementation of a counter being a vector of integers.
+ * @details The constructor of the class takes as an input two vectors W and V.
+ * It assumes that W < V coordinatewise.
+ * If the initial counter W is not specified, it is assumed to be vector of zeros.
+ * The class allows to iterate between W and V by using increment() function.
+ * The increment() function returns a bool value.
+ * The current counter reach the end counter V if the value returned by the increment function is FALSE.
+ * This class is needed for the implementation of a bitmapCubicalComplex.
+ **/
+class counter {
+ public:
+ /**
+ * Constructor of a counter class. It takes only the parameter which is the end value of the counter.
+ * The default beginning value is a vector of the same length as the endd, filled-in with zeros.
+ **/
+ counter(const std::vector<unsigned>& endd) : begin(endd.size(), 0), end(endd), current(endd.size(), 0) { }
+
+ /**
+ * Constructor of a counter class. It takes as the input beginn and end vector.
+ * It assumes that begin vector is lexicographically below the end vector.
+ **/
+ counter(const std::vector< unsigned >& beginn, const std::vector< unsigned >& endd) : begin(beginn), end(endd), current(endd.size(), 0) {
+ if (beginn.size() != endd.size())
+ throw "In constructor of a counter, begin and end vectors do not have the same size. Program terminate";
+ }
+
+ /**
+ * Function to increment the counter. If the value returned by the function is true,
+ * then the incrementation process was successful.
+ * If the value of the function is false, that means, that the counter have reached its end-value.
+ **/
+ bool increment() {
+ size_t i = 0;
+ while ((i != this->end.size()) && (this->current[i] == this->end[i])) {
+ ++i;
+ }
+
+ if (i == this->end.size())return false;
+ ++this->current[i];
+ for (size_t j = 0; j != i; ++j) {
+ this->current[j] = this->begin[j];
+ }
+ return true;
+ }
+
+ /**
+ * Function to check if we are at the end of counter.
+ **/
+ bool isFinal() {
+ for (size_t i = 0; i != this->current.size(); ++i) {
+ if (this->current[i] == this->end[i])return true;
+ }
+ return false;
+ }
+
+ /**
+ * Function required in the implementation of bitmapCubicalComplexWPeriodicBoundaryCondition.
+ * Its aim is to find an counter corresponding to the element the following
+ * boundary element is identified with when periodic boundary conditions are imposed.
+ **/
+ std::vector< unsigned > find_opposite(const std::vector< bool >& directionsForPeriodicBCond) {
+ std::vector< unsigned > result;
+ for (size_t i = 0; i != this->current.size(); ++i) {
+ if ((this->current[i] == this->end[i]) && (directionsForPeriodicBCond[i] == true)) {
+ result.push_back(this->begin[i]);
+ } else {
+ result.push_back(this->current[i]);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Function checking at which positions the current value of a counter is the final value of the counter.
+ **/
+ std::vector< bool > directions_of_finals() {
+ std::vector< bool > result;
+ for (size_t i = 0; i != this->current.size(); ++i) {
+ if (this->current[i] == this->end[i]) {
+ result.push_back(true);
+ } else {
+ result.push_back(false);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Function to write counter to the stream.
+ **/
+ friend std::ostream& operator<<(std::ostream& out, const counter& c) {
+ // std::cerr << "c.current.size() : " << c.current.size() << endl;
+ for (size_t i = 0; i != c.current.size(); ++i) {
+ out << c.current[i] << " ";
+ }
+ return out;
+ }
+
+ private:
+ std::vector< unsigned > begin;
+ std::vector< unsigned > end;
+ std::vector< unsigned > current;
+};
+
+} // namespace cubical_complex
+
+namespace Cubical_complex = cubical_complex;
+
+} // namespace Gudhi
+
+#endif // BITMAP_CUBICAL_COMPLEX_COUNTER_H_
diff --git a/include/gudhi/Bitmap_cubical_complex_base.h b/include/gudhi/Bitmap_cubical_complex_base.h
new file mode 100644
index 00000000..0442ac34
--- /dev/null
+++ b/include/gudhi/Bitmap_cubical_complex_base.h
@@ -0,0 +1,817 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Pawel Dlotko
+ *
+ * Copyright (C) 2015 INRIA Sophia-Saclay (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef BITMAP_CUBICAL_COMPLEX_BASE_H_
+#define BITMAP_CUBICAL_COMPLEX_BASE_H_
+
+#include <gudhi/Bitmap_cubical_complex/counter.h>
+
+#include <iostream>
+#include <vector>
+#include <string>
+#include <fstream>
+#include <algorithm>
+#include <iterator>
+#include <limits>
+#include <utility> // for pair<>
+
+namespace Gudhi {
+
+namespace cubical_complex {
+
+/**
+ * @brief Cubical complex represented as a bitmap, class with basic implementation.
+ * @ingroup cubical_complex
+ * @details This is a class implementing a basic bitmap data structure to store cubical complexes.
+ * It implements only the most basic subroutines.
+ * The idea of the bitmap is the following. Our aim is to have a memory efficient
+ * data structure to store d-dimensional cubical complex
+ * C being a cubical decomposition
+ * of a rectangular region of a space. This is achieved by storing C as a
+ * vector of bits (this is where the name 'bitmap' came from).
+ * Each cell is represented by a single
+ * bit (in case of black and white bitmaps, or by a single element of a type T
+ * (here T is a filtration type of a bitmap, typically a double).
+ * All the informations needed for homology and
+ * persistent homology computations (like dimension of a cell, boundary and
+ * coboundary elements of a cell, are then obtained from the
+ * position of the element in C.
+ * The default filtration used in this implementation is the lower star filtration.
+ */
+template <typename T>
+class Bitmap_cubical_complex_base {
+ public:
+ typedef T filtration_type;
+
+ /**
+ *Default constructor
+ **/
+ Bitmap_cubical_complex_base() :
+ total_number_of_cells(0) { }
+ /**
+ * There are a few constructors of a Bitmap_cubical_complex_base class.
+ * First one, that takes vector<unsigned>, creates an empty bitmap of a dimension equal
+ * the number of elements in the
+ * input vector and size in the i-th dimension equal the number in the position i-of the input vector.
+ */
+ Bitmap_cubical_complex_base(const std::vector<unsigned>& sizes);
+ /**
+ * The second constructor takes as a input a Perseus style file. For more details,
+ * please consult the documentations of
+ * Perseus software as well as examples attached to this
+ * implementation.
+ **/
+ Bitmap_cubical_complex_base(const char* perseus_style_file);
+ /**
+ * The last constructor of a Bitmap_cubical_complex_base class accepts vector of dimensions (as the first one)
+ * together with vector of filtration values of top dimensional cells.
+ **/
+ Bitmap_cubical_complex_base(const std::vector<unsigned>& dimensions, const std::vector<T>& top_dimensional_cells);
+
+ /**
+ * Destructor of the Bitmap_cubical_complex_base class.
+ **/
+ virtual ~Bitmap_cubical_complex_base() { }
+
+ /**
+ * The functions get_boundary_of_a_cell, get_coboundary_of_a_cell, get_dimension_of_a_cell
+ * and get_cell_data are the basic
+ * functions that compute boundary / coboundary / dimension and the filtration
+ * value form a position of a cell in the structure of a bitmap. The input parameter of all of those function is a
+ * non-negative integer, indicating a position of a cube in the data structure.
+ * In the case of functions that compute (co)boundary, the output is a vector if non-negative integers pointing to
+ * the positions of (co)boundary element of the input cell.
+ */
+ virtual inline std::vector< size_t > get_boundary_of_a_cell(size_t cell)const;
+ /**
+ * The functions get_coboundary_of_a_cell, get_coboundary_of_a_cell,
+ * get_dimension_of_a_cell and get_cell_data are the basic
+ * functions that compute boundary / coboundary / dimension and the filtration
+ * value form a position of a cell in the structure of a bitmap.
+ * The input parameter of all of those function is a non-negative integer,
+ * indicating a position of a cube in the data structure.
+ * In the case of functions that compute (co)boundary, the output is a vector if
+ * non-negative integers pointing to the
+ * positions of (co)boundary element of the input cell.
+ **/
+ virtual inline std::vector< size_t > get_coboundary_of_a_cell(size_t cell)const;
+ /**
+ * In the case of get_dimension_of_a_cell function, the output is a non-negative integer
+ * indicating the dimension of a cell.
+ **/
+ inline unsigned get_dimension_of_a_cell(size_t cell)const;
+ /**
+ * In the case of get_cell_data, the output parameter is a reference to the value of a cube in a given position.
+ * This allows reading and changing the value of filtration. Note that if the value of a filtration is changed, the
+ * code do not check if we have a filtration or not. i.e. it do not check if the value of a filtration of a cell is
+ * not smaller than the value of a filtration of its boundary and not greater than the value of its coboundary.
+ **/
+ inline T& get_cell_data(size_t cell);
+
+
+ /**
+ * Typical input used to construct a baseBitmap class is a filtration given at the top dimensional cells.
+ * Then, there are a few ways one can pick the filtration of lower dimensional
+ * cells. The most typical one is by so called lower star filtration. This function is always called by any
+ * constructor which takes the top dimensional cells. If you use such a constructor,
+ * then there is no need to call this function. Call it only if you are putting the filtration
+ * of the cells by your own (for instance by using Top_dimensional_cells_iterator).
+ **/
+ void impose_lower_star_filtration(); // assume that top dimensional cells are already set.
+
+ /**
+ * Returns dimension of a complex.
+ **/
+ inline unsigned dimension()const {
+ return sizes.size();
+ }
+
+ /**
+ * Returns number of all cubes in the data structure.
+ **/
+ inline unsigned size()const {
+ return this->data.size();
+ }
+
+ /**
+ * Writing to stream operator. By using it we get the values T of cells in order in which they are stored in the
+ * structure. This procedure is used for debugging purposes.
+ **/
+ template <typename K>
+ friend std::ostream& operator<<(std::ostream & os, const Bitmap_cubical_complex_base<K>& b);
+
+ /**
+ * Function that put the input data to bins. By putting data to bins we mean rounding them to a sequence of values
+ * equally distributed in the range of data.
+ * Sometimes if most of the cells have different birth-death times, the performance of the algorithms to compute
+ * persistence gets worst. When dealing with this type of data, one may want to put different values on cells to
+ * some number of bins. The function put_data_to_bins( size_t number_of_bins ) is designed for that purpose.
+ * The parameter of the function is the number of bins (distinct values) we want to have in the cubical complex.
+ **/
+ void put_data_to_bins(size_t number_of_bins);
+
+ /**
+ * Function that put the input data to bins. By putting data to bins we mean rounding them to a sequence of values
+ * equally distributed in the range of data.
+ * Sometimes if most of the cells have different birth-death times, the performance of the algorithms to compute
+ * persistence gets worst. When dealing with this type of data, one may want to put different values on cells to
+ * some number of bins. The function put_data_to_bins( T diameter_of_bin ) is designed for that purpose.
+ * The parameter of it is the diameter of each bin. Note that the bottleneck distance between the persistence
+ * diagram of the cubical complex before and after using such a function will be bounded by the parameter
+ * diameter_of_bin.
+ **/
+ void put_data_to_bins(T diameter_of_bin);
+
+ /**
+ * Functions to find min and max values of filtration.
+ **/
+ std::pair< T, T > min_max_filtration();
+
+ // ITERATORS
+
+ /**
+ * @brief Iterator through all cells in the complex (in order they appear in the structure -- i.e.
+ * in lexicographical order).
+ **/
+ class All_cells_iterator : std::iterator< std::input_iterator_tag, T > {
+ public:
+ All_cells_iterator() {
+ this->counter = 0;
+ }
+
+ All_cells_iterator operator++() {
+ // first find first element of the counter that can be increased:
+ ++this->counter;
+ return *this;
+ }
+
+ All_cells_iterator operator++(int) {
+ All_cells_iterator result = *this;
+ ++(*this);
+ return result;
+ }
+
+ All_cells_iterator& operator=(const All_cells_iterator& rhs) {
+ this->counter = rhs.counter;
+ return *this;
+ }
+
+ bool operator==(const All_cells_iterator& rhs)const {
+ if (this->counter != rhs.counter)return false;
+ return true;
+ }
+
+ bool operator!=(const All_cells_iterator& rhs)const {
+ return !(*this == rhs);
+ }
+
+ /*
+ * The operator * returns position of a cube in the structure of cubical complex. This position can be then used as
+ * an argument of the following functions:
+ * get_boundary_of_a_cell, get_coboundary_of_a_cell, get_dimension_of_a_cell to get information about the cell
+ * boundary and coboundary and dimension
+ * and in function get_cell_data to get a filtration of a cell.
+ */
+ size_t operator*() {
+ return this->counter;
+ }
+ friend class Bitmap_cubical_complex_base;
+ protected:
+ size_t counter;
+ };
+
+ /**
+ * Function returning a All_cells_iterator to the first cell of the bitmap.
+ **/
+ All_cells_iterator all_cells_iterator_begin() {
+ All_cells_iterator a;
+ return a;
+ }
+
+ /**
+ * Function returning a All_cells_iterator to the last cell of the bitmap.
+ **/
+ All_cells_iterator all_cells_iterator_end() {
+ All_cells_iterator a;
+ a.counter = this->data.size();
+ return a;
+ }
+
+ /**
+ * @brief All_cells_range class provides ranges for All_cells_iterator
+ **/
+ class All_cells_range {
+ public:
+ All_cells_range(Bitmap_cubical_complex_base* b) : b(b) { }
+
+ All_cells_iterator begin() {
+ return b->all_cells_iterator_begin();
+ }
+
+ All_cells_iterator end() {
+ return b->all_cells_iterator_end();
+ }
+ private:
+ Bitmap_cubical_complex_base<T>* b;
+ };
+
+ All_cells_range all_cells_range() {
+ return All_cells_range(this);
+ }
+
+
+ /**
+ * Boundary_range class provides ranges for boundary iterators.
+ **/
+ typedef typename std::vector< size_t >::const_iterator Boundary_iterator;
+ typedef typename std::vector< size_t > Boundary_range;
+
+ /**
+ * boundary_simplex_range creates an object of a Boundary_simplex_range class
+ * that provides ranges for the Boundary_simplex_iterator.
+ **/
+ Boundary_range boundary_range(size_t sh) {
+ return this->get_boundary_of_a_cell(sh);
+ }
+
+ /**
+ * Coboundary_range class provides ranges for boundary iterators.
+ **/
+ typedef typename std::vector< size_t >::const_iterator Coboundary_iterator;
+ typedef typename std::vector< size_t > Coboundary_range;
+
+ /**
+ * boundary_simplex_range creates an object of a Boundary_simplex_range class
+ * that provides ranges for the Boundary_simplex_iterator.
+ **/
+ Coboundary_range coboundary_range(size_t sh) {
+ return this->get_coboundary_of_a_cell(sh);
+ }
+
+ /**
+ * @brief Iterator through top dimensional cells of the complex. The cells appear in order they are stored
+ * in the structure (i.e. in lexicographical order)
+ **/
+ class Top_dimensional_cells_iterator : std::iterator< std::input_iterator_tag, T > {
+ public:
+ Top_dimensional_cells_iterator(Bitmap_cubical_complex_base& b) : b(b) {
+ this->counter = std::vector<size_t>(b.dimension());
+ // std::fill( this->counter.begin() , this->counter.end() , 0 );
+ }
+
+ Top_dimensional_cells_iterator operator++() {
+ // first find first element of the counter that can be increased:
+ size_t dim = 0;
+ while ((dim != this->b.dimension()) && (this->counter[dim] == this->b.sizes[dim] - 1))++dim;
+
+ if (dim != this->b.dimension()) {
+ ++this->counter[dim];
+ for (size_t i = 0; i != dim; ++i) {
+ this->counter[i] = 0;
+ }
+ } else {
+ ++this->counter[0];
+ }
+ return *this;
+ }
+
+ Top_dimensional_cells_iterator operator++(int) {
+ Top_dimensional_cells_iterator result = *this;
+ ++(*this);
+ return result;
+ }
+
+ Top_dimensional_cells_iterator& operator=(const Top_dimensional_cells_iterator& rhs) {
+ this->counter = rhs.counter;
+ this->b = rhs.b;
+ return *this;
+ }
+
+ bool operator==(const Top_dimensional_cells_iterator& rhs)const {
+ if (&this->b != &rhs.b)return false;
+ if (this->counter.size() != rhs.counter.size())return false;
+ for (size_t i = 0; i != this->counter.size(); ++i) {
+ if (this->counter[i] != rhs.counter[i])return false;
+ }
+ return true;
+ }
+
+ bool operator!=(const Top_dimensional_cells_iterator& rhs)const {
+ return !(*this == rhs);
+ }
+
+ /*
+ * The operator * returns position of a cube in the structure of cubical complex. This position can be then used as
+ * an argument of the following functions:
+ * get_boundary_of_a_cell, get_coboundary_of_a_cell, get_dimension_of_a_cell to get information about the cell
+ * boundary and coboundary and dimension
+ * and in function get_cell_data to get a filtration of a cell.
+ */
+ size_t operator*() {
+ return this->compute_index_in_bitmap();
+ }
+
+ size_t compute_index_in_bitmap()const {
+ size_t index = 0;
+ for (size_t i = 0; i != this->counter.size(); ++i) {
+ index += (2 * this->counter[i] + 1) * this->b.multipliers[i];
+ }
+ return index;
+ }
+
+ void print_counter()const {
+ for (size_t i = 0; i != this->counter.size(); ++i) {
+ std::cout << this->counter[i] << " ";
+ }
+ }
+ friend class Bitmap_cubical_complex_base;
+ protected:
+ std::vector< size_t > counter;
+ Bitmap_cubical_complex_base& b;
+ };
+
+ /**
+ * Function returning a Top_dimensional_cells_iterator to the first top dimensional cell of the bitmap.
+ **/
+ Top_dimensional_cells_iterator top_dimensional_cells_iterator_begin() {
+ Top_dimensional_cells_iterator a(*this);
+ return a;
+ }
+
+ /**
+ * Function returning a Top_dimensional_cells_iterator to the last top dimensional cell of the bitmap.
+ **/
+ Top_dimensional_cells_iterator top_dimensional_cells_iterator_end() {
+ Top_dimensional_cells_iterator a(*this);
+ for (size_t i = 0; i != this->dimension(); ++i) {
+ a.counter[i] = this->sizes[i] - 1;
+ }
+ a.counter[0]++;
+ return a;
+ }
+
+ /**
+ * @brief Top_dimensional_cells_iterator_range class provides ranges for Top_dimensional_cells_iterator_range
+ **/
+ class Top_dimensional_cells_range {
+ public:
+ Top_dimensional_cells_range(Bitmap_cubical_complex_base* b) : b(b) { }
+
+ Top_dimensional_cells_iterator begin() {
+ return b->top_dimensional_cells_iterator_begin();
+ }
+
+ Top_dimensional_cells_iterator end() {
+ return b->top_dimensional_cells_iterator_end();
+ }
+ private:
+ Bitmap_cubical_complex_base<T>* b;
+ };
+
+ Top_dimensional_cells_range top_dimensional_cells_range() {
+ return Top_dimensional_cells_range(this);
+ }
+
+
+ //****************************************************************************************************************//
+ //****************************************************************************************************************//
+ //****************************************************************************************************************//
+ //****************************************************************************************************************//
+
+ inline size_t number_cells()const {
+ return this->total_number_of_cells;
+ }
+
+ //****************************************************************************************************************//
+ //****************************************************************************************************************//
+ //****************************************************************************************************************//
+ //****************************************************************************************************************//
+
+ protected:
+ std::vector<unsigned> sizes;
+ std::vector<unsigned> multipliers;
+ std::vector<T> data;
+ size_t total_number_of_cells;
+
+ void set_up_containers(const std::vector<unsigned>& sizes) {
+ unsigned multiplier = 1;
+ for (size_t i = 0; i != sizes.size(); ++i) {
+ this->sizes.push_back(sizes[i]);
+ this->multipliers.push_back(multiplier);
+ multiplier *= 2 * sizes[i] + 1;
+ }
+ this->data = std::vector<T>(multiplier, std::numeric_limits<T>::max());
+ this->total_number_of_cells = multiplier;
+ }
+
+ size_t compute_position_in_bitmap(const std::vector< unsigned >& counter) {
+ size_t position = 0;
+ for (size_t i = 0; i != this->multipliers.size(); ++i) {
+ position += this->multipliers[i] * counter[i];
+ }
+ return position;
+ }
+
+ std::vector<unsigned> compute_counter_for_given_cell(size_t cell)const {
+ std::vector<unsigned> counter;
+ counter.reserve(this->sizes.size());
+ for (size_t dim = this->sizes.size(); dim != 0; --dim) {
+ counter.push_back(cell / this->multipliers[dim - 1]);
+ cell = cell % this->multipliers[dim - 1];
+ }
+ std::reverse(counter.begin(), counter.end());
+ return counter;
+ }
+ void read_perseus_style_file(const char* perseus_style_file);
+ void setup_bitmap_based_on_top_dimensional_cells_list(const std::vector<unsigned>& sizes_in_following_directions,
+ const std::vector<T>& top_dimensional_cells);
+ Bitmap_cubical_complex_base(const char* perseus_style_file, std::vector<bool> directions);
+ Bitmap_cubical_complex_base(const std::vector<unsigned>& sizes, std::vector<bool> directions);
+ Bitmap_cubical_complex_base(const std::vector<unsigned>& dimensions,
+ const std::vector<T>& top_dimensional_cells,
+ std::vector<bool> directions);
+};
+
+template <typename T>
+void Bitmap_cubical_complex_base<T>::put_data_to_bins(size_t number_of_bins) {
+ bool bdg = false;
+
+ std::pair< T, T > min_max = this->min_max_filtration();
+ T dx = (min_max.second - min_max.first) / (T) number_of_bins;
+
+ // now put the data into the appropriate bins:
+ for (size_t i = 0; i != this->data.size(); ++i) {
+ if (bdg) {
+ std::cerr << "Before binning : " << this->data[i] << std::endl;
+ }
+ this->data[i] = min_max.first + dx * (this->data[i] - min_max.first) / number_of_bins;
+ if (bdg) {
+ std::cerr << "After binning : " << this->data[i] << std::endl;
+ getchar();
+ }
+ }
+}
+
+template <typename T>
+void Bitmap_cubical_complex_base<T>::put_data_to_bins(T diameter_of_bin) {
+ bool bdg = false;
+ std::pair< T, T > min_max = this->min_max_filtration();
+
+ size_t number_of_bins = (min_max.second - min_max.first) / diameter_of_bin;
+ // now put the data into the appropriate bins:
+ for (size_t i = 0; i != this->data.size(); ++i) {
+ if (bdg) {
+ std::cerr << "Before binning : " << this->data[i] << std::endl;
+ }
+ this->data[i] = min_max.first + diameter_of_bin * (this->data[i] - min_max.first) / number_of_bins;
+ if (bdg) {
+ std::cerr << "After binning : " << this->data[i] << std::endl;
+ getchar();
+ }
+ }
+}
+
+template <typename T>
+std::pair< T, T > Bitmap_cubical_complex_base<T>::min_max_filtration() {
+ std::pair< T, T > min_max(std::numeric_limits<T>::max(), std::numeric_limits<T>::min());
+ for (size_t i = 0; i != this->data.size(); ++i) {
+ if (this->data[i] < min_max.first)min_max.first = this->data[i];
+ if (this->data[i] > min_max.second)min_max.second = this->data[i];
+ }
+ return min_max;
+}
+
+template <typename K>
+std::ostream& operator<<(std::ostream & out, const Bitmap_cubical_complex_base<K>& b) {
+ for (typename Bitmap_cubical_complex_base<K>::all_cells_const_iterator
+ it = b.all_cells_const_begin(); it != b.all_cells_const_end(); ++it) {
+ out << *it << " ";
+ }
+ return out;
+}
+
+template <typename T>
+Bitmap_cubical_complex_base<T>::Bitmap_cubical_complex_base
+(const std::vector<unsigned>& sizes) {
+ this->set_up_containers(sizes);
+}
+
+template <typename T>
+void Bitmap_cubical_complex_base<T>::setup_bitmap_based_on_top_dimensional_cells_list(const std::vector<unsigned>& sizes_in_following_directions,
+ const std::vector<T>& top_dimensional_cells) {
+ this->set_up_containers(sizes_in_following_directions);
+
+ size_t number_of_top_dimensional_elements = 1;
+ for (size_t i = 0; i != sizes_in_following_directions.size(); ++i) {
+ number_of_top_dimensional_elements *= sizes_in_following_directions[i];
+ }
+ if (number_of_top_dimensional_elements != top_dimensional_cells.size()) {
+ std::cerr << "Error in constructor Bitmap_cubical_complex_base ( std::vector<size_t> sizes_in_following_directions"
+ << ", std::vector<T> top_dimensional_cells ). Number of top dimensional elements that follow from "
+ << "sizes_in_following_directions vector is different than the size of top_dimensional_cells vector."
+ << std::endl;
+ throw("Error in constructor Bitmap_cubical_complex_base( std::vector<size_t> sizes_in_following_directions,"
+ "std::vector<T> top_dimensional_cells ). Number of top dimensional elements that follow from "
+ "sizes_in_following_directions vector is different than the size of top_dimensional_cells vector.");
+ }
+
+ Bitmap_cubical_complex_base<T>::Top_dimensional_cells_iterator it(*this);
+ size_t index = 0;
+ for (it = this->top_dimensional_cells_iterator_begin(); it != this->top_dimensional_cells_iterator_end(); ++it) {
+ this->get_cell_data(*it) = top_dimensional_cells[index];
+ ++index;
+ }
+ this->impose_lower_star_filtration();
+}
+
+template <typename T>
+Bitmap_cubical_complex_base<T>::Bitmap_cubical_complex_base
+(const std::vector<unsigned>& sizes_in_following_directions, const std::vector<T>& top_dimensional_cells) {
+ this->setup_bitmap_based_on_top_dimensional_cells_list(sizes_in_following_directions, top_dimensional_cells);
+}
+
+template <typename T>
+void Bitmap_cubical_complex_base<T>::read_perseus_style_file(const char* perseus_style_file) {
+ bool dbg = false;
+ std::ifstream inFiltration;
+ inFiltration.open(perseus_style_file);
+ unsigned dimensionOfData;
+ inFiltration >> dimensionOfData;
+
+ if (dbg) {
+ std::cerr << "dimensionOfData : " << dimensionOfData << std::endl;
+ getchar();
+ }
+
+ std::vector<unsigned> sizes;
+ sizes.reserve(dimensionOfData);
+ for (size_t i = 0; i != dimensionOfData; ++i) {
+ unsigned size_in_this_dimension;
+ inFiltration >> size_in_this_dimension;
+ sizes.push_back(size_in_this_dimension);
+ if (dbg) {
+ std::cerr << "size_in_this_dimension : " << size_in_this_dimension << std::endl;
+ }
+ }
+ this->set_up_containers(sizes);
+
+ Bitmap_cubical_complex_base<T>::Top_dimensional_cells_iterator it(*this);
+ it = this->top_dimensional_cells_iterator_begin();
+
+ while (!inFiltration.eof()) {
+ T filtrationLevel;
+ inFiltration >> filtrationLevel;
+ if (dbg) {
+ std::cerr << "Cell of an index : "
+ << it.compute_index_in_bitmap()
+ << " and dimension: "
+ << this->get_dimension_of_a_cell(it.compute_index_in_bitmap())
+ << " get the value : " << filtrationLevel << std::endl;
+ }
+ this->get_cell_data(*it) = filtrationLevel;
+ ++it;
+ }
+ inFiltration.close();
+ this->impose_lower_star_filtration();
+}
+
+template <typename T>
+Bitmap_cubical_complex_base<T>::Bitmap_cubical_complex_base(const char* perseus_style_file,
+ std::vector<bool> directions) {
+ // this constructor is here just for compatibility with a class that creates cubical complexes with periodic boundary
+ // conditions.
+ // It ignores the last parameter of the function.
+ this->read_perseus_style_file(perseus_style_file);
+}
+
+template <typename T>
+Bitmap_cubical_complex_base<T>::Bitmap_cubical_complex_base(const std::vector<unsigned>& sizes,
+ std::vector<bool> directions) {
+ // this constructor is here just for compatibility with a class that creates cubical complexes with periodic boundary
+ // conditions.
+ // It ignores the last parameter of the function.
+ this->set_up_containers(sizes);
+}
+
+template <typename T>
+Bitmap_cubical_complex_base<T>::Bitmap_cubical_complex_base(const std::vector<unsigned>& dimensions,
+ const std::vector<T>& top_dimensional_cells,
+ std::vector<bool> directions) {
+ // this constructor is here just for compatibility with a class that creates cubical complexes with periodic boundary
+ // conditions.
+ // It ignores the last parameter of the function.
+ this->setup_bitmap_based_on_top_dimensional_cells_list(dimensions, top_dimensional_cells);
+}
+
+template <typename T>
+Bitmap_cubical_complex_base<T>::Bitmap_cubical_complex_base(const char* perseus_style_file) {
+ this->read_perseus_style_file(perseus_style_file);
+}
+
+template <typename T>
+std::vector< size_t > Bitmap_cubical_complex_base<T>::get_boundary_of_a_cell(size_t cell)const {
+ std::vector< size_t > boundary_elements;
+
+ // Speed traded of for memory. Check if it is better in practice.
+ boundary_elements.reserve(this->dimension()*2);
+
+ size_t cell1 = cell;
+ for (size_t i = this->multipliers.size(); i != 0; --i) {
+ unsigned position = cell1 / this->multipliers[i - 1];
+ if (position % 2 == 1) {
+ boundary_elements.push_back(cell - this->multipliers[ i - 1 ]);
+ boundary_elements.push_back(cell + this->multipliers[ i - 1 ]);
+ }
+ cell1 = cell1 % this->multipliers[i - 1];
+ }
+ return boundary_elements;
+}
+
+template <typename T>
+std::vector< size_t > Bitmap_cubical_complex_base<T>::get_coboundary_of_a_cell(size_t cell)const {
+ std::vector<unsigned> counter = this->compute_counter_for_given_cell(cell);
+ std::vector< size_t > coboundary_elements;
+ size_t cell1 = cell;
+ for (size_t i = this->multipliers.size(); i != 0; --i) {
+ unsigned position = cell1 / this->multipliers[i - 1];
+ if (position % 2 == 0) {
+ if ((cell > this->multipliers[i - 1]) && (counter[i - 1] != 0)) {
+ coboundary_elements.push_back(cell - this->multipliers[i - 1]);
+ }
+ if (
+ (cell + this->multipliers[i - 1] < this->data.size()) && (counter[i - 1] != 2 * this->sizes[i - 1])) {
+ coboundary_elements.push_back(cell + this->multipliers[i - 1]);
+ }
+ }
+ cell1 = cell1 % this->multipliers[i - 1];
+ }
+ return coboundary_elements;
+}
+
+template <typename T>
+unsigned Bitmap_cubical_complex_base<T>::get_dimension_of_a_cell(size_t cell)const {
+ bool dbg = false;
+ if (dbg) std::cerr << "\n\n\n Computing position o a cell of an index : " << cell << std::endl;
+ unsigned dimension = 0;
+ for (size_t i = this->multipliers.size(); i != 0; --i) {
+ unsigned position = cell / this->multipliers[i - 1];
+
+ if (dbg) {
+ std::cerr << "i-1 :" << i - 1 << std::endl;
+ std::cerr << "cell : " << cell << std::endl;
+ std::cerr << "position : " << position << std::endl;
+ std::cerr << "multipliers[" << i - 1 << "] = " << this->multipliers[i - 1] << std::endl;
+ getchar();
+ }
+
+ if (position % 2 == 1) {
+ if (dbg) std::cerr << "Nonzero length in this direction \n";
+ dimension++;
+ }
+ cell = cell % this->multipliers[i - 1];
+ }
+ return dimension;
+}
+
+template <typename T>
+inline T& Bitmap_cubical_complex_base<T>::get_cell_data(size_t cell) {
+ return this->data[cell];
+}
+
+template <typename T>
+void Bitmap_cubical_complex_base<T>::impose_lower_star_filtration() {
+ bool dbg = false;
+
+ // this vector will be used to check which elements have already been taken care of in imposing lower star filtration
+ std::vector<bool> is_this_cell_considered(this->data.size(), false);
+
+ size_t size_to_reserve = 1;
+ for (size_t i = 0; i != this->multipliers.size(); ++i) {
+ size_to_reserve *= (size_t) ((this->multipliers[i] - 1) / 2);
+ }
+
+ std::vector<size_t> indices_to_consider;
+ indices_to_consider.reserve(size_to_reserve);
+ // we assume here that we already have a filtration on the top dimensional cells and
+ // we have to extend it to lower ones.
+ typename Bitmap_cubical_complex_base<T>::Top_dimensional_cells_iterator it(*this);
+ for (it = this->top_dimensional_cells_iterator_begin(); it != this->top_dimensional_cells_iterator_end(); ++it) {
+ indices_to_consider.push_back(it.compute_index_in_bitmap());
+ }
+
+ while (indices_to_consider.size()) {
+ if (dbg) {
+ std::cerr << "indices_to_consider in this iteration \n";
+ for (size_t i = 0; i != indices_to_consider.size(); ++i) {
+ std::cout << indices_to_consider[i] << " ";
+ }
+ getchar();
+ }
+ std::vector<size_t> new_indices_to_consider;
+ for (size_t i = 0; i != indices_to_consider.size(); ++i) {
+ std::vector<size_t> bd = this->get_boundary_of_a_cell(indices_to_consider[i]);
+ for (size_t boundaryIt = 0; boundaryIt != bd.size(); ++boundaryIt) {
+ if (dbg) {
+ std::cerr << "filtration of a cell : " << bd[boundaryIt] << " is : " << this->data[ bd[boundaryIt] ]
+ << " while of a cell: " << indices_to_consider[i] << " is: " << this->data[ indices_to_consider[i] ]
+ << std::endl;
+ getchar();
+ }
+ if (this->data[ bd[boundaryIt] ] > this->data[ indices_to_consider[i] ]) {
+ this->data[ bd[boundaryIt] ] = this->data[ indices_to_consider[i] ];
+ if (dbg) {
+ std::cerr << "Setting the value of a cell : " << bd[boundaryIt] << " to : "
+ << this->data[ indices_to_consider[i] ] << std::endl;
+ getchar();
+ }
+ }
+ if (is_this_cell_considered[ bd[boundaryIt] ] == false) {
+ new_indices_to_consider.push_back(bd[boundaryIt]);
+ is_this_cell_considered[ bd[boundaryIt] ] = true;
+ }
+ }
+ }
+ indices_to_consider.swap(new_indices_to_consider);
+ }
+}
+
+template <typename T>
+bool compareFirstElementsOfTuples(const std::pair< std::pair< T, size_t >, char >& first,
+ const std::pair< std::pair< T, size_t >, char >& second) {
+ if (first.first.first < second.first.first) {
+ return true;
+ } else {
+ if (first.first.first > second.first.first) {
+ return false;
+ }
+ // in this case first.first.first == second.first.first, so we need to compare dimensions
+ return first.second < second.second;
+ }
+}
+
+} // namespace cubical_complex
+
+namespace Cubical_complex = cubical_complex;
+
+} // namespace Gudhi
+
+#endif // BITMAP_CUBICAL_COMPLEX_BASE_H_
diff --git a/include/gudhi/Bitmap_cubical_complex_periodic_boundary_conditions_base.h b/include/gudhi/Bitmap_cubical_complex_periodic_boundary_conditions_base.h
new file mode 100644
index 00000000..c3cc93dd
--- /dev/null
+++ b/include/gudhi/Bitmap_cubical_complex_periodic_boundary_conditions_base.h
@@ -0,0 +1,308 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Pawel Dlotko
+ *
+ * Copyright (C) 2015 INRIA Sophia-Saclay (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef BITMAP_CUBICAL_COMPLEX_PERIODIC_BOUNDARY_CONDITIONS_BASE_H_
+#define BITMAP_CUBICAL_COMPLEX_PERIODIC_BOUNDARY_CONDITIONS_BASE_H_
+
+#include <gudhi/Bitmap_cubical_complex_base.h>
+
+#include <cmath>
+#include <limits> // for numeric_limits<>
+#include <vector>
+
+namespace Gudhi {
+
+namespace cubical_complex {
+
+// in this class, we are storing all the elements which are in normal bitmap (i.e. the bitmap without the periodic
+// boundary conditions). But, we set up the iterators and the procedures to compute boundary and coboundary in the way
+// that it is all right. We assume here that all the cells that are on the left / bottom and so on remains, while all
+// the cells on the right / top are not in the Bitmap_cubical_complex_periodic_boundary_conditions_base
+
+/**
+ * @brief Cubical complex with periodic boundary conditions represented as a bitmap.
+ * @ingroup cubical_complex
+ * @details This is a class implementing a bitmap data structure with periodic boundary conditions. Most of the functions are
+ * identical to the functions from Bitmap_cubical_complex_base.
+ * The ones that needed to be updated are the constructors and get_boundary_of_a_cell and get_coboundary_of_a_cell.
+ */
+template <typename T>
+class Bitmap_cubical_complex_periodic_boundary_conditions_base : public Bitmap_cubical_complex_base<T> {
+ public:
+ // constructors that take an extra parameter:
+
+ /**
+ * Default constructor of Bitmap_cubical_complex_periodic_boundary_conditions_base class.
+ */
+ Bitmap_cubical_complex_periodic_boundary_conditions_base() { }
+ /**
+ * A constructor of Bitmap_cubical_complex_periodic_boundary_conditions_base class that takes the following
+ * parameters: (1) vector with numbers of top dimensional cells in all dimensions and (2) vector of booleans. If
+ * at i-th position of this vector there is true value, that means that periodic boundary conditions are to be
+ * imposed in this direction. In case of false, the periodic boundary conditions will not be imposed in the direction
+ * i.
+ */
+ Bitmap_cubical_complex_periodic_boundary_conditions_base(const std::vector<unsigned>& sizes,
+ const std::vector<bool>& directions_in_which_periodic_b_cond_are_to_be_imposed);
+ /**
+ * A constructor of Bitmap_cubical_complex_periodic_boundary_conditions_base class that takes the name of Perseus
+ * style file as an input. Please consult the documentation about the specification of the file.
+ */
+ Bitmap_cubical_complex_periodic_boundary_conditions_base(const char* perseusStyleFile);
+ /**
+ * A constructor of Bitmap_cubical_complex_periodic_boundary_conditions_base class that takes the following
+ * parameters: (1) vector with numbers of top dimensional cells in all dimensions and (2) vector of top dimensional
+ * cells (ordered lexicographically) and (3) vector of booleans. If at i-th position of this vector there is true
+ * value, that means that periodic boundary conditions are to be imposed in this direction. In case of false, the
+ * periodic boundary conditions will not be imposed in the direction i.
+ */
+ Bitmap_cubical_complex_periodic_boundary_conditions_base(const std::vector<unsigned>& dimensions,
+ const std::vector<T>& topDimensionalCells,
+ const std::vector< bool >& directions_in_which_periodic_b_cond_are_to_be_imposed);
+
+ /**
+ * Destructor of the Bitmap_cubical_complex_periodic_boundary_conditions_base class.
+ **/
+ virtual ~Bitmap_cubical_complex_periodic_boundary_conditions_base() {}
+
+ // overwritten methods co compute boundary and coboundary
+ /**
+ * A version of a function that return boundary of a given cell for an object of
+ * Bitmap_cubical_complex_periodic_boundary_conditions_base class.
+ */
+ virtual std::vector< size_t > get_boundary_of_a_cell(size_t cell) const;
+
+ /**
+ * A version of a function that return coboundary of a given cell for an object of
+ * Bitmap_cubical_complex_periodic_boundary_conditions_base class.
+ */
+ virtual std::vector< size_t > get_coboundary_of_a_cell(size_t cell) const;
+
+ protected:
+ std::vector< bool > directions_in_which_periodic_b_cond_are_to_be_imposed;
+
+ void set_up_containers(const std::vector<unsigned>& sizes) {
+ unsigned multiplier = 1;
+ for (size_t i = 0; i != sizes.size(); ++i) {
+ this->sizes.push_back(sizes[i]);
+ this->multipliers.push_back(multiplier);
+
+ if (directions_in_which_periodic_b_cond_are_to_be_imposed[i]) {
+ multiplier *= 2 * sizes[i];
+ } else {
+ multiplier *= 2 * sizes[i] + 1;
+ }
+ }
+ // std::reverse( this->sizes.begin() , this->sizes.end() );
+ this->data = std::vector<T>(multiplier, std::numeric_limits<T>::max());
+ this->total_number_of_cells = multiplier;
+ }
+ Bitmap_cubical_complex_periodic_boundary_conditions_base(const std::vector<unsigned>& sizes);
+ Bitmap_cubical_complex_periodic_boundary_conditions_base(const std::vector<unsigned>& dimensions,
+ const std::vector<T>& topDimensionalCells);
+ void construct_complex_based_on_top_dimensional_cells(const std::vector<unsigned>& dimensions,
+ const std::vector<T>& topDimensionalCells,
+ const std::vector<bool>& directions_in_which_periodic_b_cond_are_to_be_imposed);
+};
+
+template <typename T>
+void Bitmap_cubical_complex_periodic_boundary_conditions_base<T>::construct_complex_based_on_top_dimensional_cells(const std::vector<unsigned>& dimensions,
+ const std::vector<T>& topDimensionalCells,
+ const std::vector<bool>& directions_in_which_periodic_b_cond_are_to_be_imposed) {
+ this->directions_in_which_periodic_b_cond_are_to_be_imposed = directions_in_which_periodic_b_cond_are_to_be_imposed;
+ this->set_up_containers(dimensions);
+
+ size_t i = 0;
+ for (auto it = this->top_dimensional_cells_iterator_begin(); it != this->top_dimensional_cells_iterator_end(); ++it) {
+ this->get_cell_data(*it) = topDimensionalCells[i];
+ ++i;
+ }
+ this->impose_lower_star_filtration();
+}
+
+template <typename T>
+Bitmap_cubical_complex_periodic_boundary_conditions_base<T>::Bitmap_cubical_complex_periodic_boundary_conditions_base(const std::vector<unsigned>& sizes,
+ const std::vector<bool>& directions_in_which_periodic_b_cond_are_to_be_imposed) {
+ this->directions_in_which_periodic_b_cond_are_to_be_imposed(directions_in_which_periodic_b_cond_are_to_be_imposed);
+ this->set_up_containers(sizes);
+}
+
+template <typename T>
+Bitmap_cubical_complex_periodic_boundary_conditions_base<T>::Bitmap_cubical_complex_periodic_boundary_conditions_base(const char* perseus_style_file) {
+ // for Perseus style files:
+ bool dbg = false;
+
+ std::ifstream inFiltration;
+ inFiltration.open(perseus_style_file);
+ unsigned dimensionOfData;
+ inFiltration >> dimensionOfData;
+
+ this->directions_in_which_periodic_b_cond_are_to_be_imposed = std::vector<bool>(dimensionOfData, false);
+
+ std::vector<unsigned> sizes;
+ sizes.reserve(dimensionOfData);
+ for (size_t i = 0; i != dimensionOfData; ++i) {
+ int size_in_this_dimension;
+ inFiltration >> size_in_this_dimension;
+ if (size_in_this_dimension < 0) {
+ this->directions_in_which_periodic_b_cond_are_to_be_imposed[i] = true;
+ }
+ sizes.push_back(abs(size_in_this_dimension));
+ }
+ this->set_up_containers(sizes);
+
+ typename Bitmap_cubical_complex_periodic_boundary_conditions_base<T>::Top_dimensional_cells_iterator it(*this);
+ it = this->top_dimensional_cells_iterator_begin();
+
+ while (!inFiltration.eof()) {
+ double filtrationLevel;
+ inFiltration >> filtrationLevel;
+ if (inFiltration.eof())break;
+
+ if (dbg) {
+ std::cerr << "Cell of an index : "
+ << it.compute_index_in_bitmap()
+ << " and dimension: "
+ << this->get_dimension_of_a_cell(it.compute_index_in_bitmap())
+ << " get the value : " << filtrationLevel << std::endl;
+ }
+ this->get_cell_data(*it) = filtrationLevel;
+ ++it;
+ }
+ inFiltration.close();
+ this->impose_lower_star_filtration();
+}
+
+template <typename T>
+Bitmap_cubical_complex_periodic_boundary_conditions_base<T>::Bitmap_cubical_complex_periodic_boundary_conditions_base(const std::vector<unsigned>& sizes) {
+ this->directions_in_which_periodic_b_cond_are_to_be_imposed = std::vector<bool>(sizes.size(), false);
+ this->set_up_containers(sizes);
+}
+
+template <typename T>
+Bitmap_cubical_complex_periodic_boundary_conditions_base<T>::Bitmap_cubical_complex_periodic_boundary_conditions_base(const std::vector<unsigned>& dimensions,
+ const std::vector<T>& topDimensionalCells) {
+ std::vector<bool> directions_in_which_periodic_b_cond_are_to_be_imposed = std::vector<bool>(dimensions.size(), false);
+ this->construct_complex_based_on_top_dimensional_cells(dimensions, topDimensionalCells,
+ directions_in_which_periodic_b_cond_are_to_be_imposed);
+}
+
+template <typename T>
+Bitmap_cubical_complex_periodic_boundary_conditions_base<T>::
+Bitmap_cubical_complex_periodic_boundary_conditions_base(const std::vector<unsigned>& dimensions,
+ const std::vector<T>& topDimensionalCells,
+ const std::vector<bool>& directions_in_which_periodic_b_cond_are_to_be_imposed) {
+ this->construct_complex_based_on_top_dimensional_cells(dimensions, topDimensionalCells,
+ directions_in_which_periodic_b_cond_are_to_be_imposed);
+}
+
+// ***********************Methods************************ //
+
+template <typename T>
+std::vector< size_t > Bitmap_cubical_complex_periodic_boundary_conditions_base<T>::get_boundary_of_a_cell(size_t cell) const {
+ bool dbg = false;
+ if (dbg) {
+ std::cerr << "Computations of boundary of a cell : " << cell << std::endl;
+ }
+
+ std::vector< size_t > boundary_elements;
+ size_t cell1 = cell;
+ for (size_t i = this->multipliers.size(); i != 0; --i) {
+ unsigned position = cell1 / this->multipliers[i - 1];
+ // this cell have a nonzero length in this direction, therefore we can compute its boundary in this direction.
+
+ if (position % 2 == 1) {
+ // if there are no periodic boundary conditions in this direction, we do not have to do anything.
+ if (!directions_in_which_periodic_b_cond_are_to_be_imposed[i - 1]) {
+ // std::cerr << "A\n";
+ boundary_elements.push_back(cell - this->multipliers[ i - 1 ]);
+ boundary_elements.push_back(cell + this->multipliers[ i - 1 ]);
+ if (dbg) {
+ std::cerr << cell - this->multipliers[ i - 1 ] << " " << cell + this->multipliers[ i - 1 ] << " ";
+ }
+ } else {
+ // in this direction we have to do boundary conditions. Therefore, we need to check if we are not at the end.
+ if (position != 2 * this->sizes[ i - 1 ] - 1) {
+ // std::cerr << "B\n";
+ boundary_elements.push_back(cell - this->multipliers[ i - 1 ]);
+ boundary_elements.push_back(cell + this->multipliers[ i - 1 ]);
+ if (dbg) {
+ std::cerr << cell - this->multipliers[ i - 1 ] << " " << cell + this->multipliers[ i - 1 ] << " ";
+ }
+ } else {
+ // std::cerr << "C\n";
+ boundary_elements.push_back(cell - this->multipliers[ i - 1 ]);
+ boundary_elements.push_back(cell - (2 * this->sizes[ i - 1 ] - 1) * this->multipliers[ i - 1 ]);
+ if (dbg) {
+ std::cerr << cell - this->multipliers[ i - 1 ] << " " <<
+ cell - (2 * this->sizes[ i - 1 ] - 1) * this->multipliers[ i - 1 ] << " ";
+ }
+ }
+ }
+ }
+ cell1 = cell1 % this->multipliers[i - 1];
+ }
+ return boundary_elements;
+}
+
+template <typename T>
+std::vector< size_t > Bitmap_cubical_complex_periodic_boundary_conditions_base<T>::get_coboundary_of_a_cell(size_t cell) const {
+ std::vector<unsigned> counter = this->compute_counter_for_given_cell(cell);
+ std::vector< size_t > coboundary_elements;
+ size_t cell1 = cell;
+ for (size_t i = this->multipliers.size(); i != 0; --i) {
+ unsigned position = cell1 / this->multipliers[i - 1];
+ // if the cell has zero length in this direction, then it will have cbd in this direction.
+ if (position % 2 == 0) {
+ if (!this->directions_in_which_periodic_b_cond_are_to_be_imposed[i - 1]) {
+ // no periodic boundary conditions in this direction
+ if ((counter[i - 1] != 0) && (cell > this->multipliers[i - 1])) {
+ coboundary_elements.push_back(cell - this->multipliers[i - 1]);
+ }
+ if ((counter[i - 1] != 2 * this->sizes[i - 1]) && (cell + this->multipliers[i - 1] < this->data.size())) {
+ coboundary_elements.push_back(cell + this->multipliers[i - 1]);
+ }
+ } else {
+ // we want to have periodic boundary conditions in this direction
+ if (counter[i - 1] != 0) {
+ coboundary_elements.push_back(cell - this->multipliers[i - 1]);
+ coboundary_elements.push_back(cell + this->multipliers[i - 1]);
+ } else {
+ // in this case counter[i-1] == 0.
+ coboundary_elements.push_back(cell + this->multipliers[i - 1]);
+ coboundary_elements.push_back(cell + (2 * this->sizes[ i - 1 ] - 1) * this->multipliers[i - 1]);
+ }
+ }
+ }
+
+ cell1 = cell1 % this->multipliers[i - 1];
+ }
+ return coboundary_elements;
+}
+
+} // namespace cubical_complex
+
+namespace Cubical_complex = cubical_complex;
+
+} // namespace Gudhi
+
+#endif // BITMAP_CUBICAL_COMPLEX_PERIODIC_BOUNDARY_CONDITIONS_BASE_H_
diff --git a/include/gudhi/Clock.h b/include/gudhi/Clock.h
new file mode 100644
index 00000000..04c6ffb9
--- /dev/null
+++ b/include/gudhi/Clock.h
@@ -0,0 +1,79 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CLOCK_H_
+#define CLOCK_H_
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+#include <string>
+
+class Clock {
+ public:
+ Clock() : end_called(false) {
+ startTime = boost::posix_time::microsec_clock::local_time();
+ }
+
+ Clock(const std::string& msg_) {
+ end_called = false;
+ begin();
+ msg = msg_;
+ }
+
+ void begin() const {
+ end_called = false;
+ startTime = boost::posix_time::microsec_clock::local_time();
+ }
+
+ void end() const {
+ end_called = true;
+ endTime = boost::posix_time::microsec_clock::local_time();
+ }
+
+ void print() const {
+ std::cout << *this << std::endl;
+ }
+
+ friend std::ostream& operator<<(std::ostream& stream, const Clock& clock) {
+ if (!clock.end_called)
+ clock.end();
+
+ if (!clock.end_called) {
+ stream << "end not called";
+ } else {
+ stream << clock.msg << ":" << clock.num_seconds() << "s";
+ }
+ return stream;
+ }
+
+ double num_seconds() const {
+ if (!end_called) return -1;
+ return (endTime - startTime).total_milliseconds() / 1000.;
+ }
+
+ private:
+ mutable boost::posix_time::ptime startTime, endTime;
+ mutable bool end_called;
+ std::string msg;
+};
+
+#endif // CLOCK_H_
diff --git a/include/gudhi/Contraction/CGAL_queue/Modifiable_priority_queue.h b/include/gudhi/Contraction/CGAL_queue/Modifiable_priority_queue.h
new file mode 100644
index 00000000..5a55c513
--- /dev/null
+++ b/include/gudhi/Contraction/CGAL_queue/Modifiable_priority_queue.h
@@ -0,0 +1,101 @@
+// Copyright (c) 2006-2011 GeometryFactory (France). All rights reserved.
+//
+// This file is part of CGAL (www.cgal.org); you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation; either version 3 of the License,
+// or (at your option) any later version.
+//
+// Licensees holding a valid commercial license may use this file in
+// accordance with the commercial license agreement provided with the software.
+//
+// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+//
+// $URL$
+// $Id$
+//
+// Author(s) : Fernando Cacciola <fernando.cacciola@geometryfactory.com>
+//
+#ifndef CONTRACTION_CGAL_QUEUE_MODIFIABLE_PRIORITY_QUEUE_H_
+#define CONTRACTION_CGAL_QUEUE_MODIFIABLE_PRIORITY_QUEUE_H_
+
+#define CGAL_SURFACE_MESH_SIMPLIFICATION_USE_RELAXED_HEAP
+
+#include <boost/optional.hpp>
+#include <boost/pending/relaxed_heap.hpp>
+
+#include <climits> // Neeeded by the following Boost header for CHAR_BIT.
+#include <functional> // for less
+
+namespace CGAL {
+
+template <class IndexedType_, class Compare_ = std::less<IndexedType_>, class ID_ = boost::identity_property_map>
+class Modifiable_priority_queue {
+ public:
+ typedef Modifiable_priority_queue Self;
+
+ typedef IndexedType_ IndexedType;
+ typedef Compare_ Compare;
+ typedef ID_ ID;
+
+ typedef boost::relaxed_heap<IndexedType, Compare, ID> Heap;
+ typedef typename Heap::value_type value_type;
+ typedef typename Heap::size_type size_type;
+
+ typedef bool handle;
+
+ public:
+ Modifiable_priority_queue(size_type largest_ID, Compare const& c, ID const& id) : mHeap(largest_ID, c, id) { }
+
+ handle push(value_type const& v) {
+ mHeap.push(v);
+ return handle(true);
+ }
+
+ handle update(value_type const& v, handle h) {
+ mHeap.update(v);
+ return h;
+ }
+
+ handle erase(value_type const& v, handle) {
+ mHeap.remove(v);
+ return null_handle();
+ }
+
+ value_type top() const {
+ return mHeap.top();
+ }
+
+ void pop() {
+ mHeap.pop();
+ }
+
+ bool empty() const {
+ return mHeap.empty();
+ }
+
+ bool contains(value_type const& v) {
+ return mHeap.contains(v);
+ }
+
+ boost::optional<value_type> extract_top() {
+ boost::optional<value_type> r;
+ if (!empty()) {
+ value_type v = top();
+ pop();
+ r = boost::optional<value_type>(v);
+ }
+ return r;
+ }
+
+ static handle null_handle() {
+ return handle(false);
+ }
+
+ private:
+ Heap mHeap;
+};
+
+} // namespace CGAL
+
+#endif // CONTRACTION_CGAL_QUEUE_MODIFIABLE_PRIORITY_QUEUE_H_
diff --git a/include/gudhi/Contraction/Edge_profile.h b/include/gudhi/Contraction/Edge_profile.h
new file mode 100644
index 00000000..e4910b27
--- /dev/null
+++ b/include/gudhi/Contraction/Edge_profile.h
@@ -0,0 +1,130 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CONTRACTION_EDGE_PROFILE_H_
+#define CONTRACTION_EDGE_PROFILE_H_
+
+#include <ostream>
+
+namespace Gudhi {
+
+namespace contraction {
+
+template<typename GeometricSimplifiableComplex> class Edge_profile {
+ public:
+ typedef GeometricSimplifiableComplex Complex;
+ typedef typename Complex::GT GT;
+
+ typedef typename GeometricSimplifiableComplex::Vertex_handle Vertex_handle;
+ typedef typename GeometricSimplifiableComplex::Root_vertex_handle Root_vertex_handle;
+
+
+ typedef typename GeometricSimplifiableComplex::Edge_handle Edge_handle;
+ typedef typename GeometricSimplifiableComplex::Graph_vertex Graph_vertex;
+ typedef typename GeometricSimplifiableComplex::Graph_edge Graph_edge;
+ typedef typename GeometricSimplifiableComplex::Point Point;
+
+ Edge_profile(GeometricSimplifiableComplex& complex, Edge_handle edge) : complex_(complex), edge_handle_(edge),
+ v0_(complex_.first_vertex(edge_handle_)), v1_(complex_.second_vertex(edge_handle_)) {
+ assert(complex_.get_address(complex_[edge_handle_].first()));
+ assert(complex_.get_address(complex_[edge_handle_].second()));
+ assert(complex_.contains_edge(v0_handle(), v1_handle()));
+ assert(v0_handle() != v1_handle());
+ }
+
+ virtual ~Edge_profile() { }
+
+ GeometricSimplifiableComplex& complex() const {
+ return complex_;
+ }
+
+ Edge_handle edge_handle() const {
+ return edge_handle_;
+ }
+
+ Graph_edge& edge() const {
+ return complex_[edge_handle_];
+ }
+
+ Graph_vertex& v0() const {
+ return complex_[v0_handle()];
+ }
+
+ Graph_vertex& v1() const {
+ return complex_[v1_handle()];
+ }
+
+ Vertex_handle v0_handle() const {
+ return v0_;
+ // Root_vertex_handle root = complex_[edge_handle_].first();
+ // assert(complex_.get_address(root));
+ // return *complex_.get_address(root);
+ }
+
+ Vertex_handle v1_handle() const {
+ return v1_;
+ // Root_vertex_handle root = complex_[edge_handle_].second();
+ // assert(complex_.get_address(root));
+ // return *complex_.get_address(root);
+ }
+
+ const Point& p0() const {
+ return complex_.point(v0_handle());
+ }
+
+ const Point& p1() const {
+ return complex_.point(v1_handle());
+ }
+
+ friend std::ostream& operator<<(std::ostream& o, const Edge_profile& v) {
+ return o << "v0:" << v.v0_handle() << " v1:" << v.v1_handle();
+ }
+
+ private:
+ GeometricSimplifiableComplex& complex_;
+
+ Edge_handle edge_handle_;
+
+ Vertex_handle v0_;
+
+ Vertex_handle v1_;
+};
+
+template<typename EdgeProfile> class Edge_profile_factory {
+ public:
+ typedef typename EdgeProfile::Edge_handle Edge_handle_;
+ typedef typename EdgeProfile::Complex Complex_;
+
+ virtual EdgeProfile make_profile(
+ Complex_& complex,
+ Edge_handle_ edge) const {
+ return EdgeProfile(complex, edge);
+ }
+
+ virtual ~Edge_profile_factory() { }
+};
+
+} // namespace contraction
+
+} // namespace Gudhi
+
+#endif // CONTRACTION_EDGE_PROFILE_H_
diff --git a/include/gudhi/Contraction/policies/Contraction_visitor.h b/include/gudhi/Contraction/policies/Contraction_visitor.h
new file mode 100644
index 00000000..7ee05aad
--- /dev/null
+++ b/include/gudhi/Contraction/policies/Contraction_visitor.h
@@ -0,0 +1,91 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CONTRACTION_POLICIES_CONTRACTION_VISITOR_H_
+#define CONTRACTION_POLICIES_CONTRACTION_VISITOR_H_
+
+#include <gudhi/Contraction/Edge_profile.h>
+#include <boost/optional.hpp>
+
+namespace Gudhi {
+
+namespace contraction {
+
+/**
+ *@class Contraction_visitor
+ *@brief Interface for a visitor of the edge contraction process.
+ *@ingroup contr
+ */
+template <typename EdgeProfile>
+class Contraction_visitor { // : public Dummy_complex_visitor<typename EdgeProfile::Vertex_handle> {
+ public:
+ // typedef typename ComplexType::GeometryTrait GT;
+ typedef EdgeProfile Profile;
+ typedef double FT;
+ typedef typename Profile::Complex Complex;
+ typedef Complex ComplexType;
+ typedef typename ComplexType::Point Point;
+
+ virtual ~Contraction_visitor() { }
+
+ /**
+ * @brief Called before the edge contraction process starts.
+ */
+ virtual void on_started(ComplexType & complex) { }
+
+ /**
+ * @brief Called when the algorithm stops.
+ */
+ virtual void on_stop_condition_reached() { }
+
+ /**
+ * @brief Called during the collecting phase (when a cost is assigned to the edges), for each edge collected.
+ */
+ virtual void on_collected(const Profile &profile, boost::optional< FT > cost) { }
+
+ /**
+ * @brief Called during the processing phase (when edges are contracted), for each edge that is selected.
+ */
+ virtual void on_selected(const Profile &profile, boost::optional< FT > cost, int initial_count, int current_count) { }
+
+ /**
+ * @brief Called when an edge is about to be contracted and replaced by a vertex whose position is *placement.
+ */
+ virtual void on_contracting(const Profile &profile, boost::optional< Point > placement) { }
+
+ /**
+ * @brief Called when after an edge has been contracted onto a new point placement.
+ * A possibility would to remove popable blockers at this point for instance.
+ */
+ virtual void on_contracted(const Profile &profile, boost::optional< Point > placement) { }
+
+ /**
+ * @brief Called for each selected edge which cannot be contracted because the ValidContractionPredicate is false
+ */
+ virtual void on_non_valid(const Profile &profile) { }
+};
+
+} // namespace contraction
+
+} // namespace Gudhi
+
+#endif // CONTRACTION_POLICIES_CONTRACTION_VISITOR_H_
diff --git a/include/gudhi/Contraction/policies/Cost_policy.h b/include/gudhi/Contraction/policies/Cost_policy.h
new file mode 100644
index 00000000..f4d343ec
--- /dev/null
+++ b/include/gudhi/Contraction/policies/Cost_policy.h
@@ -0,0 +1,53 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CONTRACTION_POLICIES_COST_POLICY_H_
+#define CONTRACTION_POLICIES_COST_POLICY_H_
+
+#include <boost/optional.hpp>
+
+namespace Gudhi {
+
+namespace contraction {
+
+/**
+ *@brief Policy to specify the cost of contracting an edge.
+ *@ingroup contr
+ */
+template< typename EdgeProfile>
+class Cost_policy {
+ public:
+ typedef typename EdgeProfile::Point Point;
+ typedef typename EdgeProfile::Graph_vertex Graph_vertex;
+
+ typedef boost::optional<double> Cost_type;
+
+ virtual Cost_type operator()(const EdgeProfile& profile, const boost::optional<Point>& placement) const = 0;
+
+ virtual ~Cost_policy() { }
+};
+
+} // namespace contraction
+
+} // namespace Gudhi
+
+#endif // CONTRACTION_POLICIES_COST_POLICY_H_
diff --git a/include/gudhi/Contraction/policies/Dummy_valid_contraction.h b/include/gudhi/Contraction/policies/Dummy_valid_contraction.h
new file mode 100644
index 00000000..5d329496
--- /dev/null
+++ b/include/gudhi/Contraction/policies/Dummy_valid_contraction.h
@@ -0,0 +1,49 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CONTRACTION_POLICIES_DUMMY_VALID_CONTRACTION_H_
+#define CONTRACTION_POLICIES_DUMMY_VALID_CONTRACTION_H_
+
+#include <gudhi/Contraction/policies/Valid_contraction_policy.h>
+
+namespace Gudhi {
+
+namespace contraction {
+
+/**
+ *@brief Policy that accept all edge contraction.
+ */
+template< typename EdgeProfile>
+class Dummy_valid_contraction : public Valid_contraction_policy<EdgeProfile> {
+ public:
+ typedef typename EdgeProfile::Point Point;
+
+ bool operator()(const EdgeProfile& profile, const boost::optional<Point>& placement) {
+ return true;
+ }
+};
+
+} // namespace contraction
+
+} // namespace Gudhi
+
+#endif // CONTRACTION_POLICIES_DUMMY_VALID_CONTRACTION_H_
diff --git a/include/gudhi/Contraction/policies/Edge_length_cost.h b/include/gudhi/Contraction/policies/Edge_length_cost.h
new file mode 100644
index 00000000..dac2d448
--- /dev/null
+++ b/include/gudhi/Contraction/policies/Edge_length_cost.h
@@ -0,0 +1,56 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CONTRACTION_POLICIES_EDGE_LENGTH_COST_H_
+#define CONTRACTION_POLICIES_EDGE_LENGTH_COST_H_
+
+#include <gudhi/Contraction/policies/Cost_policy.h>
+
+namespace Gudhi {
+
+namespace contraction {
+
+/**
+ * @brief return a cost corresponding to the squared length of the edge
+ */
+template< typename EdgeProfile>
+class Edge_length_cost : public Cost_policy<EdgeProfile> {
+ public:
+ typedef typename Cost_policy<EdgeProfile>::Cost_type Cost_type;
+ typedef typename EdgeProfile::Point Point;
+
+ Cost_type operator()(const EdgeProfile& profile, const boost::optional<Point>& placement) const override {
+ double res = 0;
+ auto p0_coord = profile.p0().begin();
+ auto p1_coord = profile.p1().begin();
+ for (; p0_coord != profile.p0().end(); p0_coord++, p1_coord++) {
+ res += (*p0_coord - *p1_coord) * (*p0_coord - *p1_coord);
+ }
+ return res;
+ }
+};
+
+} // namespace contraction
+
+} // namespace Gudhi
+
+#endif // CONTRACTION_POLICIES_EDGE_LENGTH_COST_H_
diff --git a/include/gudhi/Contraction/policies/First_vertex_placement.h b/include/gudhi/Contraction/policies/First_vertex_placement.h
new file mode 100644
index 00000000..1f68db0d
--- /dev/null
+++ b/include/gudhi/Contraction/policies/First_vertex_placement.h
@@ -0,0 +1,52 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CONTRACTION_POLICIES_FIRST_VERTEX_PLACEMENT_H_
+#define CONTRACTION_POLICIES_FIRST_VERTEX_PLACEMENT_H_
+
+#include <gudhi/Contraction/policies/Placement_policy.h>
+
+namespace Gudhi {
+
+namespace contraction {
+
+/**
+ * @brief Places the contracted point onto the first point of the edge
+ */
+template< typename EdgeProfile>
+class First_vertex_placement : public Placement_policy<EdgeProfile> {
+ public:
+ typedef typename EdgeProfile::Point Point;
+ typedef typename EdgeProfile::Edge_handle Edge_handle;
+
+ typedef typename Placement_policy<EdgeProfile>::Placement_type Placement_type;
+
+ Placement_type operator()(const EdgeProfile& profile) const override {
+ return Placement_type(profile.p0());
+ }
+};
+
+} // namespace contraction
+
+} // namespace Gudhi
+
+#endif // CONTRACTION_POLICIES_FIRST_VERTEX_PLACEMENT_H_
diff --git a/include/gudhi/Contraction/policies/Link_condition_valid_contraction.h b/include/gudhi/Contraction/policies/Link_condition_valid_contraction.h
new file mode 100644
index 00000000..250bba27
--- /dev/null
+++ b/include/gudhi/Contraction/policies/Link_condition_valid_contraction.h
@@ -0,0 +1,56 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CONTRACTION_POLICIES_LINK_CONDITION_VALID_CONTRACTION_H_
+#define CONTRACTION_POLICIES_LINK_CONDITION_VALID_CONTRACTION_H_
+
+#include <gudhi/Contraction/policies/Valid_contraction_policy.h>
+#include <gudhi/Debug_utils.h>
+
+
+namespace Gudhi {
+
+namespace contraction {
+
+/**
+ *@brief Policy that only accept edges verifying the link condition (and therefore whose contraction preserving homotopy type).
+ *@ingroup contr
+ */
+template< typename EdgeProfile>
+class Link_condition_valid_contraction : public Valid_contraction_policy<EdgeProfile> {
+ public:
+ typedef typename EdgeProfile::Edge_handle Edge_handle;
+ typedef typename EdgeProfile::Point Point;
+ // typedef typename EdgeProfile::Edge_handle Edge_handle;
+
+ bool operator()(const EdgeProfile& profile, const boost::optional<Point>& placement) const override {
+ Edge_handle edge(profile.edge_handle());
+ DBGMSG("Link_condition_valid_contraction:", profile.complex().link_condition(edge));
+ return profile.complex().link_condition(edge);
+ }
+};
+
+} // namespace contraction
+
+} // namespace Gudhi
+
+#endif // CONTRACTION_POLICIES_LINK_CONDITION_VALID_CONTRACTION_H_
diff --git a/include/gudhi/Contraction/policies/Middle_placement.h b/include/gudhi/Contraction/policies/Middle_placement.h
new file mode 100644
index 00000000..4b59f1b5
--- /dev/null
+++ b/include/gudhi/Contraction/policies/Middle_placement.h
@@ -0,0 +1,51 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CONTRACTION_POLICIES_MIDDLE_PLACEMENT_H_
+#define CONTRACTION_POLICIES_MIDDLE_PLACEMENT_H_
+
+#include <gudhi/Contraction/policies/Placement_policy.h>
+
+namespace Gudhi {
+
+namespace contraction {
+
+template< typename EdgeProfile>
+class Middle_placement : public Placement_policy<EdgeProfile> {
+ public:
+ typedef typename EdgeProfile::Point Point;
+ typedef typename EdgeProfile::Edge_handle Edge_handle;
+ typedef typename EdgeProfile::Graph_vertex Graph_vertex;
+
+ typedef typename Placement_policy<EdgeProfile>::Placement_type Placement_type;
+
+ Placement_type operator()(const EdgeProfile& profile) const override {
+ // todo compute the middle
+ return Placement_type(profile.p0());
+ }
+};
+
+} // namespace contraction
+
+} // namespace Gudhi
+
+#endif // CONTRACTION_POLICIES_MIDDLE_PLACEMENT_H_
diff --git a/include/gudhi/Contraction/policies/Placement_policy.h b/include/gudhi/Contraction/policies/Placement_policy.h
new file mode 100644
index 00000000..34ffa49f
--- /dev/null
+++ b/include/gudhi/Contraction/policies/Placement_policy.h
@@ -0,0 +1,51 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CONTRACTION_POLICIES_PLACEMENT_POLICY_H_
+#define CONTRACTION_POLICIES_PLACEMENT_POLICY_H_
+
+#include <boost/optional.hpp>
+
+namespace Gudhi {
+
+namespace contraction {
+
+/**
+ *@brief Policy to specify where the merged point had to be placed after an edge contraction.
+ *@ingroup contr
+ */
+template< typename EdgeProfile>
+class Placement_policy {
+ public:
+ typedef typename EdgeProfile::Point Point;
+ typedef boost::optional<Point> Placement_type;
+
+ virtual Placement_type operator()(const EdgeProfile& profile) const = 0;
+
+ virtual ~Placement_policy() { }
+};
+
+} // namespace contraction
+
+} // namespace Gudhi
+
+#endif // CONTRACTION_POLICIES_PLACEMENT_POLICY_H_
diff --git a/include/gudhi/Contraction/policies/Valid_contraction_policy.h b/include/gudhi/Contraction/policies/Valid_contraction_policy.h
new file mode 100644
index 00000000..78d61173
--- /dev/null
+++ b/include/gudhi/Contraction/policies/Valid_contraction_policy.h
@@ -0,0 +1,51 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CONTRACTION_POLICIES_VALID_CONTRACTION_POLICY_H_
+#define CONTRACTION_POLICIES_VALID_CONTRACTION_POLICY_H_
+
+namespace Gudhi {
+
+namespace contraction {
+
+/**
+ *@brief Policy to specify if an edge contraction is valid or not.
+ *@ingroup contr
+ */
+template< typename EdgeProfile>
+class Valid_contraction_policy {
+ public:
+ typedef typename EdgeProfile::Point Point;
+ typedef typename EdgeProfile::Edge_handle Edge_handle;
+ typedef typename EdgeProfile::Graph_vertex Graph_vertex;
+
+ virtual bool operator()(const EdgeProfile& profile, const boost::optional<Point>& placement) const = 0;
+
+ virtual ~Valid_contraction_policy() { }
+};
+
+} // namespace contraction
+
+} // namespace Gudhi
+
+
+#endif // CONTRACTION_POLICIES_VALID_CONTRACTION_POLICY_H_
diff --git a/include/gudhi/Debug_utils.h b/include/gudhi/Debug_utils.h
new file mode 100644
index 00000000..7573a9db
--- /dev/null
+++ b/include/gudhi/Debug_utils.h
@@ -0,0 +1,55 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef DEBUG_UTILS_H_
+#define DEBUG_UTILS_H_
+
+#include <iostream>
+
+#ifndef NDEBUG
+ // GUDHI_DEBUG is the Gudhi official flag for debug mode.
+ #define GUDHI_DEBUG
+#endif
+
+// GUDHI_CHECK throw an exception if expression is false in debug mode, but does nothing in release mode
+// Could assert in release mode, but cmake sets NDEBUG (for "NO DEBUG") in this mode, means assert does nothing.
+#ifdef GUDHI_DEBUG
+ #define GUDHI_CHECK(expression, excpt) if ((expression) == 0) throw excpt
+#else
+ #define GUDHI_CHECK(expression, excpt) (void) 0
+#endif
+
+#define PRINT(a) std::cerr << #a << ": " << (a) << " (DISP)" << std::endl
+
+// #define DBG_VERBOSE
+#ifdef DBG_VERBOSE
+ #define DBG(a) std::cout << "DBG: " << (a) << std::endl
+ #define DBGMSG(a, b) std::cout << "DBG: " << a << b << std::endl
+ #define DBGVALUE(a) std::cout << "DBG: " << #a << ": " << a << std::endl
+ #define DBGCONT(a) std::cout << "DBG: container " << #a << " -> "; for (auto x : a) std::cout << x << ","; std::cout << std::endl
+#else
+ #define DBG(a) (void) 0
+ #define DBGMSG(a, b) (void) 0
+ #define DBGVALUE(a) (void) 0
+ #define DBGCONT(a) (void) 0
+#endif
+
+#endif // DEBUG_UTILS_H_
diff --git a/include/gudhi/Edge_contraction.h b/include/gudhi/Edge_contraction.h
new file mode 100644
index 00000000..5af13c3e
--- /dev/null
+++ b/include/gudhi/Edge_contraction.h
@@ -0,0 +1,235 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EDGE_CONTRACTION_H_
+#define EDGE_CONTRACTION_H_
+
+
+#include <gudhi/Skeleton_blocker_contractor.h>
+#include <gudhi/Contraction/policies/Edge_length_cost.h>
+#include <gudhi/Contraction/policies/First_vertex_placement.h>
+#include <gudhi/Contraction/policies/Valid_contraction_policy.h>
+#include <gudhi/Contraction/policies/Dummy_valid_contraction.h>
+#include <gudhi/Contraction/policies/Link_condition_valid_contraction.h>
+#include <gudhi/Debug_utils.h>
+
+namespace Gudhi {
+
+namespace contraction {
+
+
+/** \defgroup contr Edge contraction
+
+\author David Salinas
+
+\section Introduction
+
+The purpose of this package is to offer a user-friendly interface for edge contraction simplification of huge simplicial complexes.
+It uses the \ref skbl data-structure whose size remains small during simplification
+of most used geometrical complexes of topological data analysis such as the Rips or the Delaunay complexes. In practice, the
+size of this data-structure is even much lower than the total number of simplices.
+
+The edge contraction operation consists in identifying two vertices of a simplicial complex.
+A lot of algorithms have been developed in computer graphics that allows to reduce efficiently the size of 2-dimensional simplicial complexes
+ while preserving its geometry (for instance see \cite Garland \cite Lindstrom).
+These approaches can be extended to higher-dimensional simplicial complexes.
+The main advantage of using the Skeleton-Blocker data structure for edge contraction is that when the number of blockers is small,
+the operations needed for edge contraction algorithms have polynomial complexity regarding the size the graph.
+Therefore, the simplification can be done without enumerating the set of simplices that is often non tracktable in high-dimension and is then very efficient
+(sub-linear with regards to the number of simplices in practice).
+
+A typical application of this package is homology group computation. It is illustrated in the next figure where a Rips complex is built uppon a set of high-dimensional points and
+simplified with edge contractions.
+It has initially a big number of simplices (around 20 millions) but simplifying it to a much reduced form with only 15 vertices (and 714 simplices) takes only few seconds on a desktop machine (see the example bellow).
+One can then compute homology group with a simplicial complex having very few simplices instead of running the homology algorithm on the much bigger initial set of
+simplices which would take much more time and memory.
+
+
+\image html "so3.png" "Edge contraction illustration"
+
+\section Design
+
+This class design is policy based and heavily inspired from the similar edge collapse package of CGAL http://doc.cgal.org/latest/Surface_mesh_simplification/index.html (which is however restricted to 2D triangulations).
+
+
+\subsection Policies
+
+Four policies can be customized in this package:
+
+\li Cost_policy: specify how much cost an edge contraction of a given edge. The edge with lowest cost is iteratively picked and contracted if valid.
+\li Valid_contraction_policy: specify if a given edge contraction is valid. For instance, this policy can check the link condition which ensures that the homotopy type is preserved afer the edge contraction.
+\li Placement_policy: every time an edge is contracted, its points are merge to one point specified by this policy. This may be the middle of the edge of some more sophisticated point such as the minimum of a cost as in
+\cite Garland.
+
+
+\subsection Visitor
+
+A visitor which implements the class Contraction_visitor gets called at several key moments
+during the simplification:
+
+\li when the algorithms starts or ends,
+\li when an edge is seen for the first time,
+\li when an edge is considered for an edge contraction,
+\li before and after an edge is contracted.
+
+This allows to implements most of edge contraction based algorithm with this
+package without having to change the main simplification source code.
+
+
+\section Performance
+
+The next figure shows the computation time to reduce a random 2-sphere to a single tetrahedron with
+this package and with the CGAL equivalent package.
+Despite this package is able to deal with \a arbitrary simplicial complexes (any dimensions, manifold or non manifold),
+it is still \a 65% times faster than the CGAL package which is focused on 2-manifold.
+The main reason is that few blockers appears during the simplification and hence,
+the algorithm only have to deal with the graph and not higher-dimensional simplices
+(in this case triangles). However, we recall that higher-dimensional simplices are \a implicitely
+stored in the \ref skbl data-structure. Hence, one has to store
+simplices in an external map if some information needs to be associated with them (information that could be a filtration value or
+an orientation for instance).
+
+
+\image html "sphere_contraction.png" "Time in seconds to simplify random 2-spheres to a tetrahedron" width=10cm
+
+\section Example
+
+
+This example loads points from an OFF file and builds the Rips complex with an user provided parameter. It then simplifies the built Rips complex
+while ensuring its homotopy type is preserved during the contraction (edge are contracted only when the link condition is valid).
+
+ \code{.cpp}
+#include <boost/timer/timer.hpp>
+#include <iostream>
+#include <gudhi/Edge_contraction.h>
+#include <gudhi/Skeleton_blocker.h>
+#include <gudhi/Off_reader.h>
+
+
+using namespace std;
+using namespace Gudhi;
+using namespace skeleton_blocker;
+using namespace contraction;
+
+struct Geometry_trait{
+ typedef std::vector<double> Point;
+};
+
+typedef Geometry_trait::Point Point;
+typedef Skeleton_blocker_simple_geometric_traits<Geometry_trait> Complex_geometric_traits;
+typedef Skeleton_blocker_geometric_complex< Complex_geometric_traits > Complex;
+typedef Edge_profile<Complex> Profile;
+typedef Skeleton_blocker_contractor<Complex> Complex_contractor;
+
+template<typename Point>
+double eucl_distance(const Point& a,const Point& b){
+ double res = 0;
+ auto a_coord = a.begin();
+ auto b_coord = b.begin();
+ for(; a_coord != a.end(); a_coord++, b_coord++){
+ res += (*a_coord - *b_coord) * (*a_coord - *b_coord);
+ }
+ return sqrt(res);
+}
+
+template<typename ComplexType>
+void build_rips(ComplexType& complex, double offset){
+ if (offset<=0) return;
+ auto vertices = complex.vertex_range();
+ for (auto p = vertices.begin(); p != vertices.end(); ++p)
+ for (auto q = p; ++q != vertices.end(); )
+ if (eucl_distance(complex.point(*p), complex.point(*q)) < 2 * offset)
+ complex.add_edge_without_blockers(*p,*q);
+}
+
+int main (int argc, char *argv[])
+{
+ if (argc!=3){
+ std::cerr << "Usage "<<argv[0]<<" ../../data/SO3_10000.off 0.3 to load the file ../../data/SO3_10000.off and contract the Rips complex built with paremeter 0.3.\n";
+ return -1;
+ }
+
+ Complex complex;
+
+ // load the points
+ Skeleton_blocker_off_reader<Complex> off_reader(argv[1],complex,true);
+ if(!off_reader.is_valid()){
+ std::cerr << "Unable to read file:"<<argv[1]<<std::endl;
+ return EXIT_FAILURE;
+ }
+ std::cout << "Build the Rips complex"<<std::endl;
+
+ build_rips(complex,atof(argv[2]));
+
+ boost::timer::auto_cpu_timer t;
+
+ std::cout << "Initial complex has "<<
+ complex.num_vertices()<<" vertices and "<<
+ complex.num_edges()<<" edges"<<std::endl;
+
+ Complex_contractor contractor(complex,
+ new Edge_length_cost<Profile>,
+ contraction::make_first_vertex_placement<Profile>(),
+ contraction::make_link_valid_contraction<Profile>(),
+ contraction::make_remove_popable_blockers_visitor<Profile>());
+ contractor.contract_edges();
+
+ std::cout << "Counting final number of simplices \n";
+ unsigned num_simplices = std::distance(complex.star_simplex_range().begin(),complex.star_simplex_range().end());
+
+ std::cout << "Final complex has "<<
+ complex.num_vertices()<<" vertices, "<<
+ complex.num_edges()<<" edges, "<<
+ complex.num_blockers()<<" blockers and "<<
+ num_simplices<<" simplices"<<std::endl;
+
+
+ std::cout << "Time to simplify and enumerate simplices:\n";
+
+ return EXIT_SUCCESS;
+}
+}
+ \endcode
+
+
+\verbatim
+./example/Contraction/RipsContraction ../../data/SO3_10000.off 0.3
+[ 50%] [100%] Built target SkeletonBlockerIteration
+Built target RipsContraction
+Build the Rips complex
+Initial complex has 10000 vertices and 195664 edges
+Counting final number of simplices
+Final complex has 15 vertices, 101 edges, 165 blockers and 714 simplices
+Time to simplify and enumerate simplices:
+ 3.166621s wall, 3.150000s user + 0.010000s system = 3.160000s CPU (99.8%)
+\endverbatim
+
+
+
+\copyright GNU General Public License v3.
+*/
+/** @} */ // end defgroup
+} // namespace contraction
+
+} // namespace Gudhi
+
+#endif // EDGE_CONTRACTION_H_
diff --git a/include/gudhi/Hasse_complex.h b/include/gudhi/Hasse_complex.h
new file mode 100644
index 00000000..8b06b771
--- /dev/null
+++ b/include/gudhi/Hasse_complex.h
@@ -0,0 +1,255 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Clément Maria
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Méditerranée (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HASSE_COMPLEX_H_
+#define HASSE_COMPLEX_H_
+
+#include <gudhi/allocator.h>
+
+#include <boost/iterator/counting_iterator.hpp>
+
+#include <algorithm>
+#include <utility> // for pair
+#include <vector>
+
+#ifdef GUDHI_USE_TBB
+#include <tbb/parallel_for.h>
+#endif
+
+namespace Gudhi {
+
+template < class HasseCpx >
+struct Hasse_simplex {
+ // Complex_ds must verify that cpx->key(sh) is the order of sh in the filtration
+
+ template< class Complex_ds >
+ Hasse_simplex(Complex_ds & cpx
+ , typename Complex_ds::Simplex_handle sh)
+ : filtration_(cpx.filtration(sh))
+ , boundary_() {
+ boundary_.reserve(cpx.dimension(sh) + 1);
+ for (auto b_sh : cpx.boundary_simplex_range(sh)) {
+ boundary_.push_back(cpx.key(b_sh));
+ }
+ }
+
+ Hasse_simplex(typename HasseCpx::Simplex_key key
+ , typename HasseCpx::Filtration_value fil
+ , std::vector<typename HasseCpx::Simplex_handle> const& boundary)
+ : key_(key)
+ , filtration_(fil)
+ , boundary_(boundary) { }
+
+ typename HasseCpx::Simplex_key key_;
+ typename HasseCpx::Filtration_value filtration_;
+ std::vector<typename HasseCpx::Simplex_handle> boundary_;
+};
+
+/** \private
+ * \brief Data structure representing a Hasse diagram, i.e.
+ * a complex where all codimension 1 incidence
+ * relations are explicitly encoded.
+ *
+ * \implements FilteredComplex
+ * \ingroup simplex_tree
+ */
+template < typename FiltrationValue = double
+, typename SimplexKey = int
+, typename VertexHandle = int
+>
+class Hasse_complex {
+ public:
+ typedef Hasse_simplex<Hasse_complex> Hasse_simp;
+ typedef FiltrationValue Filtration_value;
+ typedef SimplexKey Simplex_key;
+ typedef int Simplex_handle; // index in vector complex_
+
+ typedef boost::counting_iterator< Simplex_handle > Filtration_simplex_iterator;
+ typedef boost::iterator_range<Filtration_simplex_iterator> Filtration_simplex_range;
+
+ typedef typename std::vector< Simplex_handle >::iterator Boundary_simplex_iterator;
+ typedef boost::iterator_range<Boundary_simplex_iterator> Boundary_simplex_range;
+
+ typedef typename std::vector< Simplex_handle >::iterator Skeleton_simplex_iterator;
+ typedef boost::iterator_range< Skeleton_simplex_iterator > Skeleton_simplex_range;
+
+ /* only dimension 0 skeleton_simplex_range(...) */
+ Skeleton_simplex_range skeleton_simplex_range(int dim = 0) {
+ if (dim != 0) {
+ std::cerr << "Dimension must be 0 \n";
+ }
+ return Skeleton_simplex_range(vertices_.begin(), vertices_.end());
+ }
+
+ template < class Complex_ds >
+ Hasse_complex(Complex_ds & cpx)
+ : complex_(cpx.num_simplices())
+ , vertices_()
+ , threshold_(cpx.filtration())
+ , num_vertices_()
+ , dim_max_(cpx.dimension()) {
+ int size = complex_.size();
+#ifdef GUDHI_USE_TBB
+ tbb::parallel_for(0, size, [&](int idx){new (&complex_[idx]) Hasse_simp(cpx, cpx.simplex(idx));});
+ for (int idx=0; idx < size; ++idx)
+ if (complex_[idx].boundary_.empty())
+ vertices_.push_back(idx);
+#else
+ for (int idx=0; idx < size; ++idx) {
+ new (&complex_[idx]) Hasse_simp(cpx, cpx.simplex(idx));
+ if (complex_[idx].boundary_.empty())
+ vertices_.push_back(idx);
+ }
+#endif
+ }
+
+ Hasse_complex()
+ : complex_()
+ , vertices_()
+ , threshold_(0)
+ , num_vertices_(0)
+ , dim_max_(-1) { }
+
+ size_t num_simplices() {
+ return complex_.size();
+ }
+
+ Filtration_simplex_range filtration_simplex_range() {
+ return Filtration_simplex_range(Filtration_simplex_iterator(0)
+ , Filtration_simplex_iterator(complex_.size()));
+ }
+
+ Simplex_key key(Simplex_handle sh) {
+ return complex_[sh].key_;
+ }
+
+ Simplex_key null_key() {
+ return -1;
+ }
+
+ Simplex_handle simplex(Simplex_key key) {
+ if (key == null_key()) return null_simplex();
+ return key;
+ }
+
+ Simplex_handle null_simplex() {
+ return -1;
+ }
+
+ Filtration_value filtration(Simplex_handle sh) {
+ if (sh == null_simplex()) {
+ return filtration();
+ }
+ return complex_[sh].filtration_;
+ }
+
+ Filtration_value filtration() {
+ return threshold_;
+ }
+
+ int dimension(Simplex_handle sh) {
+ if (complex_[sh].boundary_.empty()) return 0;
+ return complex_[sh].boundary_.size() - 1;
+ }
+
+ int dimension() {
+ return dim_max_;
+ }
+
+ std::pair<Simplex_handle, Simplex_handle> endpoints(Simplex_handle sh) {
+ return std::pair<Simplex_handle, Simplex_handle>(complex_[sh].boundary_[0]
+ , complex_[sh].boundary_[1]);
+ }
+
+ void assign_key(Simplex_handle sh, Simplex_key key) {
+ complex_[sh].key_ = key;
+ }
+
+ Boundary_simplex_range boundary_simplex_range(Simplex_handle sh) {
+ return Boundary_simplex_range(complex_[sh].boundary_.begin()
+ , complex_[sh].boundary_.end());
+ }
+
+ void display_simplex(Simplex_handle sh) {
+ std::cout << dimension(sh) << " ";
+ for (auto sh_b : boundary_simplex_range(sh)) std::cout << sh_b << " ";
+ std::cout << " " << filtration(sh) << " key=" << key(sh);
+ }
+
+ void initialize_filtration() {
+ // Setting the keys is done by pcoh, Simplex_tree doesn't do it either.
+#if 0
+ Simplex_key key = 0;
+ for (auto & h_simp : complex_)
+ h_simp.key_ = key++;
+#endif
+ }
+
+ std::vector< Hasse_simp, Gudhi::no_init_allocator<Hasse_simp> > complex_;
+ std::vector<Simplex_handle> vertices_;
+ Filtration_value threshold_;
+ size_t num_vertices_;
+ int dim_max_;
+};
+
+template< typename T1, typename T2, typename T3 >
+std::istream& operator>>(std::istream & is
+ , Hasse_complex< T1, T2, T3 > & hcpx) {
+ assert(hcpx.num_simplices() == 0);
+
+ size_t num_simp;
+ is >> num_simp;
+ hcpx.complex_.reserve(num_simp);
+
+ std::vector< typename Hasse_complex<T1, T2, T3>::Simplex_key > boundary;
+ typename Hasse_complex<T1, T2, T3>::Filtration_value fil;
+ typename Hasse_complex<T1, T2, T3>::Filtration_value max_fil = 0;
+ int max_dim = -1;
+ int key = 0;
+ // read all simplices in the file as a list of vertices
+ while (read_hasse_simplex(is, boundary, fil)) {
+ // insert every simplex in the simplex tree
+ hcpx.complex_.emplace_back(key, fil, boundary);
+
+ if (max_dim < hcpx.dimension(key)) {
+ max_dim = hcpx.dimension(key);
+ }
+ if (hcpx.dimension(key) == 0) {
+ hcpx.vertices_.push_back(key);
+ }
+ if (max_fil < fil) {
+ max_fil = fil;
+ }
+
+ ++key;
+ boundary.clear();
+ }
+
+ hcpx.dim_max_ = max_dim;
+ hcpx.threshold_ = max_fil;
+
+ return is;
+}
+
+} // namespace Gudhi
+
+#endif // HASSE_COMPLEX_H_
diff --git a/include/gudhi/Landmark_choice_by_furthest_point.h b/include/gudhi/Landmark_choice_by_furthest_point.h
new file mode 100644
index 00000000..df93155b
--- /dev/null
+++ b/include/gudhi/Landmark_choice_by_furthest_point.h
@@ -0,0 +1,105 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Siargey Kachanovich
+ *
+ * Copyright (C) 2015 INRIA Sophia Antipolis-Méditerranée (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LANDMARK_CHOICE_BY_FURTHEST_POINT_H_
+#define LANDMARK_CHOICE_BY_FURTHEST_POINT_H_
+
+#include <boost/range/size.hpp>
+
+#include <limits> // for numeric_limits<>
+#include <iterator>
+#include <algorithm> // for sort
+#include <vector>
+
+namespace Gudhi {
+
+namespace witness_complex {
+
+ typedef std::vector<int> typeVectorVertex;
+
+ /**
+ * \ingroup witness_complex
+ * \brief Landmark choice strategy by iteratively adding the furthest witness from the
+ * current landmark set as the new landmark.
+ * \details It chooses nbL landmarks from a random access range `points` and
+ * writes {witness}*{closest landmarks} matrix in `knn`.
+ *
+ * The type KNearestNeighbors can be seen as
+ * Witness_range<Closest_landmark_range<Vertex_handle>>, where
+ * Witness_range and Closest_landmark_range are random access ranges
+ *
+ */
+
+ template <typename KNearestNeighbours,
+ typename Point_random_access_range>
+ void landmark_choice_by_furthest_point(Point_random_access_range const &points,
+ int nbL,
+ KNearestNeighbours &knn) {
+ int nb_points = boost::size(points);
+ assert(nb_points >= nbL);
+ // distance matrix witness x landmarks
+ std::vector<std::vector<double>> wit_land_dist(nb_points, std::vector<double>());
+ // landmark list
+ typeVectorVertex chosen_landmarks;
+
+ knn = KNearestNeighbours(nb_points, std::vector<int>());
+ int current_number_of_landmarks = 0; // counter for landmarks
+ double curr_max_dist = 0; // used for defining the furhest point from L
+ const double infty = std::numeric_limits<double>::infinity(); // infinity (see next entry)
+ std::vector< double > dist_to_L(nb_points, infty); // vector of current distances to L from points
+
+ // TODO(SK) Consider using rand_r(...) instead of rand(...) for improved thread safety
+ // or better yet std::uniform_int_distribution
+ int rand_int = rand() % nb_points;
+ int curr_max_w = rand_int; // For testing purposes a pseudo-random number is used here
+
+ for (current_number_of_landmarks = 0; current_number_of_landmarks != nbL; current_number_of_landmarks++) {
+ // curr_max_w at this point is the next landmark
+ chosen_landmarks.push_back(curr_max_w);
+ unsigned i = 0;
+ for (auto& p : points) {
+ double curr_dist = euclidean_distance(p, *(std::begin(points) + chosen_landmarks[current_number_of_landmarks]));
+ wit_land_dist[i].push_back(curr_dist);
+ knn[i].push_back(current_number_of_landmarks);
+ if (curr_dist < dist_to_L[i])
+ dist_to_L[i] = curr_dist;
+ ++i;
+ }
+ curr_max_dist = 0;
+ for (i = 0; i < dist_to_L.size(); i++)
+ if (dist_to_L[i] > curr_max_dist) {
+ curr_max_dist = dist_to_L[i];
+ curr_max_w = i;
+ }
+ }
+ for (int i = 0; i < nb_points; ++i)
+ std::sort(std::begin(knn[i]),
+ std::end(knn[i]),
+ [&wit_land_dist, i](int a, int b) {
+ return wit_land_dist[i][a] < wit_land_dist[i][b]; });
+ }
+
+} // namespace witness_complex
+
+} // namespace Gudhi
+
+#endif // LANDMARK_CHOICE_BY_FURTHEST_POINT_H_
diff --git a/include/gudhi/Landmark_choice_by_random_point.h b/include/gudhi/Landmark_choice_by_random_point.h
new file mode 100644
index 00000000..ebf6aad1
--- /dev/null
+++ b/include/gudhi/Landmark_choice_by_random_point.h
@@ -0,0 +1,96 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Siargey Kachanovich
+ *
+ * Copyright (C) 2015 INRIA Sophia Antipolis-Méditerranée (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LANDMARK_CHOICE_BY_RANDOM_POINT_H_
+#define LANDMARK_CHOICE_BY_RANDOM_POINT_H_
+
+#include <boost/range/size.hpp>
+
+#include <queue> // for priority_queue<>
+#include <utility> // for pair<>
+#include <iterator>
+#include <vector>
+#include <set>
+
+namespace Gudhi {
+
+namespace witness_complex {
+
+ /**
+ * \ingroup witness_complex
+ * \brief Landmark choice strategy by taking random vertices for landmarks.
+ * \details It chooses nbL distinct landmarks from a random access range `points`
+ * and outputs a matrix {witness}*{closest landmarks} in knn.
+ *
+ * The type KNearestNeighbors can be seen as
+ * Witness_range<Closest_landmark_range<Vertex_handle>>, where
+ * Witness_range and Closest_landmark_range are random access ranges and
+ * Vertex_handle is the label type of a vertex in a simplicial complex.
+ * Closest_landmark_range needs to have push_back operation.
+ */
+
+ template <typename KNearestNeighbours,
+ typename Point_random_access_range>
+ void landmark_choice_by_random_point(Point_random_access_range const &points,
+ int nbL,
+ KNearestNeighbours &knn) {
+ int nbP = boost::size(points);
+ assert(nbP >= nbL);
+ std::set<int> landmarks;
+ int current_number_of_landmarks = 0; // counter for landmarks
+
+ // TODO(SK) Consider using rand_r(...) instead of rand(...) for improved thread safety
+ int chosen_landmark = rand() % nbP;
+ for (current_number_of_landmarks = 0; current_number_of_landmarks != nbL; current_number_of_landmarks++) {
+ while (landmarks.find(chosen_landmark) != landmarks.end())
+ chosen_landmark = rand() % nbP;
+ landmarks.insert(chosen_landmark);
+ }
+
+ int dim = boost::size(*std::begin(points));
+ typedef std::pair<double, int> dist_i;
+ typedef bool (*comp)(dist_i, dist_i);
+ knn = KNearestNeighbours(nbP);
+ for (int points_i = 0; points_i < nbP; points_i++) {
+ std::priority_queue<dist_i, std::vector<dist_i>, comp> l_heap([](dist_i j1, dist_i j2) {
+ return j1.first > j2.first;
+ });
+ std::set<int>::iterator landmarks_it;
+ int landmarks_i = 0;
+ for (landmarks_it = landmarks.begin(), landmarks_i = 0; landmarks_it != landmarks.end();
+ ++landmarks_it, landmarks_i++) {
+ dist_i dist = std::make_pair(euclidean_distance(points[points_i], points[*landmarks_it]), landmarks_i);
+ l_heap.push(dist);
+ }
+ for (int i = 0; i < dim + 1; i++) {
+ dist_i dist = l_heap.top();
+ knn[points_i].push_back(dist.second);
+ l_heap.pop();
+ }
+ }
+ }
+
+} // namespace witness_complex
+
+} // namespace Gudhi
+
+#endif // LANDMARK_CHOICE_BY_RANDOM_POINT_H_
diff --git a/include/gudhi/Off_reader.h b/include/gudhi/Off_reader.h
new file mode 100644
index 00000000..4fcd2af2
--- /dev/null
+++ b/include/gudhi/Off_reader.h
@@ -0,0 +1,184 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#ifndef OFF_READER_H_
+#define OFF_READER_H_
+
+
+#include <sstream>
+#include <iostream>
+#include <iterator>
+#include <string>
+#include <vector>
+#include <fstream>
+
+namespace Gudhi {
+
+/** \brief OFF file reader top class visitor.
+ *
+ * OFF file must be conform to format described here :
+ * http://www.geomview.org/docs/html/OFF.html
+ */
+class Off_reader {
+ public:
+ Off_reader(std::ifstream& stream) : stream_(stream) { }
+
+ ~Off_reader() {
+ stream_.close();
+ }
+
+ /** \brief
+ * Read an OFF file and calls the following methods :
+ *
+ * <CODE>void init(int dim,int num_vertices,int num_faces,int num_edges); // from file header - num_edges may not be set
+ *
+ * void point(const std::vector<double>& point); // for each point read
+ *
+ * void maximal_face(const std::list<int>& face); // for each face read
+ *
+ * void done(); // upon file read is finished</CODE>
+ *
+ * of the visitor when reading a point or a maximal face. Edges are not taken into account.
+ */
+ template<typename OffVisitor>
+ bool read(OffVisitor& off_visitor) {
+ bool success_read_off_preambule = read_off_preambule(off_visitor);
+ if (!success_read_off_preambule) {
+ std::cerr << "could not read off preambule\n";
+ return false;
+ }
+
+ bool success_read_off_points = read_off_points(off_visitor);
+ if (!success_read_off_points) {
+ std::cerr << "could not read off points\n";
+ return false;
+ }
+
+ bool success_read_off_faces = read_off_faces(off_visitor);
+ if (!success_read_off_faces) {
+ std::cerr << "could not read off faces\n";
+ return false;
+ }
+
+ off_visitor.done();
+ return success_read_off_preambule && success_read_off_points && success_read_off_faces;
+ }
+
+ private:
+ std::ifstream& stream_;
+
+ struct Off_info {
+ int dim;
+ int num_vertices;
+ int num_edges;
+ int num_faces;
+ };
+
+ Off_info off_info_;
+
+ template<typename OffVisitor>
+ bool read_off_preambule(OffVisitor& off_visitor) {
+ std::string line;
+ if (!goto_next_uncomment_line(line)) return false;
+
+ bool is_off_file = (line.find("OFF") != std::string::npos);
+ bool is_noff_file = (line.find("nOFF") != std::string::npos);
+
+ if (!is_off_file && !is_noff_file) {
+ std::cerr << line << std::endl;
+ std::cerr << "missing off header\n";
+ return false;
+ }
+
+ if (!goto_next_uncomment_line(line)) return false;
+ std::istringstream iss(line);
+ if ((is_off_file) && (!is_noff_file)) {
+ off_info_.dim = 3;
+ if (!(iss >> off_info_.num_vertices >> off_info_.num_faces >> off_info_.num_edges)) {
+ std::cerr << "incorrect number of vertices/faces/edges\n";
+ return false;
+ }
+ } else {
+ if (!(iss >> off_info_.dim >> off_info_.num_vertices >> off_info_.num_faces >> off_info_.num_edges)) {
+ std::cerr << "incorrect number of vertices/faces/edges\n";
+ return false;
+ }
+ }
+ off_visitor.init(off_info_.dim, off_info_.num_vertices, off_info_.num_faces, off_info_.num_edges);
+
+ return true;
+ }
+
+ bool goto_next_uncomment_line(std::string& uncomment_line) {
+ uncomment_line.clear();
+ do
+ std::getline(stream_, uncomment_line); while (uncomment_line[0] == '%');
+ return (uncomment_line.size() > 0 && uncomment_line[0] != '%');
+ }
+
+ template<typename OffVisitor>
+ bool read_off_points(OffVisitor& visitor) {
+ int num_vertices_to_read = off_info_.num_vertices;
+ while (num_vertices_to_read--) {
+ std::string line;
+ if (!goto_next_uncomment_line(line)) return false;
+ std::vector<double> point;
+ std::istringstream iss(line);
+ point.assign(std::istream_iterator<double>(iss), std::istream_iterator<double>());
+ // if(point.size() != off_info_.dim) return false;
+ visitor.point(point);
+ }
+ return true;
+ }
+
+ template<typename OffVisitor>
+ bool read_off_faces(OffVisitor& visitor) {
+ std::string line;
+ while (goto_next_uncomment_line(line)) {
+ std::istringstream iss(line);
+ int num_face_vertices;
+ iss >> num_face_vertices;
+ std::vector<int> face;
+ face.assign(std::istream_iterator<int>(iss), std::istream_iterator<int>());
+ // if (face.size() != (off_info_.dim + 1)) return false;
+ visitor.maximal_face(face);
+ }
+ return true;
+ }
+};
+
+template<typename OFFVisitor>
+void read_off(const std::string& name_file_off, OFFVisitor& vis) {
+ std::ifstream stream(name_file_off);
+ if (!stream.is_open()) {
+ std::cerr << "could not open file \n";
+ } else {
+ Off_reader off_reader(stream);
+ off_reader.read(vis);
+ }
+}
+
+} // namespace Gudhi
+
+#endif // OFF_READER_H_
diff --git a/include/gudhi/Persistent_cohomology.h b/include/gudhi/Persistent_cohomology.h
new file mode 100644
index 00000000..b31df6a4
--- /dev/null
+++ b/include/gudhi/Persistent_cohomology.h
@@ -0,0 +1,745 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Clément Maria
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Méditerranée (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PERSISTENT_COHOMOLOGY_H_
+#define PERSISTENT_COHOMOLOGY_H_
+
+#include <gudhi/Persistent_cohomology/Persistent_cohomology_column.h>
+#include <gudhi/Persistent_cohomology/Field_Zp.h>
+#include <gudhi/Simple_object_pool.h>
+
+#include <boost/intrusive/set.hpp>
+#include <boost/pending/disjoint_sets.hpp>
+#include <boost/intrusive/list.hpp>
+
+#include <map>
+#include <utility>
+#include <list>
+#include <vector>
+#include <set>
+#include <fstream> // std::ofstream
+#include <limits> // for numeric_limits<>
+#include <tuple>
+#include <algorithm>
+#include <string>
+#include <stdexcept> // for std::out_of_range
+
+namespace Gudhi {
+
+namespace persistent_cohomology {
+
+/** \brief Computes the persistent cohomology of a filtered complex.
+ *
+ * \ingroup persistent_cohomology
+ *
+ * The computation is implemented with a Compressed Annotation Matrix
+ * (CAM)\cite DBLP:conf/esa/BoissonnatDM13,
+ * and is adapted to the computation of Multi-Field Persistent Homology (MF)
+ * \cite boissonnat:hal-00922572 .
+ *
+ * \implements PersistentHomology
+ *
+ */
+// TODO(CM): Memory allocation policy: classic, use a mempool, etc.
+template<class FilteredComplex, class CoefficientField>
+class Persistent_cohomology {
+ public:
+ typedef FilteredComplex Complex_ds;
+ // Data attached to each simplex to interface with a Property Map.
+ typedef typename Complex_ds::Simplex_key Simplex_key;
+ typedef typename Complex_ds::Simplex_handle Simplex_handle;
+ typedef typename Complex_ds::Filtration_value Filtration_value;
+ typedef typename CoefficientField::Element Arith_element;
+ // Compressed Annotation Matrix types:
+ // Column type
+ typedef Persistent_cohomology_column<Simplex_key, Arith_element> Column; // contains 1 set_hook
+ // Cell type
+ typedef typename Column::Cell Cell; // contains 2 list_hooks
+ // Remark: constant_time_size must be false because base_hook_cam_h has auto_unlink link_mode
+ typedef boost::intrusive::list<Cell,
+ boost::intrusive::constant_time_size<false>,
+ boost::intrusive::base_hook<base_hook_cam_h> > Hcell;
+
+ typedef boost::intrusive::set<Column,
+ boost::intrusive::constant_time_size<false> > Cam;
+ // Sparse column type for the annotation of the boundary of an element.
+ typedef std::vector<std::pair<Simplex_key, Arith_element> > A_ds_type;
+ // Persistent interval type. The Arith_element field is used for the multi-field framework.
+ typedef std::tuple<Simplex_handle, Simplex_handle, Arith_element> Persistent_interval;
+
+ /** \brief Initializes the Persistent_cohomology class.
+ *
+ * @param[in] cpx Complex for which the persistent homology is computed.
+ * cpx is a model of FilteredComplex
+ * @exception std::out_of_range In case the number of simplices is more than Simplex_key type numeric limit.
+ */
+ explicit Persistent_cohomology(Complex_ds& cpx)
+ : cpx_(&cpx),
+ dim_max_(cpx.dimension()), // upper bound on the dimension of the simplices
+ coeff_field_(), // initialize the field coefficient structure.
+ num_simplices_(cpx_->num_simplices()), // num_simplices save to avoid to call thrice the function
+ ds_rank_(num_simplices_), // union-find
+ ds_parent_(num_simplices_), // union-find
+ ds_repr_(num_simplices_, NULL), // union-find -> annotation vectors
+ dsets_(&ds_rank_[0], &ds_parent_[0]), // union-find
+ cam_(), // collection of annotation vectors
+ zero_cocycles_(), // union-find -> Simplex_key of creator for 0-homology
+ transverse_idx_(), // key -> row
+ persistent_pairs_(),
+ interval_length_policy(&cpx, 0),
+ column_pool_(), // memory pools for the CAM
+ cell_pool_() {
+ if (cpx_->num_simplices() > std::numeric_limits<Simplex_key>::max()) {
+ // num_simplices must be strictly lower than the limit, because a value is reserved for null_key.
+ throw std::out_of_range ("The number of simplices is more than Simplex_key type numeric limit.");
+ }
+ Simplex_key idx_fil = 0;
+ for (auto sh : cpx_->filtration_simplex_range()) {
+ cpx_->assign_key(sh, idx_fil);
+ ++idx_fil;
+ dsets_.make_set(cpx_->key(sh));
+ }
+ }
+
+ /** \brief Initializes the Persistent_cohomology class.
+ *
+ * @param[in] cpx Complex for which the persistent homology is compiuted.
+ * cpx is a model of FilteredComplex
+ *
+ * @param[in] persistence_dim_max if true, the persistent homology for the maximal dimension in the
+ * complex is computed. If false, it is ignored. Default is false.
+ */
+ Persistent_cohomology(Complex_ds& cpx, bool persistence_dim_max)
+ : Persistent_cohomology(cpx) {
+ if (persistence_dim_max) {
+ ++dim_max_;
+ }
+ }
+
+ ~Persistent_cohomology() {
+ // Clean the transversal lists
+ for (auto & transverse_ref : transverse_idx_) {
+ // Destruct all the cells
+ transverse_ref.second.row_->clear_and_dispose([&](Cell*p){p->~Cell();});
+ delete transverse_ref.second.row_;
+ }
+ }
+
+ private:
+ struct length_interval {
+ length_interval(Complex_ds * cpx, Filtration_value min_length)
+ : cpx_(cpx),
+ min_length_(min_length) {
+ }
+
+ bool operator()(Simplex_handle sh1, Simplex_handle sh2) {
+ return cpx_->filtration(sh2) - cpx_->filtration(sh1) > min_length_;
+ }
+
+ void set_length(Filtration_value new_length) {
+ min_length_ = new_length;
+ }
+
+ Complex_ds * cpx_;
+ Filtration_value min_length_;
+ };
+
+ public:
+ /** \brief Initializes the coefficient field.*/
+ void init_coefficients(int charac) {
+ coeff_field_.init(charac);
+ }
+ /** \brief Initializes the coefficient field for multi-field persistent homology.*/
+ void init_coefficients(int charac_min, int charac_max) {
+ coeff_field_.init(charac_min, charac_max);
+ }
+
+ /** \brief Compute the persistent homology of the filtered simplicial
+ * complex.
+ *
+ * @param[in] min_interval_length the computation discards all intervals of length
+ * less or equal than min_interval_length
+ *
+ * Assumes that the filtration provided by the simplicial complex is
+ * valid. Undefined behavior otherwise. */
+ void compute_persistent_cohomology(Filtration_value min_interval_length = 0) {
+ interval_length_policy.set_length(min_interval_length);
+ // Compute all finite intervals
+ for (auto sh : cpx_->filtration_simplex_range()) {
+ int dim_simplex = cpx_->dimension(sh);
+ switch (dim_simplex) {
+ case 0:
+ break;
+ case 1:
+ update_cohomology_groups_edge(sh);
+ break;
+ default:
+ update_cohomology_groups(sh, dim_simplex);
+ break;
+ }
+ }
+ // Compute infinite intervals of dimension 0
+ Simplex_key key;
+ for (auto v_sh : cpx_->skeleton_simplex_range(0)) { // for all 0-dimensional simplices
+ key = cpx_->key(v_sh);
+
+ if (ds_parent_[key] == key // root of its tree
+ && zero_cocycles_.find(key) == zero_cocycles_.end()) {
+ persistent_pairs_.emplace_back(
+ cpx_->simplex(key), cpx_->null_simplex(), coeff_field_.characteristic());
+ }
+ }
+ for (auto zero_idx : zero_cocycles_) {
+ persistent_pairs_.emplace_back(
+ cpx_->simplex(zero_idx.second), cpx_->null_simplex(), coeff_field_.characteristic());
+ }
+ // Compute infinite interval of dimension > 0
+ for (auto cocycle : transverse_idx_) {
+ persistent_pairs_.emplace_back(
+ cpx_->simplex(cocycle.first), cpx_->null_simplex(), cocycle.second.characteristics_);
+ }
+ }
+
+ private:
+ /** \brief Update the cohomology groups under the insertion of an edge.
+ *
+ * The 0-homology is maintained with a simple Union-Find data structure, which
+ * explains the existance of a specific function of edge insertions. */
+ void update_cohomology_groups_edge(Simplex_handle sigma) {
+ Simplex_handle u, v;
+ boost::tie(u, v) = cpx_->endpoints(sigma);
+
+ Simplex_key ku = dsets_.find_set(cpx_->key(u));
+ Simplex_key kv = dsets_.find_set(cpx_->key(v));
+
+ if (ku != kv) { // Destroy a connected component
+ dsets_.link(ku, kv);
+ // Keys of the simplices which created the connected components containing
+ // respectively u and v.
+ Simplex_key idx_coc_u, idx_coc_v;
+ auto map_it_u = zero_cocycles_.find(ku);
+ // If the index of the cocycle representing the class is already ku.
+ if (map_it_u == zero_cocycles_.end()) {
+ idx_coc_u = ku;
+ } else {
+ idx_coc_u = map_it_u->second;
+ }
+
+ auto map_it_v = zero_cocycles_.find(kv);
+ // If the index of the cocycle representing the class is already kv.
+ if (map_it_v == zero_cocycles_.end()) {
+ idx_coc_v = kv;
+ } else {
+ idx_coc_v = map_it_v->second;
+ }
+
+ if (cpx_->filtration(cpx_->simplex(idx_coc_u))
+ < cpx_->filtration(cpx_->simplex(idx_coc_v))) { // Kill cocycle [idx_coc_v], which is younger.
+ if (interval_length_policy(cpx_->simplex(idx_coc_v), sigma)) {
+ persistent_pairs_.emplace_back(
+ cpx_->simplex(idx_coc_v), sigma, coeff_field_.characteristic());
+ }
+ // Maintain the index of the 0-cocycle alive.
+ if (kv != idx_coc_v) {
+ zero_cocycles_.erase(map_it_v);
+ }
+ if (kv == dsets_.find_set(kv)) {
+ if (ku != idx_coc_u) {
+ zero_cocycles_.erase(map_it_u);
+ }
+ zero_cocycles_[kv] = idx_coc_u;
+ }
+ } else { // Kill cocycle [idx_coc_u], which is younger.
+ if (interval_length_policy(cpx_->simplex(idx_coc_u), sigma)) {
+ persistent_pairs_.emplace_back(
+ cpx_->simplex(idx_coc_u), sigma, coeff_field_.characteristic());
+ }
+ // Maintain the index of the 0-cocycle alive.
+ if (ku != idx_coc_u) {
+ zero_cocycles_.erase(map_it_u);
+ }
+ if (ku == dsets_.find_set(ku)) {
+ if (kv != idx_coc_v) {
+ zero_cocycles_.erase(map_it_v);
+ }
+ zero_cocycles_[ku] = idx_coc_v;
+ }
+ }
+ cpx_->assign_key(sigma, cpx_->null_key());
+ } else { // If ku == kv, same connected component: create a 1-cocycle class.
+ create_cocycle(sigma, coeff_field_.multiplicative_identity(), coeff_field_.characteristic());
+ }
+ }
+
+ /*
+ * Compute the annotation of the boundary of a simplex.
+ */
+ void annotation_of_the_boundary(
+ std::map<Simplex_key, Arith_element> & map_a_ds, Simplex_handle sigma,
+ int dim_sigma) {
+ // traverses the boundary of sigma, keeps track of the annotation vectors,
+ // with multiplicity. We used to sum the coefficients directly in
+ // annotations_in_boundary by using a map, we now do it later.
+ typedef std::pair<Column *, int> annotation_t;
+ // Danger: not thread-safe!
+ static std::vector<annotation_t> annotations_in_boundary;
+ annotations_in_boundary.clear();
+ int sign = 1 - 2 * (dim_sigma % 2); // \in {-1,1} provides the sign in the
+ // alternate sum in the boundary.
+ Simplex_key key;
+ Column * curr_col;
+
+ for (auto sh : cpx_->boundary_simplex_range(sigma)) {
+ key = cpx_->key(sh);
+ if (key != cpx_->null_key()) { // A simplex with null_key is a killer, and have null annotation
+ // Find its annotation vector
+ curr_col = ds_repr_[dsets_.find_set(key)];
+ if (curr_col != NULL) { // and insert it in annotations_in_boundary with multyiplicative factor "sign".
+ annotations_in_boundary.emplace_back(curr_col, sign);
+ }
+ }
+ sign = -sign;
+ }
+ // Place identical annotations consecutively so we can easily sum their multiplicities.
+ std::sort(annotations_in_boundary.begin(), annotations_in_boundary.end(),
+ [](annotation_t const& a, annotation_t const& b) { return a.first < b.first; });
+
+ // Sum the annotations with multiplicity, using a map<key,coeff>
+ // to represent a sparse vector.
+ std::pair<typename std::map<Simplex_key, Arith_element>::iterator, bool> result_insert_a_ds;
+
+ for (auto ann_it = annotations_in_boundary.begin(); ann_it != annotations_in_boundary.end(); /**/) {
+ Column* col = ann_it->first;
+ int mult = ann_it->second;
+ while (++ann_it != annotations_in_boundary.end() && ann_it->first == col) {
+ mult += ann_it->second;
+ }
+ // The following test is just a heuristic, it is not required, and it is fine that is misses p == 0.
+ if (mult != coeff_field_.additive_identity()) { // For all columns in the boundary,
+ for (auto cell_ref : col->col_) { // insert every cell in map_a_ds with multiplicity
+ Arith_element w_y = coeff_field_.times(cell_ref.coefficient_, mult); // coefficient * multiplicity
+
+ if (w_y != coeff_field_.additive_identity()) { // if != 0
+ result_insert_a_ds = map_a_ds.insert(std::pair<Simplex_key, Arith_element>(cell_ref.key_, w_y));
+ if (!(result_insert_a_ds.second)) { // if cell_ref.key_ already a Key in map_a_ds
+ result_insert_a_ds.first->second = coeff_field_.plus_equal(result_insert_a_ds.first->second, w_y);
+ if (result_insert_a_ds.first->second == coeff_field_.additive_identity()) {
+ map_a_ds.erase(result_insert_a_ds.first);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * Update the cohomology groups under the insertion of a simplex.
+ */
+ void update_cohomology_groups(Simplex_handle sigma, int dim_sigma) {
+// Compute the annotation of the boundary of sigma:
+ std::map<Simplex_key, Arith_element> map_a_ds;
+ annotation_of_the_boundary(map_a_ds, sigma, dim_sigma);
+// Update the cohomology groups:
+ if (map_a_ds.empty()) { // sigma is a creator in all fields represented in coeff_field_
+ if (dim_sigma < dim_max_) {
+ create_cocycle(sigma, coeff_field_.multiplicative_identity(),
+ coeff_field_.characteristic());
+ }
+ } else { // sigma is a destructor in at least a field in coeff_field_
+ // Convert map_a_ds to a vector
+ A_ds_type a_ds; // admits reverse iterators
+ for (auto map_a_ds_ref : map_a_ds) {
+ a_ds.push_back(
+ std::pair<Simplex_key, Arith_element>(map_a_ds_ref.first,
+ map_a_ds_ref.second));
+ }
+
+ Arith_element inv_x, charac;
+ Arith_element prod = coeff_field_.characteristic(); // Product of characteristic of the fields
+ for (auto a_ds_rit = a_ds.rbegin();
+ (a_ds_rit != a_ds.rend())
+ && (prod != coeff_field_.multiplicative_identity()); ++a_ds_rit) {
+ std::tie(inv_x, charac) = coeff_field_.inverse(a_ds_rit->second, prod);
+
+ if (inv_x != coeff_field_.additive_identity()) {
+ destroy_cocycle(sigma, a_ds, a_ds_rit->first, inv_x, charac);
+ prod /= charac;
+ }
+ }
+ if (prod != coeff_field_.multiplicative_identity()
+ && dim_sigma < dim_max_) {
+ create_cocycle(sigma, coeff_field_.multiplicative_identity(prod), prod);
+ }
+ }
+ }
+
+ /* \brief Create a new cocycle class.
+ *
+ * The class is created by the insertion of the simplex sigma.
+ * The methods adds a cocycle, representing the new cocycle class,
+ * to the matrix representing the cohomology groups.
+ * The new cocycle has value 0 on every simplex except on sigma
+ * where it worths 1.*/
+ void create_cocycle(Simplex_handle sigma, Arith_element x,
+ Arith_element charac) {
+ Simplex_key key = cpx_->key(sigma);
+ // Create a column containing only one cell,
+ Column * new_col = column_pool_.construct(key);
+ Cell * new_cell = cell_pool_.construct(key, x, new_col);
+ new_col->col_.push_back(*new_cell);
+ // and insert it in the matrix, in constant time thanks to the hint cam_.end().
+ // Indeed *new_col has the biggest lexicographic value because key is the
+ // biggest key used so far.
+ cam_.insert(cam_.end(), *new_col);
+ // Update the disjoint sets data structure.
+ Hcell * new_hcell = new Hcell;
+ new_hcell->push_back(*new_cell);
+ transverse_idx_[key] = cocycle(charac, new_hcell); // insert the new row
+ ds_repr_[key] = new_col;
+ }
+
+ /* \brief Destroy a cocycle class.
+ *
+ * The cocycle class is destroyed by the insertion of sigma.
+ * The methods proceeds to a reduction of the matrix representing
+ * the cohomology groups using Gauss pivoting. The reduction zeros-out
+ * the row containing the cell with highest key in
+ * a_ds, the annotation of the boundary of simplex sigma. This key
+ * is "death_key".*/
+ void destroy_cocycle(Simplex_handle sigma, A_ds_type const& a_ds,
+ Simplex_key death_key, Arith_element inv_x,
+ Arith_element charac) {
+ // Create a finite persistent interval for which the interval exists
+ if (interval_length_policy(cpx_->simplex(death_key), sigma)) {
+ persistent_pairs_.emplace_back(cpx_->simplex(death_key) // creator
+ , sigma // destructor
+ , charac); // fields
+ }
+
+ auto death_key_row = transverse_idx_.find(death_key); // Find the beginning of the row.
+ std::pair<typename Cam::iterator, bool> result_insert_cam;
+
+ auto row_cell_it = death_key_row->second.row_->begin();
+
+ while (row_cell_it != death_key_row->second.row_->end()) { // Traverse all cells in
+ // the row at index death_key.
+ Arith_element w = coeff_field_.times_minus(inv_x, row_cell_it->coefficient_);
+
+ if (w != coeff_field_.additive_identity()) {
+ Column * curr_col = row_cell_it->self_col_;
+ ++row_cell_it;
+ // Disconnect the column from the rows in the CAM.
+ for (auto& col_cell : curr_col->col_) {
+ col_cell.base_hook_cam_h::unlink();
+ }
+
+ // Remove the column from the CAM before modifying its value
+ cam_.erase(cam_.iterator_to(*curr_col));
+ // Proceed to the reduction of the column
+ plus_equal_column(*curr_col, a_ds, w);
+
+ if (curr_col->col_.empty()) { // If the column is null
+ ds_repr_[curr_col->class_key_] = NULL;
+ column_pool_.destroy(curr_col); // delete curr_col;
+ } else {
+ // Find whether the column obtained is already in the CAM
+ result_insert_cam = cam_.insert(*curr_col);
+ if (result_insert_cam.second) { // If it was not in the CAM before: insertion has succeeded
+ for (auto& col_cell : curr_col->col_) {
+ // re-establish the row links
+ transverse_idx_[col_cell.key_].row_->push_front(col_cell);
+ }
+ } else { // There is already an identical column in the CAM:
+ // merge two disjoint sets.
+ dsets_.link(curr_col->class_key_,
+ result_insert_cam.first->class_key_);
+
+ Simplex_key key_tmp = dsets_.find_set(curr_col->class_key_);
+ ds_repr_[key_tmp] = &(*(result_insert_cam.first));
+ result_insert_cam.first->class_key_ = key_tmp;
+ // intrusive containers don't own their elements, we have to release them manually
+ curr_col->col_.clear_and_dispose([&](Cell*p){cell_pool_.destroy(p);});
+ column_pool_.destroy(curr_col); // delete curr_col;
+ }
+ }
+ } else {
+ ++row_cell_it;
+ } // If w == 0, pass.
+ }
+
+ // Because it is a killer simplex, set the data of sigma to null_key().
+ if (charac == coeff_field_.characteristic()) {
+ cpx_->assign_key(sigma, cpx_->null_key());
+ }
+ if (death_key_row->second.characteristics_ == charac) {
+ delete death_key_row->second.row_;
+ transverse_idx_.erase(death_key_row);
+ } else {
+ death_key_row->second.characteristics_ /= charac;
+ }
+ }
+
+ /*
+ * Assign: target <- target + w * other.
+ */
+ void plus_equal_column(Column & target, A_ds_type const& other // value_type is pair<Simplex_key,Arith_element>
+ , Arith_element w) {
+ auto target_it = target.col_.begin();
+ auto other_it = other.begin();
+ while (target_it != target.col_.end() && other_it != other.end()) {
+ if (target_it->key_ < other_it->first) {
+ ++target_it;
+ } else {
+ if (target_it->key_ > other_it->first) {
+ Cell * cell_tmp = cell_pool_.construct(Cell(other_it->first // key
+ , coeff_field_.additive_identity(), &target));
+
+ cell_tmp->coefficient_ = coeff_field_.plus_times_equal(cell_tmp->coefficient_, other_it->second, w);
+
+ target.col_.insert(target_it, *cell_tmp);
+
+ ++other_it;
+ } else { // it1->key == it2->key
+ // target_it->coefficient_ <- target_it->coefficient_ + other_it->second * w
+ target_it->coefficient_ = coeff_field_.plus_times_equal(target_it->coefficient_, other_it->second, w);
+ if (target_it->coefficient_ == coeff_field_.additive_identity()) {
+ auto tmp_it = target_it;
+ ++target_it;
+ ++other_it; // iterators remain valid
+ Cell * tmp_cell_ptr = &(*tmp_it);
+ target.col_.erase(tmp_it); // removed from column
+
+ cell_pool_.destroy(tmp_cell_ptr); // delete from memory
+ } else {
+ ++target_it;
+ ++other_it;
+ }
+ }
+ }
+ }
+ while (other_it != other.end()) {
+ Cell * cell_tmp = cell_pool_.construct(Cell(other_it->first, coeff_field_.additive_identity(), &target));
+ cell_tmp->coefficient_ = coeff_field_.plus_times_equal(cell_tmp->coefficient_, other_it->second, w);
+ target.col_.insert(target.col_.end(), *cell_tmp);
+
+ ++other_it;
+ }
+ }
+
+ /*
+ * Compare two intervals by length.
+ */
+ struct cmp_intervals_by_length {
+ explicit cmp_intervals_by_length(Complex_ds * sc)
+ : sc_(sc) {
+ }
+ bool operator()(const Persistent_interval & p1, const Persistent_interval & p2) {
+ return (sc_->filtration(get < 1 > (p1)) - sc_->filtration(get < 0 > (p1))
+ > sc_->filtration(get < 1 > (p2)) - sc_->filtration(get < 0 > (p2)));
+ }
+ Complex_ds * sc_;
+ };
+
+ public:
+ /** \brief Output the persistence diagram in ostream.
+ *
+ * The file format is the following:
+ * p1*...*pr dim b d
+ *
+ * where "dim" is the dimension of the homological feature,
+ * b and d are respectively the birth and death of the feature and
+ * p1*...*pr is the product of prime numbers pi such that the homology
+ * feature exists in homology with Z/piZ coefficients.
+ */
+ void output_diagram(std::ostream& ostream = std::cout) {
+ cmp_intervals_by_length cmp(cpx_);
+ std::sort(std::begin(persistent_pairs_), std::end(persistent_pairs_), cmp);
+ bool has_infinity = std::numeric_limits<Filtration_value>::has_infinity;
+ for (auto pair : persistent_pairs_) {
+ // Special case on windows, inf is "1.#INF" (cf. unitary tests and R package TDA)
+ if (has_infinity && cpx_->filtration(get<1>(pair)) == std::numeric_limits<Filtration_value>::infinity()) {
+ ostream << get<2>(pair) << " " << cpx_->dimension(get<0>(pair)) << " "
+ << cpx_->filtration(get<0>(pair)) << " inf " << std::endl;
+ } else {
+ ostream << get<2>(pair) << " " << cpx_->dimension(get<0>(pair)) << " "
+ << cpx_->filtration(get<0>(pair)) << " "
+ << cpx_->filtration(get<1>(pair)) << " " << std::endl;
+ }
+ }
+ }
+
+ void write_output_diagram(std::string diagram_name) {
+ std::ofstream diagram_out(diagram_name.c_str());
+ cmp_intervals_by_length cmp(cpx_);
+ std::sort(std::begin(persistent_pairs_), std::end(persistent_pairs_), cmp);
+ for (auto pair : persistent_pairs_) {
+ diagram_out << cpx_->dimension(get<0>(pair)) << " "
+ << cpx_->filtration(get<0>(pair)) << " "
+ << cpx_->filtration(get<1>(pair)) << std::endl;
+ }
+ }
+
+ /** @brief Returns Betti numbers.
+ * @return A vector of Betti numbers.
+ */
+ std::vector<int> betti_numbers() const {
+ // Init Betti numbers vector with zeros until Simplicial complex dimension
+ std::vector<int> betti_numbers(cpx_->dimension(), 0);
+
+ for (auto pair : persistent_pairs_) {
+ // Count never ended persistence intervals
+ if (cpx_->null_simplex() == get<1>(pair)) {
+ // Increment corresponding betti number
+ betti_numbers[cpx_->dimension(get<0>(pair))] += 1;
+ }
+ }
+ return betti_numbers;
+ }
+
+ /** @brief Returns the Betti number of the dimension passed by parameter.
+ * @param[in] dimension The Betti number dimension to get.
+ * @return Betti number of the given dimension
+ *
+ */
+ int betti_number(int dimension) const {
+ int betti_number = 0;
+
+ for (auto pair : persistent_pairs_) {
+ // Count never ended persistence intervals
+ if (cpx_->null_simplex() == get<1>(pair)) {
+ if (cpx_->dimension(get<0>(pair)) == dimension) {
+ // Increment betti number found
+ ++betti_number;
+ }
+ }
+ }
+ return betti_number;
+ }
+
+ /** @brief Returns the persistent Betti numbers.
+ * @param[in] from The persistence birth limit to be added in the number \f$(persistent birth \leq from)\f$.
+ * @param[in] to The persistence death limit to be added in the number \f$(persistent death > to)\f$.
+ * @return A vector of persistent Betti numbers.
+ */
+ std::vector<int> persistent_betti_numbers(Filtration_value from, Filtration_value to) const {
+ // Init Betti numbers vector with zeros until Simplicial complex dimension
+ std::vector<int> betti_numbers(cpx_->dimension(), 0);
+
+ for (auto pair : persistent_pairs_) {
+ // Count persistence intervals that covers the given interval
+ // null_simplex test : if the function is called with to=+infinity, we still get something useful. And it will
+ // still work if we change the complex filtration function to reject null simplices.
+ if (cpx_->filtration(get<0>(pair)) <= from &&
+ (get<1>(pair) == cpx_->null_simplex() || cpx_->filtration(get<1>(pair)) > to)) {
+ // Increment corresponding betti number
+ betti_numbers[cpx_->dimension(get<0>(pair))] += 1;
+ }
+ }
+ return betti_numbers;
+ }
+
+ /** @brief Returns the persistent Betti number of the dimension passed by parameter.
+ * @param[in] dimension The Betti number dimension to get.
+ * @param[in] from The persistence birth limit to be added in the number \f$(persistent birth \leq from)\f$.
+ * @param[in] to The persistence death limit to be added in the number \f$(persistent death > to)\f$.
+ * @return Persistent Betti number of the given dimension
+ */
+ int persistent_betti_number(int dimension, Filtration_value from, Filtration_value to) const {
+ int betti_number = 0;
+
+ for (auto pair : persistent_pairs_) {
+ // Count persistence intervals that covers the given interval
+ // null_simplex test : if the function is called with to=+infinity, we still get something useful. And it will
+ // still work if we change the complex filtration function to reject null simplices.
+ if (cpx_->filtration(get<0>(pair)) <= from &&
+ (get<1>(pair) == cpx_->null_simplex() || cpx_->filtration(get<1>(pair)) > to)) {
+ if (cpx_->dimension(get<0>(pair)) == dimension) {
+ // Increment betti number found
+ ++betti_number;
+ }
+ }
+ }
+ return betti_number;
+ }
+
+ /** @brief Returns the persistent pairs.
+ * @return Persistent pairs
+ *
+ */
+ const std::vector<Persistent_interval>& get_persistent_pairs() const {
+ return persistent_pairs_;
+ }
+
+ private:
+ /*
+ * Structure representing a cocycle.
+ */
+ struct cocycle {
+ cocycle()
+ : row_(nullptr),
+ characteristics_() {
+ }
+ cocycle(Arith_element characteristics, Hcell * row)
+ : row_(row),
+ characteristics_(characteristics) {
+ }
+
+ Hcell * row_; // points to the corresponding row in the CAM
+ Arith_element characteristics_; // product of field characteristics for which the cocycle exist
+ };
+
+ public:
+ Complex_ds * cpx_;
+ int dim_max_;
+ CoefficientField coeff_field_;
+ size_t num_simplices_;
+
+ /* Disjoint sets data structure to link the model of FilteredComplex
+ * with the compressed annotation matrix.
+ * ds_rank_ is a property map Simplex_key -> int, ds_parent_ is a property map
+ * Simplex_key -> simplex_key_t */
+ std::vector<int> ds_rank_;
+ std::vector<Simplex_key> ds_parent_;
+ std::vector<Column *> ds_repr_;
+ boost::disjoint_sets<int *, Simplex_key *> dsets_;
+ /* The compressed annotation matrix fields.*/
+ Cam cam_;
+ /* Dictionary establishing the correspondance between the Simplex_key of
+ * the root vertex in the union-find ds and the Simplex_key of the vertex which
+ * created the connected component as a 0-dimension homology feature.*/
+ std::map<Simplex_key, Simplex_key> zero_cocycles_;
+ /* Key -> row. */
+ std::map<Simplex_key, cocycle> transverse_idx_;
+ /* Persistent intervals. */
+ std::vector<Persistent_interval> persistent_pairs_;
+ length_interval interval_length_policy;
+
+ Simple_object_pool<Column> column_pool_;
+ Simple_object_pool<Cell> cell_pool_;
+};
+
+} // namespace persistent_cohomology
+
+} // namespace Gudhi
+
+#endif // PERSISTENT_COHOMOLOGY_H_
diff --git a/include/gudhi/Persistent_cohomology/Field_Zp.h b/include/gudhi/Persistent_cohomology/Field_Zp.h
new file mode 100644
index 00000000..6db16e69
--- /dev/null
+++ b/include/gudhi/Persistent_cohomology/Field_Zp.h
@@ -0,0 +1,116 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Clément Maria
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Méditerranée (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PERSISTENT_COHOMOLOGY_FIELD_ZP_H_
+#define PERSISTENT_COHOMOLOGY_FIELD_ZP_H_
+
+#include <utility>
+#include <vector>
+
+namespace Gudhi {
+
+namespace persistent_cohomology {
+
+/** \brief Structure representing the coefficient field \f$\mathbb{Z}/p\mathbb{Z}\f$
+ *
+ * \implements CoefficientField
+ * \ingroup persistent_cohomology
+ */
+class Field_Zp {
+ public:
+ typedef int Element;
+
+ Field_Zp()
+ : Prime(0),
+ inverse_() {
+ }
+
+ void init(int charac) {
+ assert(charac > 0); // division by zero + non negative values
+ Prime = charac;
+ inverse_.clear();
+ inverse_.reserve(charac);
+ inverse_.push_back(0);
+ for (int i = 1; i < Prime; ++i) {
+ int inv = 1;
+ while (((inv * i) % Prime) != 1)
+ ++inv;
+ inverse_.push_back(inv);
+ }
+ }
+
+ /** Set x <- x + w * y*/
+ Element plus_times_equal(const Element& x, const Element& y, const Element& w) {
+ assert(Prime > 0); // division by zero + non negative values
+ Element result = (x + w * y) % Prime;
+ if (result < 0)
+ result += Prime;
+ return result;
+ }
+
+// operator= defined on Element
+
+ /** Returns y * w */
+ Element times(const Element& y, const Element& w) {
+ return plus_times_equal(0, y, (Element)w);
+ }
+
+ Element plus_equal(const Element& x, const Element& y) {
+ return plus_times_equal(x, y, (Element)1);
+ }
+
+ /** \brief Returns the additive idendity \f$0_{\Bbbk}\f$ of the field.*/
+ Element additive_identity() const {
+ return 0;
+ }
+ /** \brief Returns the multiplicative identity \f$1_{\Bbbk}\f$ of the field.*/
+ Element multiplicative_identity(Element = 0) const {
+ return 1;
+ }
+ /** Returns the inverse in the field. Modifies P. ??? */
+ std::pair<Element, Element> inverse(Element x, Element P) {
+ return std::pair<Element, Element>(inverse_[x], P);
+ } // <------ return the product of field characteristic for which x is invertible
+
+ /** Returns -x * y.*/
+ Element times_minus(Element x, Element y) {
+ assert(Prime > 0); // division by zero + non negative values
+ Element out = (-x * y) % Prime;
+ return (out < 0) ? out + Prime : out;
+ }
+
+ /** \brief Returns the characteristic \f$p\f$ of the field.*/
+ int characteristic() const {
+ return Prime;
+ }
+
+ private:
+ int Prime;
+ /** Property map Element -> Element, which associate to an element its inverse in the field.*/
+ std::vector<Element> inverse_;
+};
+
+} // namespace persistent_cohomology
+
+} // namespace Gudhi
+
+#endif // PERSISTENT_COHOMOLOGY_FIELD_ZP_H_
diff --git a/include/gudhi/Persistent_cohomology/Multi_field.h b/include/gudhi/Persistent_cohomology/Multi_field.h
new file mode 100644
index 00000000..38bc08d1
--- /dev/null
+++ b/include/gudhi/Persistent_cohomology/Multi_field.h
@@ -0,0 +1,185 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Clément Maria
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Méditerranée (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PERSISTENT_COHOMOLOGY_MULTI_FIELD_H_
+#define PERSISTENT_COHOMOLOGY_MULTI_FIELD_H_
+
+#include <gmpxx.h>
+
+#include <vector>
+#include <utility>
+
+namespace Gudhi {
+
+namespace persistent_cohomology {
+
+/** \brief Structure representing coefficients in a set of finite fields simultaneously
+ * using the chinese remainder theorem.
+ *
+ * \implements CoefficientField
+ * \ingroup persistent_cohomology
+
+ * Details on the algorithms may be found in \cite boissonnat:hal-00922572
+ */
+class Multi_field {
+ public:
+ typedef mpz_class Element;
+
+ Multi_field()
+ : prod_characteristics_(0),
+ mult_id_all(0),
+ add_id_all(0) {
+ }
+
+ /* Initialize the multi-field. The generation of prime numbers might fail with
+ * a very small probability.*/
+ void init(int min_prime, int max_prime) {
+ if (max_prime < 2) {
+ std::cerr << "There is no prime less than " << max_prime << std::endl;
+ }
+ if (min_prime > max_prime) {
+ std::cerr << "No prime in [" << min_prime << ":" << max_prime << "]"
+ << std::endl;
+ }
+ // fill the list of prime numbers
+ int curr_prime = min_prime;
+ mpz_t tmp_prime;
+ mpz_init_set_ui(tmp_prime, min_prime);
+ // test if min_prime is prime
+ int is_prime = mpz_probab_prime_p(tmp_prime, 25); // probabilistic primality test
+
+ if (is_prime == 0) { // min_prime is composite
+ mpz_nextprime(tmp_prime, tmp_prime);
+ curr_prime = mpz_get_ui(tmp_prime);
+ }
+
+ while (curr_prime <= max_prime) {
+ primes_.push_back(curr_prime);
+ mpz_nextprime(tmp_prime, tmp_prime);
+ curr_prime = mpz_get_ui(tmp_prime);
+ }
+ mpz_clear(tmp_prime);
+ // set m to primorial(bound_prime)
+ prod_characteristics_ = 1;
+ for (auto p : primes_) {
+ prod_characteristics_ *= p;
+ }
+
+ // Uvect_
+ Element Ui;
+ Element tmp_elem;
+ for (auto p : primes_) {
+ assert(p > 0); // division by zero + non negative values
+ tmp_elem = prod_characteristics_ / p;
+ // Element tmp_elem_bis = 10;
+ mpz_powm_ui(tmp_elem.get_mpz_t(), tmp_elem.get_mpz_t(), p - 1,
+ prod_characteristics_.get_mpz_t());
+ Uvect_.push_back(tmp_elem);
+ }
+ mult_id_all = 0;
+ for (auto uvect : Uvect_) {
+ assert(prod_characteristics_ > 0); // division by zero + non negative values
+ mult_id_all = (mult_id_all + uvect) % prod_characteristics_;
+ }
+ }
+
+ /** \brief Returns the additive idendity \f$0_{\Bbbk}\f$ of the field.*/
+ const Element& additive_identity() const {
+ return add_id_all;
+ }
+ /** \brief Returns the multiplicative identity \f$1_{\Bbbk}\f$ of the field.*/
+ const Element& multiplicative_identity() const {
+ return mult_id_all;
+ } // 1 everywhere
+
+ Element multiplicative_identity(Element Q) {
+ if (Q == prod_characteristics_) {
+ return multiplicative_identity();
+ }
+
+ assert(prod_characteristics_ > 0); // division by zero + non negative values
+ Element mult_id = 0;
+ for (unsigned int idx = 0; idx < primes_.size(); ++idx) {
+ assert(primes_[idx] > 0); // division by zero + non negative values
+ if ((Q % primes_[idx]) == 0) {
+ mult_id = (mult_id + Uvect_[idx]) % prod_characteristics_;
+ }
+ }
+ return mult_id;
+ }
+
+ /** Returns y * w */
+ Element times(const Element& y, const Element& w) {
+ return plus_times_equal(0, y, w);
+ }
+
+ Element plus_equal(const Element& x, const Element& y) {
+ return plus_times_equal(x, y, (Element)1);
+ }
+
+ /** \brief Returns the characteristic \f$p\f$ of the field.*/
+ const Element& characteristic() const {
+ return prod_characteristics_;
+ }
+
+ /** Returns the inverse in the field. Modifies P. ??? */
+ std::pair<Element, Element> inverse(Element x, Element QS) {
+ Element QR;
+ mpz_gcd(QR.get_mpz_t(), x.get_mpz_t(), QS.get_mpz_t()); // QR <- gcd(x,QS)
+ if (QR == QS)
+ return std::pair<Element, Element>(additive_identity(), multiplicative_identity()); // partial inverse is 0
+ Element QT = QS / QR;
+ Element inv_qt;
+ mpz_invert(inv_qt.get_mpz_t(), x.get_mpz_t(), QT.get_mpz_t());
+
+ assert(prod_characteristics_ > 0); // division by zero + non negative values
+ return { (inv_qt * multiplicative_identity(QT)) % prod_characteristics_, QT };
+ }
+ /** Returns -x * y.*/
+ Element times_minus(const Element& x, const Element& y) {
+ assert(prod_characteristics_ > 0); // division by zero + non negative values
+ /* This assumes that (x*y)%pc cannot be zero, but Field_Zp has specific code for the 0 case ??? */
+ return prod_characteristics_ - ((x * y) % prod_characteristics_);
+ }
+
+ /** Set x <- x + w * y*/
+ Element plus_times_equal(const Element& x, const Element& y, const Element& w) {
+ assert(prod_characteristics_ > 0); // division by zero + non negative values
+ Element result = (x + w * y) % prod_characteristics_;
+ if (result < 0)
+ result += prod_characteristics_;
+ return result;
+ }
+
+ Element prod_characteristics_; // product of characteristics of the fields
+ // represented by the multi-field class
+ std::vector<int> primes_; // all the characteristics of the fields
+ std::vector<Element> Uvect_;
+ Element mult_id_all;
+ const Element add_id_all;
+};
+
+} // namespace persistent_cohomology
+
+} // namespace Gudhi
+
+#endif // PERSISTENT_COHOMOLOGY_MULTI_FIELD_H_
diff --git a/include/gudhi/Persistent_cohomology/Persistent_cohomology_column.h b/include/gudhi/Persistent_cohomology/Persistent_cohomology_column.h
new file mode 100644
index 00000000..5deb2d88
--- /dev/null
+++ b/include/gudhi/Persistent_cohomology/Persistent_cohomology_column.h
@@ -0,0 +1,140 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Clément Maria
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Méditerranée (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PERSISTENT_COHOMOLOGY_PERSISTENT_COHOMOLOGY_COLUMN_H_
+#define PERSISTENT_COHOMOLOGY_PERSISTENT_COHOMOLOGY_COLUMN_H_
+
+#include <boost/intrusive/set.hpp>
+#include <boost/intrusive/list.hpp>
+
+#include <list>
+
+namespace Gudhi {
+
+namespace persistent_cohomology {
+
+template<typename SimplexKey, typename ArithmeticElement>
+class Persistent_cohomology_column;
+
+struct cam_h_tag;
+// for horizontal traversal in the CAM
+struct cam_v_tag;
+// for vertical traversal in the CAM
+
+typedef boost::intrusive::list_base_hook<boost::intrusive::tag<cam_h_tag>,
+ boost::intrusive::link_mode<boost::intrusive::auto_unlink> // allows .unlink()
+> base_hook_cam_h;
+
+typedef boost::intrusive::list_base_hook<boost::intrusive::tag<cam_v_tag>,
+ boost::intrusive::link_mode<boost::intrusive::normal_link> // faster hook, less safe
+> base_hook_cam_v;
+
+/** \internal
+ * \brief
+ *
+ */
+template<typename SimplexKey, typename ArithmeticElement>
+class Persistent_cohomology_cell : public base_hook_cam_h,
+ public base_hook_cam_v {
+ public:
+ template<class T1, class T2> friend class Persistent_cohomology;
+ friend class Persistent_cohomology_column<SimplexKey, ArithmeticElement>;
+
+ typedef Persistent_cohomology_column<SimplexKey, ArithmeticElement> Column;
+
+ Persistent_cohomology_cell(SimplexKey key, ArithmeticElement x,
+ Column * self_col)
+ : key_(key),
+ coefficient_(x),
+ self_col_(self_col) {
+ }
+
+ SimplexKey key_;
+ ArithmeticElement coefficient_;
+ Column * self_col_;
+};
+
+/*
+ * \brief Sparse column for the Compressed Annotation Matrix.
+ *
+ * The non-zero coefficients of the column are stored in a
+ * boost::intrusive::list. Contains a hook to be stored in a
+ * boost::intrusive::set.
+ *
+ * Movable but not Copyable.
+ */
+template<typename SimplexKey, typename ArithmeticElement>
+class Persistent_cohomology_column : public boost::intrusive::set_base_hook<
+ boost::intrusive::link_mode<boost::intrusive::normal_link> > {
+ template<class T1, class T2> friend class Persistent_cohomology;
+
+ public:
+ typedef Persistent_cohomology_cell<SimplexKey, ArithmeticElement> Cell;
+ typedef boost::intrusive::list<Cell,
+ boost::intrusive::constant_time_size<false>,
+ boost::intrusive::base_hook<base_hook_cam_v> > Col_type;
+
+ /** \brief Creates an empty column.*/
+ explicit Persistent_cohomology_column(SimplexKey key)
+ : col_(),
+ class_key_(key) {}
+
+ /** \brief Returns true iff the column is null.*/
+ bool is_null() const {
+ return col_.empty();
+ }
+ /** \brief Returns the key of the representative simplex of
+ * the set of simplices having this column as annotation vector
+ * in the compressed annotation matrix.*/
+ SimplexKey class_key() const {
+ return class_key_;
+ }
+
+ /** \brief Lexicographic comparison of two columns.*/
+ friend bool operator<(const Persistent_cohomology_column& c1,
+ const Persistent_cohomology_column& c2) {
+ typename Col_type::const_iterator it1 = c1.col_.begin();
+ typename Col_type::const_iterator it2 = c2.col_.begin();
+ while (it1 != c1.col_.end() && it2 != c2.col_.end()) {
+ if (it1->key_ == it2->key_) {
+ if (it1->coefficient_ == it2->coefficient_) {
+ ++it1;
+ ++it2;
+ } else {
+ return it1->coefficient_ < it2->coefficient_;
+ }
+ } else {
+ return it1->key_ < it2->key_;
+ }
+ }
+ return (it2 != c2.col_.end());
+ }
+
+ Col_type col_;
+ SimplexKey class_key_;
+};
+
+} // namespace persistent_cohomology
+
+} // namespace Gudhi
+
+#endif // PERSISTENT_COHOMOLOGY_PERSISTENT_COHOMOLOGY_COLUMN_H_
diff --git a/include/gudhi/Point.h b/include/gudhi/Point.h
new file mode 100644
index 00000000..0479e71e
--- /dev/null
+++ b/include/gudhi/Point.h
@@ -0,0 +1,170 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef POINT_H_
+#define POINT_H_
+
+#include <cmath>
+#include <vector>
+#include <cassert>
+#include <cstddef>
+#include <initializer_list>
+
+class Point_d {
+ public:
+ Point_d(size_t dim = 3) : coords_(dim, 0) { }
+
+ Point_d(const Point_d& other) : coords_(other.coords_) { }
+
+ Point_d(const std::initializer_list<double>& list) : coords_(list) { }
+
+ template<typename CoordsIt>
+ Point_d(CoordsIt begin, CoordsIt end) : coords_(begin, end) { }
+
+ size_t dimension() const {
+ return coords_.size();
+ }
+
+ double x() const {
+ return coords_[0];
+ }
+
+ double y() const {
+ return coords_[1];
+ }
+
+ double z() const {
+ return coords_[2];
+ }
+
+ double& x() {
+ return coords_[0];
+ }
+
+ double& y() {
+ return coords_[1];
+ }
+
+ double& z() {
+ return coords_[2];
+ }
+
+ std::vector<double>::const_iterator begin() const {
+ return coords_.begin();
+ }
+
+ std::vector<double>::const_iterator end() const {
+ return coords_.end();
+ }
+
+ double& operator[](unsigned i) {
+ return coords_[i];
+ }
+
+ const double& operator[](unsigned i) const {
+ return coords_[i];
+ }
+
+ double squared_norm() const {
+ double res = 0;
+ for (auto x : coords_)
+ res += x * x;
+ return res;
+ }
+
+ friend double squared_dist(const Point_d& p1, const Point_d& p2) {
+ assert(p1.dimension() == p2.dimension());
+ double res = 0;
+ for (unsigned i = 0; i < p1.coords_.size(); ++i)
+ res += (p1[i] - p2[i])*(p1[i] - p2[i]);
+ return res;
+ }
+
+ /**
+ * dot product
+ */
+ double operator*(const Point_d& other) const {
+ assert(dimension() == other.dimension());
+ double res = 0;
+ for (unsigned i = 0; i < coords_.size(); ++i)
+ res += coords_[i] * other[i];
+ return res;
+ }
+
+ /**
+ * only if points have dimension 3
+ */
+ Point_d cross_product(const Point_d& other) {
+ assert(dimension() == 3 && other.dimension() == 3);
+ Point_d res(3);
+ res[0] = (*this)[1] * other[2] - (*this)[2] * other[1];
+ res[1] = (*this)[2] * other[0] - (*this)[0] * other[2];
+ res[2] = (*this)[0] * other[1] - (*this)[1] * other[0];
+ return res;
+ }
+
+ Point_d operator+(const Point_d& other) const {
+ assert(dimension() == other.dimension());
+ Point_d res(dimension());
+ for (unsigned i = 0; i < coords_.size(); ++i)
+ res[i] = (*this)[i] + other[i];
+ return res;
+ }
+
+ Point_d operator*(double lambda) const {
+ Point_d res(dimension());
+ for (unsigned i = 0; i < coords_.size(); ++i)
+ res[i] = (*this)[i] * lambda;
+ return res;
+ }
+
+ Point_d operator/(double lambda) const {
+ Point_d res(dimension());
+ for (unsigned i = 0; i < coords_.size(); ++i)
+ res[i] = (*this)[i] / lambda;
+ return res;
+ }
+
+ Point_d operator-(const Point_d& other) const {
+ assert(dimension() == other.dimension());
+ Point_d res(dimension());
+ for (unsigned i = 0; i < coords_.size(); ++i)
+ res[i] = (*this)[i] - other[i];
+ return res;
+ }
+
+ friend Point_d unit_normal(const Point_d& p1, const Point_d& p2, const Point_d& p3) {
+ assert(p1.dimension() == 3);
+ assert(p2.dimension() == 3);
+ assert(p3.dimension() == 3);
+ Point_d p1p2 = p2 - p1;
+ Point_d p1p3 = p3 - p1;
+ Point_d res(p1p2.cross_product(p1p3));
+ return res / std::sqrt(res.squared_norm());
+ }
+
+ private:
+ std::vector<double> coords_;
+};
+
+#endif // POINT_H_
diff --git a/include/gudhi/Points_3D_off_io.h b/include/gudhi/Points_3D_off_io.h
new file mode 100644
index 00000000..2647f11e
--- /dev/null
+++ b/include/gudhi/Points_3D_off_io.h
@@ -0,0 +1,202 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Vincent Rouvreau
+ *
+ * Copyright (C) 2015 INRIA Saclay (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef POINTS_3D_OFF_IO_H_
+#define POINTS_3D_OFF_IO_H_
+
+#include <gudhi/Off_reader.h>
+
+#include <string>
+#include <vector>
+#include <fstream>
+#include <map>
+
+namespace Gudhi {
+
+/**
+ * @brief OFF file visitor implementation according to Off_reader in order to read points from an OFF file.
+ */
+template<typename Point_3>
+class Points_3D_off_visitor_reader {
+ private:
+ std::vector<Point_3> point_cloud_;
+ bool valid_;
+
+ public:
+ /** @brief Off_reader visitor init implementation.
+ *
+ * The init parameters are set from OFF file header.
+ * Dimension value is required and the value must be 3.
+ *
+ * @param[in] dim space dimension of vertices.
+ * @param[in] num_vertices number of vertices in the OFF file (not used).
+ * @param[in] num_faces number of faces in the OFF file (not used).
+ * @param[in] num_edges number of edges in the OFF file (not used).
+ */
+ void init(int dim, int num_vertices, int num_faces, int num_edges) {
+#ifdef DEBUG_TRACES
+ std::cout << "Points_3D_off_visitor_reader::init - dim=" << dim << " - num_vertices=" <<
+ num_vertices << " - num_faces=" << num_faces << " - num_edges=" << num_edges << std::endl;
+#endif // DEBUG_TRACES
+ if (dim == 3) {
+ valid_ = true;
+ } else {
+ valid_ = false;
+ std::cerr << "Points_3D_off_reader::Points_3D_off_reader cannot read OFF files in dimension " << dim << "\n";
+ }
+
+ if (num_faces > 0) {
+ std::cerr << "Points_3D_off_visitor_reader::init faces are not taken into account from OFF file for Points.\n";
+ }
+ if (num_edges > 0) {
+ std::cerr << "Points_3D_off_visitor_reader::init edges are not taken into account from OFF file for Points.\n";
+ }
+ }
+
+ /** @brief Off_reader visitor point implementation.
+ *
+ * The point function is called on each vertex line from OFF file.
+ * This function inserts the vertex in the vector of points.
+ *
+ * @param[in] point vector of vertex coordinates.
+ *
+ * @details
+ * Point_3 must have a constructor with the following form:
+ *
+ * @code template<class InputIterator > Point_3::Point_3(double x, double y, double z) @endcode
+ */
+ void point(const std::vector<double>& point) {
+ if (valid_) {
+#ifdef DEBUG_TRACES
+ std::cout << "Points_3D_off_visitor_reader::point ";
+ for (auto coordinate : point) {
+ std::cout << coordinate << " | ";
+ }
+ std::cout << std::endl;
+#endif // DEBUG_TRACES
+ // Fill the point cloud
+ point_cloud_.push_back(Point_3(point[0], point[1], point[2]));
+ }
+ }
+
+ // Off_reader visitor maximal_face implementation - Only points are read
+
+ void maximal_face(const std::vector<int>& face) { }
+
+ // Off_reader visitor done implementation - Only points are read
+
+ void done() { }
+
+ /** @brief Point cloud getter.
+ *
+ * @return The point cloud.
+ */
+ const std::vector<Point_3>& get_point_cloud() const {
+ return point_cloud_;
+ }
+
+ /** @brief Returns if the OFF file read operation was successful or not.
+ *
+ * @return OFF file read status.
+ */
+ bool is_valid() const {
+ return valid_;
+ }
+};
+
+/**
+ * \@brief OFF file reader implementation in order to read dimension 3 points from an OFF file.
+ *
+ * @details
+ * This class is using the Points_3D_off_visitor_reader to visit the OFF file according to Off_reader.
+ *
+ * Point_3 must have a constructor with the following form:
+ *
+ * @code template<class InputIterator > Point_3::Point_3(double x, double y, double z) @endcode
+ *
+ * @section Example
+ *
+ * This example loads points from an OFF file and builds a vector of CGAL points in dimension 3.
+ * Then, it is asked to display the points.
+ *
+ * @include common/CGAL_3D_points_off_reader.cpp
+ *
+ * When launching:
+ *
+ * @code $> ./cgal3Doffreader ../../data/points/tore3D_300.off
+ * @endcode
+ *
+ * the program output is:
+ *
+ * @include common/cgal3Doffreader_result.txt
+ */
+template<typename Point_3>
+class Points_3D_off_reader {
+ public:
+ /** @brief Reads the OFF file and constructs a vector of points from the points
+ * that are in the OFF file.
+ *
+ * @param[in] name_file OFF file to read.
+ *
+ * @post Check with is_valid() function to see if read operation was successful.
+ */
+ Points_3D_off_reader(const std::string& name_file)
+ : valid_(false) {
+ std::ifstream stream(name_file);
+ if (stream.is_open()) {
+ Off_reader off_reader(stream);
+ Points_3D_off_visitor_reader<Point_3> off_visitor;
+ valid_ = off_reader.read(off_visitor);
+ valid_ = valid_ && off_visitor.is_valid();
+ if (valid_) {
+ point_cloud = off_visitor.get_point_cloud();
+ }
+ } else {
+ std::cerr << "Points_3D_off_reader::Points_3D_off_reader could not open file " << name_file << "\n";
+ }
+ }
+
+ /** @brief Returns if the OFF file read operation was successful or not.
+ *
+ * @return OFF file read status.
+ */
+ bool is_valid() const {
+ return valid_;
+ }
+
+ /** @brief Point cloud getter.
+ *
+ * @return point_cloud.
+ */
+ const std::vector<Point_3>& get_point_cloud() const {
+ return point_cloud;
+ }
+
+ private:
+ /** @brief point_cloud.*/
+ std::vector<Point_3> point_cloud;
+ /** @brief OFF file read status.*/
+ bool valid_;
+};
+
+} // namespace Gudhi
+
+#endif // POINTS_3D_OFF_IO_H_
diff --git a/include/gudhi/Points_off_io.h b/include/gudhi/Points_off_io.h
new file mode 100644
index 00000000..74b49386
--- /dev/null
+++ b/include/gudhi/Points_off_io.h
@@ -0,0 +1,184 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Vincent Rouvreau
+ *
+ * Copyright (C) 2015 INRIA Saclay (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef POINTS_OFF_IO_H_
+#define POINTS_OFF_IO_H_
+
+#include <gudhi/Off_reader.h>
+
+#include <string>
+#include <vector>
+#include <fstream>
+#include <map>
+
+namespace Gudhi {
+
+/**
+ * \brief OFF file visitor implementation according to Off_reader in order to read points from an OFF file.
+ */
+template<typename Point_d>
+class Points_off_visitor_reader {
+ private:
+ std::vector<Point_d> point_cloud;
+
+ public:
+ /** \brief Off_reader visitor init implementation.
+ *
+ * The init parameters are set from OFF file header.
+ * Dimension value is required in order to construct a vector of points.
+ *
+ * @param[in] dim space dimension of vertices.
+ * @param[in] num_vertices number of vertices in the OFF file (not used).
+ * @param[in] num_faces number of faces in the OFF file (not used).
+ * @param[in] num_edges number of edges in the OFF file (not used).
+ */
+ void init(int dim, int num_vertices, int num_faces, int num_edges) {
+#ifdef DEBUG_TRACES
+ std::cout << "Points_off_visitor_reader::init - dim=" << dim << " - num_vertices=" <<
+ num_vertices << " - num_faces=" << num_faces << " - num_edges=" << num_edges << std::endl;
+#endif // DEBUG_TRACES
+ if (num_faces > 0) {
+ std::cerr << "Points_off_visitor_reader::init faces are not taken into account from OFF file for Points.\n";
+ }
+ if (num_edges > 0) {
+ std::cerr << "Points_off_visitor_reader::init edges are not taken into account from OFF file for Points.\n";
+ }
+ }
+
+ /** @brief Off_reader visitor point implementation.
+ *
+ * The point function is called on each vertex line from OFF file.
+ * This function inserts the vertex in the vector of points.
+ *
+ * @param[in] point vector of vertex coordinates.
+ *
+ * @details
+ * Point_d must have a constructor with the following form:
+ *
+ * @code template<class InputIterator > Point_d::Point_d(int d, InputIterator first, InputIterator last) @endcode
+ *
+ * where d is the point dimension.
+ */
+ void point(const std::vector<double>& point) {
+#ifdef DEBUG_TRACES
+ std::cout << "Points_off_visitor_reader::point ";
+ for (auto coordinate : point) {
+ std::cout << coordinate << " | ";
+ }
+ std::cout << std::endl;
+#endif // DEBUG_TRACES
+ // Fill the point cloud
+ point_cloud.push_back(Point_d(point.size(), point.begin(), point.end()));
+ }
+
+ // Off_reader visitor maximal_face implementation - Only points are read
+ void maximal_face(const std::vector<int>& face) { }
+
+ // Off_reader visitor done implementation - Only points are read
+ void done() { }
+
+ /** \brief Point cloud getter.
+ *
+ * @return point_cloud.
+ */
+ const std::vector<Point_d>& get_point_cloud() const {
+ return point_cloud;
+ }
+};
+
+/**
+ * \brief OFF file reader implementation in order to read points from an OFF file.
+ *
+ * This class is using the Points_off_visitor_reader to visit the OFF file according to Off_reader.
+ *
+ * Point_d must have a constructor with the following form:
+ *
+ * \code template<class InputIterator > Point_d::Point_d(int d, InputIterator first, InputIterator last) \endcode
+ *
+ * where d is the point dimension.
+ *
+ * \section Example
+ *
+ * This example loads points from an OFF file and builds a vector of CGAL points in dimension d.
+ * Then, it is asked to display the points.
+ *
+ * \include common/CGAL_points_off_reader.cpp
+ *
+ * When launching:
+ *
+ * \code $> ./cgaloffreader ../../data/points/alphacomplexdoc.off
+ * \endcode
+ *
+ * the program output is:
+ *
+ * \include common/cgaloffreader_result.txt
+ */
+template<typename Point_d>
+class Points_off_reader {
+ public:
+ /** \brief Reads the OFF file and constructs a vector of points from the points
+ * that are in the OFF file.
+ *
+ * @param[in] name_file OFF file to read.
+ *
+ * \post Check with is_valid() function to see if read operation was successful.
+ */
+ Points_off_reader(const std::string& name_file)
+ : valid_(false) {
+ std::ifstream stream(name_file);
+ if (stream.is_open()) {
+ Off_reader off_reader(stream);
+ Points_off_visitor_reader<Point_d> off_visitor;
+ valid_ = off_reader.read(off_visitor);
+ if (valid_) {
+ point_cloud = off_visitor.get_point_cloud();
+ }
+ } else {
+ std::cerr << "Points_off_reader::Points_off_reader could not open file " << name_file << "\n";
+ }
+ }
+
+ /** \brief Returns if the OFF file read operation was successful or not.
+ *
+ * @return OFF file read status.
+ */
+ bool is_valid() const {
+ return valid_;
+ }
+
+ /** \brief Point cloud getter.
+ *
+ * @return point_cloud.
+ */
+ const std::vector<Point_d>& get_point_cloud() const {
+ return point_cloud;
+ }
+
+ private:
+ /** \brief point_cloud.*/
+ std::vector<Point_d> point_cloud;
+ /** \brief OFF file read status.*/
+ bool valid_;
+};
+
+} // namespace Gudhi
+
+#endif // POINTS_OFF_IO_H_
diff --git a/include/gudhi/Simple_object_pool.h b/include/gudhi/Simple_object_pool.h
new file mode 100644
index 00000000..fb9c8e23
--- /dev/null
+++ b/include/gudhi/Simple_object_pool.h
@@ -0,0 +1,81 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Marc Glisse
+ *
+ * Copyright (C) 2015 INRIA Saclay - Ile de France
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SIMPLE_OBJECT_POOL_H_
+#define SIMPLE_OBJECT_POOL_H_
+
+#include <boost/pool/pool.hpp>
+#include <utility>
+
+namespace Gudhi {
+
+/** \private
+ * This is a simpler version of boost::object_pool, that requires
+ * that users explicitly destroy all objects. This lets the
+ * performance scale much better, see
+ * https://svn.boost.org/trac/boost/ticket/3789 .
+ */
+template <class T>
+class Simple_object_pool : protected boost::pool<boost::default_user_allocator_malloc_free> {
+ protected:
+ typedef boost::pool<boost::default_user_allocator_malloc_free> Base;
+ typedef T* pointer;
+
+ Base& base() {
+ return *this;
+ }
+
+ Base const& base()const {
+ return *this;
+ }
+
+ public:
+ typedef T element_type;
+ typedef boost::default_user_allocator_malloc_free user_allocator;
+ typedef typename Base::size_type size_type;
+ typedef typename Base::difference_type difference_type;
+
+ template<class...U>
+ Simple_object_pool(U&&...u) : Base(sizeof (T), std::forward<U>(u)...) { }
+
+ template<class...U>
+ pointer construct(U&&...u) {
+ void* p = base().malloc BOOST_PREVENT_MACRO_SUBSTITUTION();
+ assert(p);
+ try {
+ new(p) T(std::forward<U>(u)...);
+ } catch (...) {
+ base().free BOOST_PREVENT_MACRO_SUBSTITUTION(p);
+ throw;
+ }
+ return static_cast<pointer> (p);
+ }
+
+ void destroy(pointer p) {
+ p->~T();
+ base().free BOOST_PREVENT_MACRO_SUBSTITUTION(p);
+ }
+};
+
+} // namespace Gudhi
+
+#endif // SIMPLE_OBJECT_POOL_H_
diff --git a/include/gudhi/Simplex_tree.h b/include/gudhi/Simplex_tree.h
new file mode 100644
index 00000000..63e3f0e5
--- /dev/null
+++ b/include/gudhi/Simplex_tree.h
@@ -0,0 +1,1303 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Clément Maria
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Méditerranée (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SIMPLEX_TREE_H_
+#define SIMPLEX_TREE_H_
+
+#include <gudhi/Simplex_tree/Simplex_tree_node_explicit_storage.h>
+#include <gudhi/Simplex_tree/Simplex_tree_siblings.h>
+#include <gudhi/Simplex_tree/Simplex_tree_iterators.h>
+#include <gudhi/Simplex_tree/indexing_tag.h>
+
+#include <gudhi/reader_utils.h>
+#include <gudhi/graph_simplicial_complex.h>
+#include <gudhi/Debug_utils.h>
+
+#include <boost/container/flat_map.hpp>
+#include <boost/iterator/transform_iterator.hpp>
+#include <boost/graph/adjacency_list.hpp>
+#include <boost/range/adaptor/reversed.hpp>
+
+#ifdef GUDHI_USE_TBB
+#include <tbb/parallel_sort.h>
+#endif
+
+#include <utility>
+#include <vector>
+#include <functional> // for greater<>
+#include <stdexcept>
+#include <limits> // Inf
+#include <initializer_list>
+#include <algorithm> // for std::max
+#include <cstdint> // for std::uint32_t
+
+namespace Gudhi {
+
+struct Simplex_tree_options_full_featured;
+
+/**
+ * \class Simplex_tree Simplex_tree.h gudhi/Simplex_tree.h
+ * \brief Simplex Tree data structure for representing simplicial complexes.
+ *
+ * \details Every simplex \f$[v_0, \cdots ,v_d]\f$ admits a canonical orientation
+ * induced by the order relation on vertices \f$ v_0 < \cdots < v_d \f$.
+ *
+ * Details may be found in \cite boissonnatmariasimplextreealgorithmica.
+ *
+ * \implements FilteredComplex
+ *
+ */
+
+template<typename SimplexTreeOptions = Simplex_tree_options_full_featured>
+class Simplex_tree {
+ public:
+ typedef SimplexTreeOptions Options;
+ typedef typename Options::Indexing_tag Indexing_tag;
+ /** \brief Type for the value of the filtration function.
+ *
+ * Must be comparable with <. */
+ typedef typename Options::Filtration_value Filtration_value;
+ /** \brief Key associated to each simplex.
+ *
+ * Must be an integer type. */
+ typedef typename Options::Simplex_key Simplex_key;
+ /** \brief Type for the vertex handle.
+ *
+ * Must be a signed integer type. It admits a total order <. */
+ typedef typename Options::Vertex_handle Vertex_handle;
+
+ /* Type of node in the simplex tree. */
+ typedef Simplex_tree_node_explicit_storage<Simplex_tree> Node;
+ /* Type of dictionary Vertex_handle -> Node for traversing the simplex tree. */
+ // Note: this wastes space when Vertex_handle is 32 bits and Node is aligned on 64 bits. It would be better to use a
+ // flat_set (with our own comparator) where we can control the layout of the struct (put Vertex_handle and
+ // Simplex_key next to each other).
+ typedef typename boost::container::flat_map<Vertex_handle, Node> Dictionary;
+
+ /* \brief Set of nodes sharing a same parent in the simplex tree. */
+ /* \brief Set of nodes sharing a same parent in the simplex tree. */
+ typedef Simplex_tree_siblings<Simplex_tree, Dictionary> Siblings;
+
+ struct Key_simplex_base_real {
+ Key_simplex_base_real() : key_(-1) {}
+ void assign_key(Simplex_key k) { key_ = k; }
+ Simplex_key key() const { return key_; }
+ private:
+ Simplex_key key_;
+ };
+ struct Key_simplex_base_dummy {
+ Key_simplex_base_dummy() {}
+ void assign_key(Simplex_key) { }
+ Simplex_key key() const { assert(false); return -1; }
+ };
+ typedef typename std::conditional<Options::store_key, Key_simplex_base_real, Key_simplex_base_dummy>::type
+ Key_simplex_base;
+
+ struct Filtration_simplex_base_real {
+ Filtration_simplex_base_real() : filt_(0) {}
+ void assign_filtration(Filtration_value f) { filt_ = f; }
+ Filtration_value filtration() const { return filt_; }
+ private:
+ Filtration_value filt_;
+ };
+ struct Filtration_simplex_base_dummy {
+ Filtration_simplex_base_dummy() {}
+ void assign_filtration(Filtration_value f) { assert(f == 0); }
+ Filtration_value filtration() const { return 0; }
+ };
+ typedef typename std::conditional<Options::store_filtration, Filtration_simplex_base_real,
+ Filtration_simplex_base_dummy>::type Filtration_simplex_base;
+
+ public:
+ /** \brief Handle type to a simplex contained in the simplicial complex represented
+ * by the simplex tree. */
+ typedef typename Dictionary::iterator Simplex_handle;
+
+ private:
+ typedef typename Dictionary::iterator Dictionary_it;
+ typedef typename Dictionary_it::value_type Dit_value_t;
+
+ struct return_first {
+ Vertex_handle operator()(const Dit_value_t& p_sh) const {
+ return p_sh.first;
+ }
+ };
+
+ public:
+ /** \name Range and iterator types
+ *
+ * The naming convention is Container_content_(iterator/range). A Container_content_range is
+ * essentially an object on which the methods begin() and end() can be called. They both return
+ * an object of type Container_content_iterator, and allow the traversal of the range
+ * [ begin();end() ).
+ * @{ */
+
+ /** \brief Iterator over the vertices of the simplicial complex.
+ *
+ * 'value_type' is Vertex_handle. */
+ typedef boost::transform_iterator<return_first, Dictionary_it> Complex_vertex_iterator;
+ /** \brief Range over the vertices of the simplicial complex. */
+ typedef boost::iterator_range<Complex_vertex_iterator> Complex_vertex_range;
+ /** \brief Iterator over the vertices of a simplex.
+ *
+ * 'value_type' is Vertex_handle. */
+ typedef Simplex_tree_simplex_vertex_iterator<Simplex_tree> Simplex_vertex_iterator;
+ /** \brief Range over the vertices of a simplex. */
+ typedef boost::iterator_range<Simplex_vertex_iterator> Simplex_vertex_range;
+ /** \brief Range over the cofaces of a simplex. */
+ typedef std::vector<Simplex_handle> Cofaces_simplex_range;
+ /** \brief Iterator over the simplices of the boundary of a simplex.
+ *
+ * 'value_type' is Simplex_handle. */
+ typedef Simplex_tree_boundary_simplex_iterator<Simplex_tree> Boundary_simplex_iterator;
+ /** \brief Range over the simplices of the boundary of a simplex. */
+ typedef boost::iterator_range<Boundary_simplex_iterator> Boundary_simplex_range;
+ /** \brief Iterator over the simplices of the simplicial complex.
+ *
+ * 'value_type' is Simplex_handle. */
+ typedef Simplex_tree_complex_simplex_iterator<Simplex_tree> Complex_simplex_iterator;
+ /** \brief Range over the simplices of the simplicial complex. */
+ typedef boost::iterator_range<Complex_simplex_iterator> Complex_simplex_range;
+ /** \brief Iterator over the simplices of the skeleton of the simplicial complex, for a given
+ * dimension.
+ *
+ * 'value_type' is Simplex_handle. */
+ typedef Simplex_tree_skeleton_simplex_iterator<Simplex_tree> Skeleton_simplex_iterator;
+ /** \brief Range over the simplices of the skeleton of the simplicial complex, for a given
+ * dimension. */
+ typedef boost::iterator_range<Skeleton_simplex_iterator> Skeleton_simplex_range;
+ /** \brief Range over the simplices of the simplicial complex, ordered by the filtration. */
+ typedef std::vector<Simplex_handle> Filtration_simplex_range;
+ /** \brief Iterator over the simplices of the simplicial complex, ordered by the filtration.
+ *
+ * 'value_type' is Simplex_handle. */
+ typedef typename Filtration_simplex_range::const_iterator Filtration_simplex_iterator;
+
+ /* @} */ // end name range and iterator types
+ /** \name Range and iterator methods
+ * @{ */
+
+ /** \brief Returns a range over the vertices of the simplicial complex.
+ * The order is increasing according to < on Vertex_handles.*/
+ Complex_vertex_range complex_vertex_range() {
+ return Complex_vertex_range(
+ boost::make_transform_iterator(root_.members_.begin(), return_first()),
+ boost::make_transform_iterator(root_.members_.end(), return_first()));
+ }
+
+ /** \brief Returns a range over the simplices of the simplicial complex.
+ *
+ * In the Simplex_tree, the tree is traverse in a depth-first fashion.
+ * Consequently, simplices are ordered according to lexicographic order on the list of
+ * Vertex_handles of a simplex, read in increasing < order for Vertex_handles. */
+ Complex_simplex_range complex_simplex_range() {
+ return Complex_simplex_range(Complex_simplex_iterator(this),
+ Complex_simplex_iterator());
+ }
+
+ /** \brief Returns a range over the simplices of the dim-skeleton of the simplicial complex.
+ *
+ * The \f$d\f$-skeleton of a simplicial complex \f$\mathbf{K}\f$ is the simplicial complex containing the
+ * simplices of \f$\mathbf{K}\f$ of dimension at most \f$d\f$.
+ *
+ * @param[in] dim The maximal dimension of the simplices in the skeleton.
+ *
+ * The simplices are ordered according to lexicographic order on the list of
+ * Vertex_handles of a simplex, read in increasing < order for Vertex_handles. */
+ Skeleton_simplex_range skeleton_simplex_range(int dim) {
+ return Skeleton_simplex_range(Skeleton_simplex_iterator(this, dim),
+ Skeleton_simplex_iterator());
+ }
+
+ /** \brief Returns a range over the simplices of the simplicial complex,
+ * in the order of the filtration.
+ *
+ * The filtration is a monotonic function \f$ f: \mathbf{K} \rightarrow \mathbb{R} \f$, i.e. if two simplices
+ * \f$\tau\f$ and \f$\sigma\f$ satisfy \f$\tau \subseteq \sigma\f$ then
+ * \f$f(\tau) \leq f(\sigma)\f$.
+ *
+ * The method returns simplices ordered according to increasing filtration values. Ties are
+ * resolved by considering inclusion relation (subsimplices appear before their cofaces). If two
+ * simplices have same filtration value but are not comparable w.r.t. inclusion, lexicographic
+ * order is used.
+ *
+ * The filtration must be valid. If the filtration has not been initialized yet, the
+ * method initializes it (i.e. order the simplices). If the complex has changed since the last time the filtration
+ * was initialized, please call `initialize_filtration()` to recompute it. */
+ Filtration_simplex_range const& filtration_simplex_range(Indexing_tag = Indexing_tag()) {
+ if (filtration_vect_.empty()) {
+ initialize_filtration();
+ }
+ return filtration_vect_;
+ }
+
+ /** \brief Returns a range over the vertices of a simplex.
+ *
+ * The order in which the vertices are visited is the decreasing order for < on Vertex_handles,
+ * which is consequenlty
+ * equal to \f$(-1)^{\text{dim} \sigma}\f$ the canonical orientation on the simplex.
+ */
+ Simplex_vertex_range simplex_vertex_range(Simplex_handle sh) {
+ assert(sh != null_simplex()); // Empty simplex
+ return Simplex_vertex_range(Simplex_vertex_iterator(this, sh),
+ Simplex_vertex_iterator(this));
+ }
+
+ /** \brief Returns a range over the simplices of the boundary of a simplex.
+ *
+ * The boundary of a simplex is the set of codimension \f$1\f$ subsimplices of the simplex.
+ * If the simplex is \f$[v_0, \cdots ,v_d]\f$, with canonical orientation
+ * induced by \f$ v_0 < \cdots < v_d \f$, the iterator enumerates the
+ * simplices of the boundary in the order:
+ * \f$[v_0,\cdots,\widehat{v_i},\cdots,v_d]\f$ for \f$i\f$ from \f$0\f$ to \f$d\f$,
+ * where \f$\widehat{v_i}\f$ means that the vertex \f$v_i\f$ is omitted.
+ *
+ * We note that the alternate sum of the simplices given by the iterator
+ * gives \f$(-1)^{\text{dim} \sigma}\f$ the chains corresponding to the boundary
+ * of the simplex.
+ *
+ * @param[in] sh Simplex for which the boundary is computed. */
+ template<class SimplexHandle>
+ Boundary_simplex_range boundary_simplex_range(SimplexHandle sh) {
+ return Boundary_simplex_range(Boundary_simplex_iterator(this, sh),
+ Boundary_simplex_iterator(this));
+ }
+
+ /** @} */ // end range and iterator methods
+ /** \name Constructor/Destructor
+ * @{ */
+
+ /** \brief Constructs an empty simplex tree. */
+ Simplex_tree()
+ : null_vertex_(-1),
+ threshold_(0),
+ root_(nullptr, null_vertex_),
+ filtration_vect_(),
+ dimension_(-1) { }
+
+ /** \brief User-defined copy constructor reproduces the whole tree structure. */
+ Simplex_tree(const Simplex_tree& simplex_source)
+ : null_vertex_(simplex_source.null_vertex_),
+ threshold_(simplex_source.threshold_),
+ root_(nullptr, null_vertex_ , simplex_source.root_.members_),
+ filtration_vect_(),
+ dimension_(simplex_source.dimension_) {
+ auto root_source = simplex_source.root_;
+ rec_copy(&root_, &root_source);
+ }
+
+ /** \brief depth first search, inserts simplices when reaching a leaf. */
+ void rec_copy(Siblings *sib, Siblings *sib_source) {
+ for (auto sh = sib->members().begin(), sh_source = sib_source->members().begin();
+ sh != sib->members().end(); ++sh, ++sh_source) {
+ if (has_children(sh_source)) {
+ Siblings * newsib = new Siblings(sib, sh_source->first);
+ newsib->members_.reserve(sh_source->second.children()->members().size());
+ for (auto & child : sh_source->second.children()->members())
+ newsib->members_.emplace_hint(newsib->members_.end(), child.first, Node(newsib, child.second.filtration()));
+ rec_copy(newsib, sh_source->second.children());
+ sh->second.assign_children(newsib);
+ }
+ }
+ }
+
+ /** \brief User-defined move constructor moves the whole tree structure. */
+ Simplex_tree(Simplex_tree && old)
+ : null_vertex_(std::move(old.null_vertex_)),
+ threshold_(std::move(old.threshold_)),
+ root_(std::move(old.root_)),
+ filtration_vect_(std::move(old.filtration_vect_)),
+ dimension_(std::move(old.dimension_)) {
+ old.dimension_ = -1;
+ old.threshold_ = 0;
+ old.root_ = Siblings(nullptr, null_vertex_);
+ }
+
+ /** \brief Destructor; deallocates the whole tree structure. */
+ ~Simplex_tree() {
+ for (auto sh = root_.members().begin(); sh != root_.members().end(); ++sh) {
+ if (has_children(sh)) {
+ rec_delete(sh->second.children());
+ }
+ }
+ }
+ /** @} */ // end constructor/destructor
+ private:
+ // Recursive deletion
+ void rec_delete(Siblings * sib) {
+ for (auto sh = sib->members().begin(); sh != sib->members().end(); ++sh) {
+ if (has_children(sh)) {
+ rec_delete(sh->second.children());
+ }
+ }
+ delete sib;
+ }
+
+ public:
+ /** \brief Checks if two simplex trees are equal. */
+ bool operator==(Simplex_tree& st2) {
+ if ((null_vertex_ != st2.null_vertex_) ||
+ (threshold_ != st2.threshold_) ||
+ (dimension_ != st2.dimension_))
+ return false;
+ return rec_equal(&root_, &st2.root_);
+ }
+
+ /** \brief Checks if two simplex trees are different. */
+ bool operator!=(Simplex_tree& st2) {
+ return (!(*this == st2));
+ }
+
+ private:
+ /** rec_equal: Checks recursively whether or not two simplex trees are equal, using depth first search. */
+ bool rec_equal(Siblings* s1, Siblings* s2) {
+ if (s1->members().size() != s2->members().size())
+ return false;
+ for (auto sh1 = s1->members().begin(), sh2 = s2->members().begin();
+ (sh1 != s1->members().end() && sh2 != s2->members().end()); ++sh1, ++sh2) {
+ if (sh1->first != sh2->first || sh1->second.filtration() != sh2->second.filtration())
+ return false;
+ if (has_children(sh1) != has_children(sh2))
+ return false;
+ // Recursivity on children only if both have children
+ else if (has_children(sh1))
+ if (!rec_equal(sh1->second.children(), sh2->second.children()))
+ return false;
+ }
+ return true;
+ }
+
+ public:
+ /** \brief Returns the key associated to a simplex.
+ *
+ * The filtration must be initialized.
+ * \pre SimplexTreeOptions::store_key
+ */
+ static Simplex_key key(Simplex_handle sh) {
+ return sh->second.key();
+ }
+
+ /** \brief Returns the simplex associated to a key.
+ *
+ * The filtration must be initialized.
+ * \pre SimplexTreeOptions::store_key
+ */
+ Simplex_handle simplex(Simplex_key key) const {
+ return filtration_vect_[key];
+ }
+
+ /** \brief Returns the filtration value of a simplex.
+ *
+ * Called on the null_simplex, returns INFINITY.
+ * If SimplexTreeOptions::store_filtration is false, returns 0.
+ */
+ static Filtration_value filtration(Simplex_handle sh) {
+ if (sh != null_simplex()) {
+ return sh->second.filtration();
+ } else {
+ return INFINITY;
+ }
+ }
+
+ /** \brief Sets the filtration value of a simplex.
+ * \exception std::invalid_argument In debug mode, if sh is a null_simplex.
+ */
+ void assign_filtration(Simplex_handle sh, Filtration_value fv) {
+ GUDHI_CHECK(sh != null_simplex(),
+ std::invalid_argument("Simplex_tree::assign_filtration - cannot assign filtration on null_simplex"));
+ sh->second.assign_filtration(fv);
+ }
+
+ /** \brief Returns an upper bound of the filtration values of the simplices. */
+ Filtration_value filtration() const {
+ return threshold_;
+ }
+
+ /** \brief Returns a Simplex_handle different from all Simplex_handles
+ * associated to the simplices in the simplicial complex.
+ *
+ * One can call filtration(null_simplex()). */
+ static Simplex_handle null_simplex() {
+ return Dictionary_it(nullptr);
+ }
+
+ /** \brief Returns a key different for all keys associated to the
+ * simplices of the simplicial complex. */
+ static Simplex_key null_key() {
+ return -1;
+ }
+
+ /** \brief Returns a Vertex_handle different from all Vertex_handles associated
+ * to the vertices of the simplicial complex. */
+ Vertex_handle null_vertex() const {
+ return null_vertex_;
+ }
+
+ /** \brief Returns the number of vertices in the complex. */
+ size_t num_vertices() const {
+ return root_.members_.size();
+ }
+
+ public:
+ /** \brief returns the number of simplices in the simplex_tree. */
+ size_t num_simplices() {
+ return num_simplices(&root_);
+ }
+
+ private:
+ /** \brief returns the number of simplices in the simplex_tree. */
+ size_t num_simplices(Siblings * sib) {
+ auto sib_begin = sib->members().begin();
+ auto sib_end = sib->members().end();
+ size_t simplices_number = sib_end - sib_begin;
+ for (auto sh = sib_begin; sh != sib_end; ++sh) {
+ if (has_children(sh)) {
+ simplices_number += num_simplices(sh->second.children());
+ }
+ }
+ return simplices_number;
+ }
+
+ public:
+ /** \brief Returns the dimension of a simplex.
+ *
+ * Must be different from null_simplex().*/
+ int dimension(Simplex_handle sh) {
+ Siblings * curr_sib = self_siblings(sh);
+ int dim = 0;
+ while (curr_sib != nullptr) {
+ ++dim;
+ curr_sib = curr_sib->oncles();
+ }
+ return dim - 1;
+ }
+
+ /** \brief Returns an upper bound on the dimension of the simplicial complex. */
+ int dimension() const {
+ return dimension_;
+ }
+
+ /** \brief Returns true if the node in the simplex tree pointed by
+ * sh has children.*/
+ template<class SimplexHandle>
+ bool has_children(SimplexHandle sh) const {
+ return (sh->second.children()->parent() == sh->first);
+ }
+
+ /** \brief Given a range of Vertex_handles, returns the Simplex_handle
+ * of the simplex in the simplicial complex containing the corresponding
+ * vertices. Return null_simplex() if the simplex is not in the complex.
+ *
+ * The type InputVertexRange must be a range of <CODE>Vertex_handle</CODE>
+ * on which we can call std::begin() function
+ */
+ template<class InputVertexRange = std::initializer_list<Vertex_handle>>
+ Simplex_handle find(const InputVertexRange & s) {
+ auto first = std::begin(s);
+ auto last = std::end(s);
+
+ if (first == last)
+ return null_simplex(); // ----->>
+
+ // Copy before sorting
+ std::vector<Vertex_handle> copy(first, last);
+ std::sort(std::begin(copy), std::end(copy));
+ return find_simplex(copy);
+ }
+
+ private:
+ /** Find function, with a sorted range of vertices. */
+ Simplex_handle find_simplex(const std::vector<Vertex_handle> & simplex) {
+ Siblings * tmp_sib = &root_;
+ Dictionary_it tmp_dit;
+ Vertex_handle last = simplex.back();
+ for (auto v : simplex) {
+ tmp_dit = tmp_sib->members_.find(v);
+ if (tmp_dit == tmp_sib->members_.end()) {
+ return null_simplex();
+ }
+ if (!has_children(tmp_dit) && v != last) {
+ return null_simplex();
+ }
+ tmp_sib = tmp_dit->second.children();
+ }
+ return tmp_dit;
+ }
+
+ /** \brief Returns the Simplex_handle corresponding to the 0-simplex
+ * representing the vertex with Vertex_handle v. */
+ Simplex_handle find_vertex(Vertex_handle v) {
+ if (Options::contiguous_vertices) {
+ assert(contiguous_vertices());
+ return root_.members_.begin() + v;
+ } else {
+ return root_.members_.find(v);
+ }
+ }
+
+ public:
+ /** \private \brief Test if the vertices have contiguous numbering: 0, 1, etc. */
+ bool contiguous_vertices() const {
+ if (root_.members_.empty()) return true;
+ if (root_.members_.begin()->first != 0) return false;
+ if (std::prev(root_.members_.end())->first != static_cast<Vertex_handle>(root_.members_.size() - 1)) return false;
+ return true;
+ }
+
+ private:
+ /** \brief Inserts a simplex represented by a vector of vertex.
+ * @param[in] simplex vector of Vertex_handles, representing the vertices of the new simplex. The vector must be
+ * sorted by increasing vertex handle order.
+ * @param[in] filtration the filtration value assigned to the new simplex.
+ * @return If the new simplex is inserted successfully (i.e. it was not in the
+ * simplicial complex yet) the bool is set to true and the Simplex_handle is the handle assigned
+ * to the new simplex.
+ * If the insertion fails (the simplex is already there), the bool is set to false. If the insertion
+ * fails and the simplex already in the complex has a filtration value strictly bigger than 'filtration',
+ * we assign this simplex with the new value 'filtration', and set the Simplex_handle field of the
+ * output pair to the Simplex_handle of the simplex. Otherwise, we set the Simplex_handle part to
+ * null_simplex.
+ *
+ */
+ std::pair<Simplex_handle, bool> insert_vertex_vector(const std::vector<Vertex_handle>& simplex,
+ Filtration_value filtration) {
+ Siblings * curr_sib = &root_;
+ std::pair<Simplex_handle, bool> res_insert;
+ auto vi = simplex.begin();
+ for (; vi != simplex.end() - 1; ++vi) {
+ res_insert = curr_sib->members_.emplace(*vi, Node(curr_sib, filtration));
+ if (!(has_children(res_insert.first))) {
+ res_insert.first->second.assign_children(new Siblings(curr_sib, *vi));
+ }
+ curr_sib = res_insert.first->second.children();
+ }
+ res_insert = curr_sib->members_.emplace(*vi, Node(curr_sib, filtration));
+ if (!res_insert.second) {
+ // if already in the complex
+ if (res_insert.first->second.filtration() > filtration) {
+ // if filtration value modified
+ res_insert.first->second.assign_filtration(filtration);
+ return res_insert;
+ }
+ // if filtration value unchanged
+ return std::pair<Simplex_handle, bool>(null_simplex(), false);
+ }
+ // otherwise the insertion has succeeded
+ return res_insert;
+ }
+
+ public:
+ /** \brief Insert a simplex, represented by a range of Vertex_handles, in the simplicial complex.
+ *
+ * @param[in] simplex range of Vertex_handles, representing the vertices of the new simplex
+ * @param[in] filtration the filtration value assigned to the new simplex.
+ * @return If the new simplex is inserted successfully (i.e. it was not in the
+ * simplicial complex yet) the bool is set to true and the Simplex_handle is the handle assigned
+ * to the new simplex.
+ * If the insertion fails (the simplex is already there), the bool is set to false. If the insertion
+ * fails and the simplex already in the complex has a filtration value strictly bigger than 'filtration',
+ * we assign this simplex with the new value 'filtration', and set the Simplex_handle field of the
+ * output pair to the Simplex_handle of the simplex. Otherwise, we set the Simplex_handle part to
+ * null_simplex.
+ *
+ * All subsimplices do not necessary need to be already in the simplex tree to proceed to an
+ * insertion. However, the property of being a simplicial complex will be violated. This allows
+ * us to insert a stream of simplices contained in a simplicial complex without considering any
+ * order on them.
+ *
+ * The filtration value
+ * assigned to the new simplex must preserve the monotonicity of the filtration.
+ *
+ * The type InputVertexRange must be a range for which .begin() and
+ * .end() return input iterators, with 'value_type' Vertex_handle. */
+ template<class InputVertexRange = std::initializer_list<Vertex_handle>>
+ std::pair<Simplex_handle, bool> insert_simplex(const InputVertexRange & simplex,
+ Filtration_value filtration = 0) {
+ auto first = std::begin(simplex);
+ auto last = std::end(simplex);
+
+ if (first == last)
+ return std::pair<Simplex_handle, bool>(null_simplex(), true); // ----->>
+
+ // Copy before sorting
+ std::vector<Vertex_handle> copy(first, last);
+ std::sort(std::begin(copy), std::end(copy));
+ return insert_vertex_vector(copy, filtration);
+ }
+
+ /** \brief Insert a N-simplex and all his subfaces, from a N-simplex represented by a range of
+ * Vertex_handles, in the simplicial complex.
+ *
+ * @param[in] Nsimplex range of Vertex_handles, representing the vertices of the new N-simplex
+ * @param[in] filtration the filtration value assigned to the new N-simplex.
+ * @return If the new simplex is inserted successfully (i.e. it was not in the
+ * simplicial complex yet) the bool is set to true and the Simplex_handle is the handle assigned
+ * to the new simplex.
+ * If the insertion fails (the simplex is already there), the bool is set to false. If the insertion
+ * fails and the simplex already in the complex has a filtration value strictly bigger than 'filtration',
+ * we assign this simplex with the new value 'filtration', and set the Simplex_handle field of the
+ * output pair to the Simplex_handle of the simplex. Otherwise, we set the Simplex_handle part to
+ * null_simplex.
+ */
+ template<class InputVertexRange = std::initializer_list<Vertex_handle>>
+ std::pair<Simplex_handle, bool> insert_simplex_and_subfaces(const InputVertexRange& Nsimplex,
+ Filtration_value filtration = 0) {
+ auto first = std::begin(Nsimplex);
+ auto last = std::end(Nsimplex);
+
+ if (first == last)
+ return std::pair<Simplex_handle, bool>(null_simplex(), true); // ----->>
+
+ // Copy before sorting
+ std::vector<Vertex_handle> copy(first, last);
+ std::sort(std::begin(copy), std::end(copy));
+
+ std::vector<std::vector<Vertex_handle>> to_be_inserted;
+ std::vector<std::vector<Vertex_handle>> to_be_propagated;
+ return rec_insert_simplex_and_subfaces(copy, to_be_inserted, to_be_propagated, filtration);
+ }
+
+ private:
+ std::pair<Simplex_handle, bool> rec_insert_simplex_and_subfaces(std::vector<Vertex_handle>& the_simplex,
+ std::vector<std::vector<Vertex_handle>>& to_be_inserted,
+ std::vector<std::vector<Vertex_handle>>& to_be_propagated,
+ Filtration_value filtration = 0.0) {
+ std::pair<Simplex_handle, bool> insert_result;
+ if (the_simplex.size() > 1) {
+ // Get and remove last vertex
+ Vertex_handle last_vertex = the_simplex.back();
+ the_simplex.pop_back();
+ // Recursive call after last vertex removal
+ insert_result = rec_insert_simplex_and_subfaces(the_simplex, to_be_inserted, to_be_propagated, filtration);
+
+ // Concatenation of to_be_inserted and to_be_propagated
+ to_be_inserted.insert(to_be_inserted.begin(), to_be_propagated.begin(), to_be_propagated.end());
+ to_be_propagated = to_be_inserted;
+
+ // to_be_inserted treatment
+ for (auto& simplex_tbi : to_be_inserted) {
+ simplex_tbi.push_back(last_vertex);
+ }
+ std::vector<Vertex_handle> last_simplex(1, last_vertex);
+ to_be_inserted.insert(to_be_inserted.begin(), last_simplex);
+ // i.e. (0,1,2) =>
+ // [to_be_inserted | to_be_propagated] = [(1) (0,1) | (0)]
+ // [to_be_inserted | to_be_propagated] = [(2) (0,2) (1,2) (0,1,2) | (0) (1) (0,1)]
+ // N.B. : it is important the last inserted to be the highest in dimension
+ // in order to return the "last" insert_simplex result
+
+ // insert all to_be_inserted
+ for (auto& simplex_tbi : to_be_inserted) {
+ insert_result = insert_vertex_vector(simplex_tbi, filtration);
+ }
+ } else if (the_simplex.size() == 1) {
+ // When reaching the end of recursivity, vector of simplices shall be empty and filled on back recursive
+ if ((to_be_inserted.size() != 0) || (to_be_propagated.size() != 0)) {
+ std::cerr << "Simplex_tree::rec_insert_simplex_and_subfaces - Error vector not empty\n";
+ exit(-1);
+ }
+ std::vector<Vertex_handle> first_simplex(1, the_simplex.back());
+ // i.e. (0,1,2) => [to_be_inserted | to_be_propagated] = [(0) | ]
+ to_be_inserted.push_back(first_simplex);
+
+ insert_result = insert_vertex_vector(first_simplex, filtration);
+ } else {
+ std::cerr << "Simplex_tree::rec_insert_simplex_and_subfaces - Recursivity error\n";
+ exit(-1);
+ }
+ return insert_result;
+ }
+
+ public:
+ /** \brief Assign a value 'key' to the key of the simplex
+ * represented by the Simplex_handle 'sh'. */
+ void assign_key(Simplex_handle sh, Simplex_key key) {
+ sh->second.assign_key(key);
+ }
+
+ /** Returns the two Simplex_handle corresponding to the endpoints of
+ * and edge. sh must point to a 1-dimensional simplex. This is an
+ * optimized version of the boundary computation. */
+ std::pair<Simplex_handle, Simplex_handle> endpoints(Simplex_handle sh) {
+ assert(dimension(sh) == 1);
+ return { find_vertex(sh->first), find_vertex(self_siblings(sh)->parent()) };
+ }
+
+ /** Returns the Siblings containing a simplex.*/
+ template<class SimplexHandle>
+ Siblings* self_siblings(SimplexHandle sh) {
+ if (sh->second.children()->parent() == sh->first)
+ return sh->second.children()->oncles();
+ else
+ return sh->second.children();
+ }
+
+ public:
+ /** Returns a pointer to the root nodes of the simplex tree. */
+ Siblings * root() {
+ return &root_;
+ }
+
+ /** Set an upper bound for the filtration values. */
+ void set_filtration(Filtration_value fil) {
+ threshold_ = fil;
+ }
+
+ /** Set a dimension for the simplicial complex. */
+ void set_dimension(int dimension) {
+ dimension_ = dimension;
+ }
+
+ public:
+ /** \brief Initializes the filtrations, i.e. sort the
+ * simplices according to their order in the filtration and initializes all Simplex_keys.
+ *
+ * After calling this method, filtration_simplex_range() becomes valid, and each simplex is
+ * assigned a Simplex_key corresponding to its order in the filtration (from 0 to m-1 for a
+ * simplicial complex with m simplices).
+ *
+ * Will be automatically called when calling filtration_simplex_range()
+ * if the filtration has never been initialized yet. */
+ void initialize_filtration() {
+ filtration_vect_.clear();
+ filtration_vect_.reserve(num_simplices());
+ for (Simplex_handle sh : complex_simplex_range())
+ filtration_vect_.push_back(sh);
+
+ /* We use stable_sort here because with libstdc++ it is faster than sort.
+ * is_before_in_filtration is now a total order, but we used to call
+ * stable_sort for the following heuristic:
+ * The use of a depth-first traversal of the simplex tree, provided by
+ * complex_simplex_range(), combined with a stable sort is meant to
+ * optimize the order of simplices with same filtration value. The
+ * heuristic consists in inserting the cofaces of a simplex as soon as
+ * possible.
+ */
+#ifdef GUDHI_USE_TBB
+ tbb::parallel_sort(filtration_vect_.begin(), filtration_vect_.end(), is_before_in_filtration(this));
+#else
+ std::stable_sort(filtration_vect_.begin(), filtration_vect_.end(), is_before_in_filtration(this));
+#endif
+ }
+
+ private:
+ /** Recursive search of cofaces
+ * This function uses DFS
+ *\param vertices contains a list of vertices, which represent the vertices of the simplex not found yet.
+ *\param curr_nbVertices represents the number of vertices of the simplex we reached by going through the tree.
+ *\param cofaces contains a list of Simplex_handle, representing all the cofaces asked.
+ *\param star true if we need the star of the simplex
+ *\param nbVertices number of vertices of the cofaces we search
+ * Prefix actions : When the bottom vertex matches with the current vertex in the tree, we remove the bottom vertex from vertices.
+ * Infix actions : Then we call or not the recursion.
+ * Postfix actions : Finally, we add back the removed vertex into vertices, and remove this vertex from curr_nbVertices so that we didn't change the parameters.
+ * If the vertices list is empty, we need to check if curr_nbVertices matches with the dimension of the cofaces asked.
+ */
+ void rec_coface(std::vector<Vertex_handle> &vertices, Siblings *curr_sib, int curr_nbVertices,
+ std::vector<Simplex_handle>& cofaces, bool star, int nbVertices) {
+ if (!(star || curr_nbVertices <= nbVertices)) // dimension of actual simplex <= nbVertices
+ return;
+ for (Simplex_handle simplex = curr_sib->members().begin(); simplex != curr_sib->members().end(); ++simplex) {
+ if (vertices.empty()) {
+ // If we reached the end of the vertices, and the simplex has more vertices than the given simplex
+ // => we found a coface
+
+ // Add a coface if we wan't the star or if the number of vertices of the current simplex matches with nbVertices
+ bool addCoface = (star || curr_nbVertices == nbVertices);
+ if (addCoface)
+ cofaces.push_back(simplex);
+ if ((!addCoface || star) && has_children(simplex)) // Rec call
+ rec_coface(vertices, simplex->second.children(), curr_nbVertices + 1, cofaces, star, nbVertices);
+ } else {
+ if (simplex->first == vertices.back()) {
+ // If curr_sib matches with the top vertex
+ bool equalDim = (star || curr_nbVertices == nbVertices); // dimension of actual simplex == nbVertices
+ bool addCoface = vertices.size() == 1 && equalDim;
+ if (addCoface)
+ cofaces.push_back(simplex);
+ if ((!addCoface || star) && has_children(simplex)) {
+ // Rec call
+ Vertex_handle tmp = vertices.back();
+ vertices.pop_back();
+ rec_coface(vertices, simplex->second.children(), curr_nbVertices + 1, cofaces, star, nbVertices);
+ vertices.push_back(tmp);
+ }
+ } else if (simplex->first > vertices.back()) {
+ return;
+ } else {
+ // (simplex->first < vertices.back()
+ if (has_children(simplex))
+ rec_coface(vertices, simplex->second.children(), curr_nbVertices + 1, cofaces, star, nbVertices);
+ }
+ }
+ }
+ }
+
+ public:
+ /** \brief Compute the star of a n simplex
+ * \param simplex represent the simplex of which we search the star
+ * \return Vector of Simplex_handle, empty vector if no cofaces found.
+ */
+
+ Cofaces_simplex_range star_simplex_range(const Simplex_handle simplex) {
+ return cofaces_simplex_range(simplex, 0);
+ }
+
+ /** \brief Compute the cofaces of a n simplex
+ * \param simplex represent the n-simplex of which we search the n+codimension cofaces
+ * \param codimension The function returns the n+codimension-cofaces of the n-simplex. If codimension = 0,
+ * return all cofaces (equivalent of star function)
+ * \return Vector of Simplex_handle, empty vector if no cofaces found.
+ */
+
+ Cofaces_simplex_range cofaces_simplex_range(const Simplex_handle simplex, int codimension) {
+ Cofaces_simplex_range cofaces;
+ // codimension must be positive or null integer
+ assert(codimension >= 0);
+ Simplex_vertex_range rg = simplex_vertex_range(simplex);
+ std::vector<Vertex_handle> copy(rg.begin(), rg.end());
+ if (codimension + static_cast<int>(copy.size()) > dimension_ + 1 ||
+ (codimension == 0 && static_cast<int>(copy.size()) > dimension_)) // n+codimension greater than dimension_
+ return cofaces;
+ // must be sorted in decreasing order
+ assert(std::is_sorted(copy.begin(), copy.end(), std::greater<Vertex_handle>()));
+ bool star = codimension == 0;
+ rec_coface(copy, &root_, 1, cofaces, star, codimension + static_cast<int>(copy.size()));
+ return cofaces;
+ }
+
+ private:
+ /** \brief Returns true iff the list of vertices of sh1
+ * is smaller than the list of vertices of sh2 w.r.t.
+ * lexicographic order on the lists read in reverse.
+ *
+ * It defines a StrictWeakOrdering on simplices. The Simplex_vertex_iterators
+ * must traverse the Vertex_handle in decreasing order. Reverse lexicographic order satisfy
+ * the property that a subsimplex of a simplex is always strictly smaller with this order. */
+ bool reverse_lexicographic_order(Simplex_handle sh1, Simplex_handle sh2) {
+ Simplex_vertex_range rg1 = simplex_vertex_range(sh1);
+ Simplex_vertex_range rg2 = simplex_vertex_range(sh2);
+ Simplex_vertex_iterator it1 = rg1.begin();
+ Simplex_vertex_iterator it2 = rg2.begin();
+ while (it1 != rg1.end() && it2 != rg2.end()) {
+ if (*it1 == *it2) {
+ ++it1;
+ ++it2;
+ } else {
+ return *it1 < *it2;
+ }
+ }
+ return ((it1 == rg1.end()) && (it2 != rg2.end()));
+ }
+
+ /** \brief StrictWeakOrdering, for the simplices, defined by the filtration.
+ *
+ * It corresponds to the partial order
+ * induced by the filtration values, with ties resolved using reverse lexicographic order.
+ * Reverse lexicographic order has the property to always consider the subsimplex of a simplex
+ * to be smaller. The filtration function must be monotonic. */
+ struct is_before_in_filtration {
+ explicit is_before_in_filtration(Simplex_tree * st)
+ : st_(st) { }
+
+ bool operator()(const Simplex_handle sh1, const Simplex_handle sh2) const {
+ // Not using st_->filtration(sh1) because it uselessly tests for null_simplex.
+ if (sh1->second.filtration() != sh2->second.filtration()) {
+ return sh1->second.filtration() < sh2->second.filtration();
+ }
+ // is sh1 a proper subface of sh2
+ return st_->reverse_lexicographic_order(sh1, sh2);
+ }
+
+ Simplex_tree * st_;
+ };
+
+ public:
+ /** \brief Inserts a 1-skeleton in an empty Simplex_tree.
+ *
+ * The Simplex_tree must contain no simplex when the method is
+ * called.
+ *
+ * Inserts all vertices and edges given by a OneSkeletonGraph.
+ * OneSkeletonGraph must be a model of boost::AdjacencyGraph,
+ * boost::EdgeListGraph and boost::PropertyGraph.
+ *
+ * The vertex filtration value is accessible through the property tag
+ * vertex_filtration_t.
+ * The edge filtration value is accessible through the property tag
+ * edge_filtration_t.
+ *
+ * boost::graph_traits<OneSkeletonGraph>::vertex_descriptor
+ * must be Vertex_handle.
+ * boost::graph_traits<OneSkeletonGraph>::directed_category
+ * must be undirected_tag. */
+ template<class OneSkeletonGraph>
+ void insert_graph(const OneSkeletonGraph& skel_graph) {
+ // the simplex tree must be empty
+ assert(num_simplices() == 0);
+
+ if (boost::num_vertices(skel_graph) == 0) {
+ return;
+ }
+ if (num_edges(skel_graph) == 0) {
+ dimension_ = 0;
+ } else {
+ dimension_ = 1;
+ }
+
+ root_.members_.reserve(boost::num_vertices(skel_graph));
+
+ typename boost::graph_traits<OneSkeletonGraph>::vertex_iterator v_it,
+ v_it_end;
+ for (std::tie(v_it, v_it_end) = boost::vertices(skel_graph); v_it != v_it_end;
+ ++v_it) {
+ root_.members_.emplace_hint(
+ root_.members_.end(), *v_it,
+ Node(&root_, boost::get(vertex_filtration_t(), skel_graph, *v_it)));
+ }
+ typename boost::graph_traits<OneSkeletonGraph>::edge_iterator e_it,
+ e_it_end;
+ for (std::tie(e_it, e_it_end) = boost::edges(skel_graph); e_it != e_it_end;
+ ++e_it) {
+ auto u = source(*e_it, skel_graph);
+ auto v = target(*e_it, skel_graph);
+ if (u < v) {
+ // count edges only once { std::swap(u,v); } // u < v
+ auto sh = find_vertex(u);
+ if (!has_children(sh)) {
+ sh->second.assign_children(new Siblings(&root_, sh->first));
+ }
+
+ sh->second.children()->members().emplace(
+ v,
+ Node(sh->second.children(),
+ boost::get(edge_filtration_t(), skel_graph, *e_it)));
+ }
+ }
+ }
+
+ /** \brief Expands the Simplex_tree containing only its one skeleton
+ * until dimension max_dim.
+ *
+ * The expanded simplicial complex until dimension \f$d\f$
+ * attached to a graph \f$G\f$ is the maximal simplicial complex of
+ * dimension at most \f$d\f$ admitting the graph \f$G\f$ as \f$1\f$-skeleton.
+ * The filtration value assigned to a simplex is the maximal filtration
+ * value of one of its edges.
+ *
+ * The Simplex_tree must contain no simplex of dimension bigger than
+ * 1 when calling the method. */
+ void expansion(int max_dim) {
+ dimension_ = max_dim;
+ for (Dictionary_it root_it = root_.members_.begin();
+ root_it != root_.members_.end(); ++root_it) {
+ if (has_children(root_it)) {
+ siblings_expansion(root_it->second.children(), max_dim - 1);
+ }
+ }
+ dimension_ = max_dim - dimension_;
+ }
+
+ private:
+ /** \brief Recursive expansion of the simplex tree.*/
+ void siblings_expansion(Siblings * siblings, // must contain elements
+ int k) {
+ if (dimension_ > k) {
+ dimension_ = k;
+ }
+ if (k == 0)
+ return;
+ Dictionary_it next = siblings->members().begin();
+ ++next;
+
+ static std::vector<std::pair<Vertex_handle, Node> > inter; // static, not thread-safe.
+ for (Dictionary_it s_h = siblings->members().begin();
+ s_h != siblings->members().end(); ++s_h, ++next) {
+ Simplex_handle root_sh = find_vertex(s_h->first);
+ if (has_children(root_sh)) {
+ intersection(
+ inter, // output intersection
+ next, // begin
+ siblings->members().end(), // end
+ root_sh->second.children()->members().begin(),
+ root_sh->second.children()->members().end(),
+ s_h->second.filtration());
+ if (inter.size() != 0) {
+ Siblings * new_sib = new Siblings(siblings, // oncles
+ s_h->first, // parent
+ inter); // boost::container::ordered_unique_range_t
+ inter.clear();
+ s_h->second.assign_children(new_sib);
+ siblings_expansion(new_sib, k - 1);
+ } else {
+ // ensure the children property
+ s_h->second.assign_children(siblings);
+ inter.clear();
+ }
+ }
+ }
+ }
+
+ /** \brief Intersects Dictionary 1 [begin1;end1) with Dictionary 2 [begin2,end2)
+ * and assigns the maximal possible Filtration_value to the Nodes. */
+ static void intersection(std::vector<std::pair<Vertex_handle, Node> >& intersection,
+ Dictionary_it begin1, Dictionary_it end1,
+ Dictionary_it begin2, Dictionary_it end2,
+ Filtration_value filtration_) {
+ if (begin1 == end1 || begin2 == end2)
+ return; // ----->>
+ while (true) {
+ if (begin1->first == begin2->first) {
+ Filtration_value filt = (std::max)({begin1->second.filtration(), begin2->second.filtration(), filtration_});
+ intersection.emplace_back(begin1->first, Node(nullptr, filt));
+ if (++begin1 == end1 || ++begin2 == end2)
+ return; // ----->>
+ } else if (begin1->first < begin2->first) {
+ if (++begin1 == end1)
+ return;
+ } else /* begin1->first > begin2->first */ {
+ if (++begin2 == end2)
+ return; // ----->>
+ }
+ }
+ }
+
+ public:
+ /** \brief Write the hasse diagram of the simplicial complex in os.
+ *
+ * Each row in the file correspond to a simplex. A line is written:
+ * dim idx_1 ... idx_k fil where dim is the dimension of the simplex,
+ * idx_1 ... idx_k are the row index (starting from 0) of the simplices of the boundary
+ * of the simplex, and fil is its filtration value. */
+ void print_hasse(std::ostream& os) {
+ os << num_simplices() << " " << std::endl;
+ for (auto sh : filtration_simplex_range()) {
+ os << dimension(sh) << " ";
+ for (auto b_sh : boundary_simplex_range(sh)) {
+ os << key(b_sh) << " ";
+ }
+ os << filtration(sh) << " \n";
+ }
+ }
+
+ public:
+ /** \brief Browse the simplex tree to ensure the filtration is not decreasing.
+ * The simplex tree is browsed starting from the root until the leaf, and the filtration values are set with their
+ * parent value (increased), in case the values are decreasing.
+ * @return The filtration modification information.
+ * \post Some simplex tree functions require the filtration to be valid. `make_filtration_non_decreasing()`
+ * function is not launching `initialize_filtration()` but returns the filtration modification information. If the
+ * complex has changed , please call `initialize_filtration()` to recompute it.
+ */
+ bool make_filtration_non_decreasing() {
+ bool modified = false;
+ // Loop must be from the end to the beginning, as higher dimension simplex are always on the left part of the tree
+ for (auto& simplex : boost::adaptors::reverse(root_.members())) {
+ if (has_children(&simplex)) {
+ modified |= rec_make_filtration_non_decreasing(simplex.second.children());
+ }
+ }
+ return modified;
+ }
+
+ private:
+ /** \brief Recursively Browse the simplex tree to ensure the filtration is not decreasing.
+ * @param[in] sib Siblings to be parsed.
+ * @return The filtration modification information in order to trigger initialize_filtration.
+ */
+ bool rec_make_filtration_non_decreasing(Siblings * sib) {
+ bool modified = false;
+
+ // Loop must be from the end to the beginning, as higher dimension simplex are always on the left part of the tree
+ for (auto& simplex : boost::adaptors::reverse(sib->members())) {
+ // Find the maximum filtration value in the border
+ Boundary_simplex_range boundary = boundary_simplex_range(&simplex);
+ Boundary_simplex_iterator max_border = std::max_element(std::begin(boundary), std::end(boundary),
+ [](Simplex_handle sh1, Simplex_handle sh2) {
+ return filtration(sh1) < filtration(sh2);
+ });
+
+ Filtration_value max_filt_border_value = filtration(*max_border);
+ if (simplex.second.filtration() < max_filt_border_value) {
+ // Store the filtration modification information
+ modified = true;
+ simplex.second.assign_filtration(max_filt_border_value);
+ }
+ if (has_children(&simplex)) {
+ modified |= rec_make_filtration_non_decreasing(simplex.second.children());
+ }
+ }
+ // Make the modified information to be traced by upper call
+ return modified;
+ }
+
+ public:
+ /** \brief Prune above filtration value given as parameter.
+ * @param[in] filtration Maximum threshold value.
+ * @return The filtration modification information.
+ * \post Some simplex tree functions require the filtration to be valid. `prune_above_filtration()`
+ * function is not launching `initialize_filtration()` but returns the filtration modification information. If the
+ * complex has changed , please call `initialize_filtration()` to recompute it.
+ */
+ bool prune_above_filtration(Filtration_value filtration) {
+ return rec_prune_above_filtration(root(), filtration);
+ }
+
+ private:
+ bool rec_prune_above_filtration(Siblings* sib, Filtration_value filt) {
+ auto&& list = sib->members();
+ auto last = std::remove_if(list.begin(), list.end(), [=](Dit_value_t& simplex) {
+ if (simplex.second.filtration() <= filt) return false;
+ if (has_children(&simplex)) rec_delete(simplex.second.children());
+ return true;
+ });
+
+ bool modified = (last != list.end());
+ if (last == list.begin() && sib != root()) {
+ // Removing the whole siblings, parent becomes a leaf.
+ sib->oncles()->members()[sib->parent()].assign_children(sib->oncles());
+ delete sib;
+ return true;
+ } else {
+ // Keeping some elements of siblings. Remove the others, and recurse in the remaining ones.
+ list.erase(last, list.end());
+ for (auto&& simplex : list)
+ if (has_children(&simplex))
+ modified |= rec_prune_above_filtration(simplex.second.children(), filt);
+ }
+ return modified;
+ }
+
+ public:
+ /** \brief Remove a maximal simplex.
+ * @param[in] sh Simplex handle on the maximal simplex to remove.
+ * \pre Please check the simplex has no coface before removing it.
+ * \exception std::invalid_argument In debug mode, if sh has children.
+ * \post Be aware that removing is shifting data in a flat_map (initialize_filtration to be done).
+ */
+ void remove_maximal_simplex(Simplex_handle sh) {
+ // Guarantee the simplex has no children
+ GUDHI_CHECK(!has_children(sh),
+ std::invalid_argument("Simplex_tree::remove_maximal_simplex - argument has children"));
+
+ // Simplex is a leaf, it means the child is the Siblings owning the leaf
+ Siblings* child = sh->second.children();
+
+ if ((child->size() > 1) || (child == root())) {
+ // Not alone, just remove it from members
+ // Special case when child is the root of the simplex tree, just remove it from members
+ child->erase(sh);
+ } else {
+ // Sibling is emptied : must be deleted, and its parent must point on his own Sibling
+ child->oncles()->members().at(child->parent()).assign_children(child->oncles());
+ delete child;
+ }
+ }
+
+ private:
+ Vertex_handle null_vertex_;
+ /** \brief Upper bound on the filtration values of the simplices.*/
+ Filtration_value threshold_;
+ /** \brief Total number of simplices in the complex, without the empty simplex.*/
+ /** \brief Set of simplex tree Nodes representing the vertices.*/
+ Siblings root_;
+ /** \brief Simplices ordered according to a filtration.*/
+ std::vector<Simplex_handle> filtration_vect_;
+ /** \brief Upper bound on the dimension of the simplicial complex.*/
+ int dimension_;
+};
+
+// Print a Simplex_tree in os.
+template<typename...T>
+std::ostream& operator<<(std::ostream & os, Simplex_tree<T...> & st) {
+ for (auto sh : st.filtration_simplex_range()) {
+ os << st.dimension(sh) << " ";
+ for (auto v : st.simplex_vertex_range(sh)) {
+ os << v << " ";
+ }
+ os << st.filtration(sh) << "\n"; // TODO(VR): why adding the key ?? not read ?? << " " << st.key(sh) << " \n";
+ }
+ return os;
+}
+
+template<typename...T>
+std::istream& operator>>(std::istream & is, Simplex_tree<T...> & st) {
+ typedef Simplex_tree<T...> ST;
+ std::vector<typename ST::Vertex_handle> simplex;
+ typename ST::Filtration_value fil;
+ typename ST::Filtration_value max_fil = 0;
+ int max_dim = -1;
+ while (read_simplex(is, simplex, fil)) {
+ // read all simplices in the file as a list of vertices
+ // Warning : simplex_size needs to be casted in int - Can be 0
+ int dim = static_cast<int> (simplex.size() - 1);
+ if (max_dim < dim) {
+ max_dim = dim;
+ }
+ if (max_fil < fil) {
+ max_fil = fil;
+ }
+ // insert every simplex in the simplex tree
+ st.insert_simplex(simplex, fil);
+ simplex.clear();
+ }
+ st.set_dimension(max_dim);
+ st.set_filtration(max_fil);
+
+ return is;
+}
+
+/** Model of SimplexTreeOptions.
+ *
+ * Maximum number of simplices to compute persistence is <CODE>std::numeric_limits<std::uint32_t>::max()</CODE>
+ * (about 4 billions of simplices). */
+struct Simplex_tree_options_full_featured {
+ typedef linear_indexing_tag Indexing_tag;
+ typedef int Vertex_handle;
+ typedef double Filtration_value;
+ typedef std::uint32_t Simplex_key;
+ static const bool store_key = true;
+ static const bool store_filtration = true;
+ static const bool contiguous_vertices = false;
+};
+
+/** Model of SimplexTreeOptions, faster than `Simplex_tree_options_full_featured` but note the unsafe
+ * `contiguous_vertices` option.
+ *
+ * Maximum number of simplices to compute persistence is <CODE>std::numeric_limits<std::uint32_t>::max()</CODE>
+ * (about 4 billions of simplices). */
+
+struct Simplex_tree_options_fast_persistence {
+ typedef linear_indexing_tag Indexing_tag;
+ typedef int Vertex_handle;
+ typedef float Filtration_value;
+ typedef std::uint32_t Simplex_key;
+ static const bool store_key = true;
+ static const bool store_filtration = true;
+ static const bool contiguous_vertices = true;
+};
+
+/** @} */ // end defgroup simplex_tree
+
+} // namespace Gudhi
+
+#endif // SIMPLEX_TREE_H_
diff --git a/include/gudhi/Simplex_tree/Simplex_tree_iterators.h b/include/gudhi/Simplex_tree/Simplex_tree_iterators.h
new file mode 100644
index 00000000..7e0a454d
--- /dev/null
+++ b/include/gudhi/Simplex_tree/Simplex_tree_iterators.h
@@ -0,0 +1,338 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Clément Maria
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Méditerranée (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SIMPLEX_TREE_SIMPLEX_TREE_ITERATORS_H_
+#define SIMPLEX_TREE_SIMPLEX_TREE_ITERATORS_H_
+
+#include <boost/iterator/iterator_facade.hpp>
+#include <boost/version.hpp>
+#if BOOST_VERSION >= 105600
+# include <boost/container/static_vector.hpp>
+#endif
+
+#include <vector>
+
+namespace Gudhi {
+
+/* \addtogroup simplex_tree
+ * Iterators and range types for the Simplex_tree.
+ * @{
+ */
+
+/* \brief Iterator over the vertices of a simplex
+ * in a SimplexTree.
+ *
+ * Forward iterator, 'value_type' is SimplexTree::Vertex_handle.*/
+template<class SimplexTree>
+class Simplex_tree_simplex_vertex_iterator : public boost::iterator_facade<
+ Simplex_tree_simplex_vertex_iterator<SimplexTree>,
+ typename SimplexTree::Vertex_handle const, boost::forward_traversal_tag,
+ typename SimplexTree::Vertex_handle const> {
+ public:
+ typedef typename SimplexTree::Simplex_handle Simplex_handle;
+ typedef typename SimplexTree::Siblings Siblings;
+ typedef typename SimplexTree::Vertex_handle Vertex_handle;
+
+ explicit Simplex_tree_simplex_vertex_iterator(SimplexTree * st)
+ : // any end() iterator
+ sib_(nullptr),
+ v_(st->null_vertex()) {
+ }
+
+ Simplex_tree_simplex_vertex_iterator(SimplexTree * st, Simplex_handle sh)
+ : sib_(st->self_siblings(sh)),
+ v_(sh->first) {
+ }
+
+ private:
+ friend class boost::iterator_core_access;
+
+ bool equal(Simplex_tree_simplex_vertex_iterator const &other) const {
+ return sib_ == other.sib_ && v_ == other.v_;
+ }
+
+ Vertex_handle const& dereference() const {
+ return v_;
+ }
+
+ void increment() {
+ v_ = sib_->parent();
+ sib_ = sib_->oncles();
+ }
+
+ Siblings * sib_;
+ Vertex_handle v_;
+};
+
+/*---------------------------------------------------------------------------*/
+/* \brief Iterator over the simplices of the boundary of a
+ * simplex.
+ *
+ * Forward iterator, value_type is SimplexTree::Simplex_handle.*/
+template<class SimplexTree>
+class Simplex_tree_boundary_simplex_iterator : public boost::iterator_facade<
+ Simplex_tree_boundary_simplex_iterator<SimplexTree>,
+ typename SimplexTree::Simplex_handle const, boost::forward_traversal_tag> {
+ public:
+ typedef typename SimplexTree::Simplex_handle Simplex_handle;
+ typedef typename SimplexTree::Vertex_handle Vertex_handle;
+ typedef typename SimplexTree::Siblings Siblings;
+
+// any end() iterator
+ explicit Simplex_tree_boundary_simplex_iterator(SimplexTree * st)
+ : sib_(nullptr),
+ sh_(st->null_simplex()),
+ st_(st) {
+ }
+
+ template<class SimplexHandle>
+ Simplex_tree_boundary_simplex_iterator(SimplexTree * st, SimplexHandle sh)
+ : last_(sh->first),
+ sib_(nullptr),
+ st_(st) {
+ Siblings * sib = st->self_siblings(sh);
+ next_ = sib->parent();
+ sib_ = sib->oncles();
+ if (sib_ != nullptr) {
+ sh_ = sib_->find(next_);
+ } else {
+ sh_ = st->null_simplex();
+ } // vertex: == end()
+ }
+
+ private:
+ friend class boost::iterator_core_access;
+// valid when iterating along the SAME boundary.
+ bool equal(Simplex_tree_boundary_simplex_iterator const& other) const {
+ return sh_ == other.sh_;
+ }
+
+ Simplex_handle const& dereference() const {
+ assert(sh_ != st_->null_simplex());
+ return sh_;
+ }
+
+ void increment() {
+ if (sib_ == nullptr) {
+ sh_ = st_->null_simplex();
+ return;
+ }
+
+ Siblings * for_sib = sib_;
+ Siblings * new_sib = sib_->oncles();
+ auto rit = suffix_.rbegin();
+ if (SimplexTree::Options::contiguous_vertices && new_sib == nullptr && rit != suffix_.rend()) {
+ // We reached the root, use a short-cut to find a vertex. We could also
+ // optimize finding the second vertex of a segment, but people are
+ // expected to call endpoints().
+ assert(st_->contiguous_vertices());
+ sh_ = for_sib->members_.begin()+*rit;
+ for_sib = sh_->second.children();
+ ++rit;
+ }
+ for (; rit != suffix_.rend(); ++rit) {
+ sh_ = for_sib->find(*rit);
+ for_sib = sh_->second.children();
+ }
+ sh_ = for_sib->find(last_); // sh_ points to the right simplex now
+ suffix_.push_back(next_);
+ next_ = sib_->parent();
+ sib_ = new_sib;
+ }
+
+ // Most of the storage should be moved to the range, iterators should be light.
+ Vertex_handle last_; // last vertex of the simplex
+ Vertex_handle next_; // next vertex to push in suffix_
+#if BOOST_VERSION >= 105600
+ // 40 seems a conservative bound on the dimension of a Simplex_tree for now,
+ // as it would not fit on the biggest hard-drive.
+ boost::container::static_vector<Vertex_handle, 40> suffix_;
+ // static_vector still has some overhead compared to a trivial hand-made
+ // version using std::aligned_storage, or compared to making suffix_ static.
+#else
+ std::vector<Vertex_handle> suffix_;
+#endif
+ Siblings * sib_; // where the next search will start from
+ Simplex_handle sh_; // current Simplex_handle in the boundary
+ SimplexTree * st_; // simplex containing the simplicial complex
+};
+/*---------------------------------------------------------------------------*/
+/* \brief Iterator over the simplices of a simplicial complex.
+ *
+ * Forward iterator, value_type is SimplexTree::Simplex_handle.*/
+template<class SimplexTree>
+class Simplex_tree_complex_simplex_iterator : public boost::iterator_facade<
+ Simplex_tree_complex_simplex_iterator<SimplexTree>,
+ typename SimplexTree::Simplex_handle const, boost::forward_traversal_tag> {
+ public:
+ typedef typename SimplexTree::Simplex_handle Simplex_handle;
+ typedef typename SimplexTree::Siblings Siblings;
+ typedef typename SimplexTree::Vertex_handle Vertex_handle;
+
+// any end() iterator
+ Simplex_tree_complex_simplex_iterator()
+ : sib_(nullptr),
+ st_(nullptr) {
+ }
+
+ explicit Simplex_tree_complex_simplex_iterator(SimplexTree * st)
+ : sib_(nullptr),
+ st_(st) {
+ if (st == nullptr || st->root() == nullptr || st->root()->members().empty()) {
+ st_ = nullptr;
+ } else {
+ sh_ = st->root()->members().begin();
+ sib_ = st->root();
+ while (st->has_children(sh_)) {
+ sib_ = sh_->second.children();
+ sh_ = sib_->members().begin();
+ }
+ }
+ }
+ private:
+ friend class boost::iterator_core_access;
+
+// valid when iterating along the SAME boundary.
+ bool equal(Simplex_tree_complex_simplex_iterator const& other) const {
+ if (other.st_ == nullptr) {
+ return (st_ == nullptr);
+ }
+ if (st_ == nullptr) {
+ return false;
+ }
+ return (&(sh_->second) == &(other.sh_->second));
+ }
+
+ Simplex_handle const& dereference() const {
+ return sh_;
+ }
+
+// Depth first traversal.
+ void increment() {
+ ++sh_;
+ if (sh_ == sib_->members().end()) {
+ if (sib_->oncles() == nullptr) {
+ st_ = nullptr;
+ return;
+ } // reach the end
+ sh_ = sib_->oncles()->members().find(sib_->parent());
+ sib_ = sib_->oncles();
+ return;
+ }
+ while (st_->has_children(sh_)) {
+ sib_ = sh_->second.children();
+ sh_ = sib_->members().begin();
+ }
+ }
+
+ Simplex_handle sh_;
+ Siblings * sib_;
+ SimplexTree * st_;
+};
+
+/* \brief Iterator over the simplices of the skeleton of a given
+ * dimension of the simplicial complex.
+ *
+ * Forward iterator, value_type is SimplexTree::Simplex_handle.*/
+template<class SimplexTree>
+class Simplex_tree_skeleton_simplex_iterator : public boost::iterator_facade<
+ Simplex_tree_skeleton_simplex_iterator<SimplexTree>,
+ typename SimplexTree::Simplex_handle const, boost::forward_traversal_tag> {
+ public:
+ typedef typename SimplexTree::Simplex_handle Simplex_handle;
+ typedef typename SimplexTree::Siblings Siblings;
+ typedef typename SimplexTree::Vertex_handle Vertex_handle;
+
+// any end() iterator
+ Simplex_tree_skeleton_simplex_iterator()
+ : sib_(nullptr),
+ st_(nullptr),
+ dim_skel_(0),
+ curr_dim_(0) {
+ }
+
+ Simplex_tree_skeleton_simplex_iterator(SimplexTree * st, int dim_skel)
+ : sib_(nullptr),
+ st_(st),
+ dim_skel_(dim_skel),
+ curr_dim_(0) {
+ if (st == nullptr || st->root() == nullptr || st->root()->members().empty()) {
+ st_ = nullptr;
+ } else {
+ sh_ = st->root()->members().begin();
+ sib_ = st->root();
+ while (st->has_children(sh_) && curr_dim_ < dim_skel_) {
+ sib_ = sh_->second.children();
+ sh_ = sib_->members().begin();
+ ++curr_dim_;
+ }
+ }
+ }
+ private:
+ friend class boost::iterator_core_access;
+
+// valid when iterating along the SAME boundary.
+ bool equal(Simplex_tree_skeleton_simplex_iterator const& other) const {
+ if (other.st_ == nullptr) {
+ return (st_ == nullptr);
+ }
+ if (st_ == nullptr) {
+ return false;
+ }
+ return (&(sh_->second) == &(other.sh_->second));
+ }
+
+ Simplex_handle const& dereference() const {
+ return sh_;
+ }
+
+// Depth first traversal of the skeleton.
+ void increment() {
+ ++sh_;
+ if (sh_ == sib_->members().end()) {
+ if (sib_->oncles() == nullptr) {
+ st_ = nullptr;
+ return;
+ } // reach the end
+ sh_ = sib_->oncles()->members().find(sib_->parent());
+ sib_ = sib_->oncles();
+ --curr_dim_;
+ return;
+ }
+ while (st_->has_children(sh_) && curr_dim_ < dim_skel_) {
+ sib_ = sh_->second.children();
+ sh_ = sib_->members().begin();
+ ++curr_dim_;
+ }
+ }
+
+ Simplex_handle sh_;
+ Siblings * sib_;
+ SimplexTree * st_;
+ int dim_skel_;
+ int curr_dim_;
+};
+
+/* @} */ // end addtogroup simplex_tree
+} // namespace Gudhi
+
+#endif // SIMPLEX_TREE_SIMPLEX_TREE_ITERATORS_H_
diff --git a/include/gudhi/Simplex_tree/Simplex_tree_node_explicit_storage.h b/include/gudhi/Simplex_tree/Simplex_tree_node_explicit_storage.h
new file mode 100644
index 00000000..25d4888a
--- /dev/null
+++ b/include/gudhi/Simplex_tree/Simplex_tree_node_explicit_storage.h
@@ -0,0 +1,72 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Clément Maria
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Méditerranée (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SIMPLEX_TREE_SIMPLEX_TREE_NODE_EXPLICIT_STORAGE_H_
+#define SIMPLEX_TREE_SIMPLEX_TREE_NODE_EXPLICIT_STORAGE_H_
+
+#include <vector>
+
+namespace Gudhi {
+
+/* \addtogroup simplex_tree
+ * Represents a node of a Simplex_tree.
+ * @{
+ */
+
+/*
+ * \brief Node of a simplex tree with filtration value
+ * and simplex key.
+ *
+ * It stores explicitely its own filtration value and its own Simplex_key.
+ */
+template<class SimplexTree>
+struct Simplex_tree_node_explicit_storage : SimplexTree::Filtration_simplex_base, SimplexTree::Key_simplex_base {
+ typedef typename SimplexTree::Siblings Siblings;
+ typedef typename SimplexTree::Filtration_value Filtration_value;
+ typedef typename SimplexTree::Simplex_key Simplex_key;
+
+ Simplex_tree_node_explicit_storage(Siblings * sib = nullptr,
+ Filtration_value filtration = 0)
+ : children_(sib) {
+ this->assign_filtration(filtration);
+ }
+
+ /*
+ * Assign children to the node
+ */
+ void assign_children(Siblings * children) {
+ children_ = children;
+ }
+
+ /* Careful -> children_ can be NULL*/
+ Siblings * children() {
+ return children_;
+ }
+
+ private:
+ Siblings * children_;
+};
+
+/* @} */ // end addtogroup simplex_tree
+} // namespace Gudhi
+
+#endif // SIMPLEX_TREE_SIMPLEX_TREE_NODE_EXPLICIT_STORAGE_H_
diff --git a/include/gudhi/Simplex_tree/Simplex_tree_siblings.h b/include/gudhi/Simplex_tree/Simplex_tree_siblings.h
new file mode 100644
index 00000000..1eca7f6f
--- /dev/null
+++ b/include/gudhi/Simplex_tree/Simplex_tree_siblings.h
@@ -0,0 +1,131 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Clément Maria
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Méditerranée (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SIMPLEX_TREE_SIMPLEX_TREE_SIBLINGS_H_
+#define SIMPLEX_TREE_SIMPLEX_TREE_SIBLINGS_H_
+
+#include <gudhi/Simplex_tree/Simplex_tree_node_explicit_storage.h>
+
+#include <boost/container/flat_map.hpp>
+
+#include <utility>
+#include <vector>
+
+namespace Gudhi {
+
+/* \addtogroup simplex_tree
+ * Represents a set of node of a Simplex_tree that share the same parent.
+ * @{
+ */
+
+/* \brief Data structure to store a set of nodes in a SimplexTree sharing
+ * the same parent node.*/
+template<class SimplexTree, class MapContainer>
+class Simplex_tree_siblings {
+// private:
+// friend SimplexTree;
+ public:
+ template<class T> friend class Simplex_tree_simplex_vertex_iterator;
+ template<class T> friend class Simplex_tree_boundary_simplex_iterator;
+ template<class T> friend class Simplex_tree_complex_simplex_iterator;
+ template<class T> friend class Simplex_tree_skeleton_simplex_iterator;
+
+ typedef typename SimplexTree::Vertex_handle Vertex_handle;
+ typedef typename SimplexTree::Filtration_value Filtration_value;
+ typedef typename SimplexTree::Node Node;
+ typedef MapContainer Dictionary;
+ typedef typename MapContainer::iterator Dictionary_it;
+
+ /* Default constructor.*/
+ Simplex_tree_siblings()
+ : oncles_(nullptr),
+ parent_(-1),
+ members_() {
+ }
+
+ /* Constructor with values.*/
+ Simplex_tree_siblings(Simplex_tree_siblings * oncles, Vertex_handle parent)
+ : oncles_(oncles),
+ parent_(parent),
+ members_() {
+ }
+
+ /* \brief Constructor with initialized set of members.
+ *
+ * 'members' must be sorted and unique.*/
+ template<typename RandomAccessVertexRange>
+ Simplex_tree_siblings(Simplex_tree_siblings * oncles, Vertex_handle parent, const RandomAccessVertexRange & members)
+ : oncles_(oncles),
+ parent_(parent),
+ members_(boost::container::ordered_unique_range, members.begin(),
+ members.end()) {
+ for (auto& map_el : members_) {
+ map_el.second.assign_children(this);
+ }
+ }
+
+ /*
+ * \brief Inserts a Node in the set of siblings nodes.
+ *
+ * If already present, assigns the minimal filtration value
+ * between input filtration_value and the value already
+ * present in the node.
+ */
+ void insert(Vertex_handle v, Filtration_value filtration_value) {
+ auto ins = members_.emplace(v, Node(this, filtration_value));
+ if (!ins.second && filtration(ins.first) > filtration_value)
+ ins.first->second.assign_filtration(filtration_value);
+ }
+
+ Dictionary_it find(Vertex_handle v) {
+ return members_.find(v);
+ }
+
+ Simplex_tree_siblings * oncles() {
+ return oncles_;
+ }
+
+ Vertex_handle parent() const {
+ return parent_;
+ }
+
+ Dictionary & members() {
+ return members_;
+ }
+
+ size_t size() const {
+ return members_.size();
+ }
+
+ void erase(const Dictionary_it iterator) {
+ members_.erase(iterator);
+ }
+
+ Simplex_tree_siblings * oncles_;
+ Vertex_handle parent_;
+ Dictionary members_;
+};
+
+/* @} */ // end addtogroup simplex_tree
+} // namespace Gudhi
+
+#endif // SIMPLEX_TREE_SIMPLEX_TREE_SIBLINGS_H_
diff --git a/include/gudhi/Simplex_tree/indexing_tag.h b/include/gudhi/Simplex_tree/indexing_tag.h
new file mode 100644
index 00000000..0adeb46d
--- /dev/null
+++ b/include/gudhi/Simplex_tree/indexing_tag.h
@@ -0,0 +1,39 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Clément Maria
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Méditerranée (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SIMPLEX_TREE_INDEXING_TAG_H_
+#define SIMPLEX_TREE_INDEXING_TAG_H_
+
+namespace Gudhi {
+
+/** \brief Tag for a linear ordering of simplices.
+ *
+ * \implements IndexingTag
+ */
+struct linear_indexing_tag {
+};
+
+/* \brief Tag for a zigzag ordering of simplices. */
+// struct zigzag_indexing_tag {};
+} // namespace Gudhi
+
+#endif // SIMPLEX_TREE_INDEXING_TAG_H_
diff --git a/include/gudhi/Skeleton_blocker.h b/include/gudhi/Skeleton_blocker.h
new file mode 100644
index 00000000..822282fd
--- /dev/null
+++ b/include/gudhi/Skeleton_blocker.h
@@ -0,0 +1,252 @@
+ /* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SKELETON_BLOCKER_H_
+#define SKELETON_BLOCKER_H_
+
+#include <gudhi/Skeleton_blocker_complex.h>
+#include <gudhi/Skeleton_blocker_geometric_complex.h>
+#include <gudhi/Skeleton_blocker_simplifiable_complex.h>
+#include <gudhi/Skeleton_blocker/Skeleton_blocker_off_io.h>
+
+#include <gudhi/Skeleton_blocker/Skeleton_blocker_simple_traits.h>
+#include <gudhi/Skeleton_blocker/Skeleton_blocker_simple_geometric_traits.h>
+
+#include <gudhi/Debug_utils.h>
+
+namespace Gudhi {
+
+namespace skeleton_blocker {
+
+/** \defgroup skbl Skeleton-Blocker
+
+\author David Salinas
+
+\section Introduction
+The Skeleton-Blocker data-structure proposes a light encoding for simplicial complexes by storing only an *implicit* representation of its
+simplices
+\cite socg_blockers_2011,\cite blockers2012.
+Intuitively, it just stores the 1-skeleton of a simplicial complex with a graph and the set of its "missing faces" that
+is very small in practice (see next section for a formal definition).
+This data-structure handles all simplicial complexes operations such as
+ as simplex enumeration or simplex removal but operations that are particularly efficient
+ are operations that do not require simplex enumeration such as edge iteration, link computation or simplex contraction.
+
+
+\section Definitions
+
+We recall briefly classical definitions of simplicial complexes
+ \cite Munkres-elementsalgtop1984.
+An abstract simplex is a finite non-empty set and its dimension is its number of elements minus 1.
+Whenever \f$\tau \subset \sigma\f$ and \f$\tau \neq \emptyset \f$, \f$ \tau \f$ is called a face of
+\f$ \sigma\f$ and \f$ \sigma\f$ is called a coface of \f$ \tau \f$ . Furthermore,
+when \f$ \tau \neq \sigma\f$ we say that \f$ \tau\f$ is a proper-face of \f$ \sigma\f$.
+An abstract simplicial complex is a set of simplices that contains all the faces of its simplices.
+The 1-skeleton of a simplicial complex (or its graph) consists of its elements of dimension lower than 2.
+
+*\image html "ds_representation.png" "Skeleton-blocker representation" width=20cm
+
+
+To encode, a simplicial complex, one can encodes all its simplices.
+In case when this number gets too large,
+a lighter and implicit version consists of encoding only its graph plus some elements called missing faces or blockers.
+A blocker is a simplex of dimension greater than 1
+that does not belong to the complex but whose all proper faces does.
+
+
+Remark that for a clique complex (i.e. a simplicial complex whose simplices are cliques of its graph), the set of blockers
+is empty and the data-structure is then particularly sparse.
+One famous example of clique-complex is the Rips complex which is intensively used
+in topological data-analysis.
+In practice, the set of blockers of a simplicial complex
+remains also small when simplifying a Rips complex with edge contractions
+but also for most of the simplicial complexes used in topological data-analysis such as Delaunay, Cech or Witness complexes.
+For instance, the numbers of blockers is depicted for random 3-dimensional spheres embedded into \f$R^4\f$
+in next figure. Storing the graph and blockers of such simplicial complexes is much compact in this case than storing
+their simplices.
+
+
+*\image html "blockers_curve.png" "Number of blockers of random triangulations of 3-spheres" width=10cm
+
+
+
+
+\section API
+
+\subsection Overview
+
+Two main classes of this package are Skeleton_blocker_complex and Skeleton_blocker_geometric_complex.
+The first one can be used to represent an abstract simplicial complex and supports most used
+operations in a simplicial complex such as :
+
+\li vertex/edge/simplex enumeration
+\li simplifications operations such as remove star, add star (e.g. general form of collapse),
+edge contractions
+
+The class Skeleton_blocker_geometric_complex supports the same methods as Skeleton_blocker_complex
+and point access in addition.
+
+
+
+\subsection Visitor
+
+The class Skeleton_blocker_complex has a visitor that is called when usual operations such as adding an edge or remove a vertex are called.
+You may want to use this visitor to compute statistics or to update another data-structure (for instance this visitor is heavily used in the \ref contr package).
+
+
+
+
+\section Example
+
+
+\subsection Iterating Iterating through vertices, edges, blockers and simplices
+
+Iteration through vertices, edges, simplices or blockers is straightforward with c++11 for range loops.
+Note that simplex iteration with this implicit data-structure just takes
+a few more time compared to iteration via an explicit representation
+such as the Simplex Tree. The following example computes the Euler Characteristic
+of a simplicial complex.
+
+ \code{.cpp}
+ typedef Skeleton_blocker_complex<Skeleton_blocker_simple_traits> Complex;
+ typedef Complex::Vertex_handle Vertex_handle;
+ typedef Complex::Simplex Simplex;
+
+ const int n = 15;
+
+ // build a full complex with 10 vertices and 2^n-1 simplices
+ Complex complex;
+ for(int i=0;i<n;i++)
+ complex.add_vertex();
+ for(int i=0;i<n;i++)
+ for(int j=0;j<i;j++)
+ complex.add_edge_without_blockers(Vertex_handle(i),Vertex_handle(j));
+
+ // this is just to illustrate iterators, to count number of vertices
+ // or edges, complex.num_vertices() and complex.num_edges() are
+ // more appropriated!
+ unsigned num_vertices = 0;
+ for(auto v : complex.vertex_range()){
+ ++num_vertices;
+ }
+
+ unsigned num_edges = 0;
+ for(auto e : complex.edge_range())
+ ++num_edges;
+
+ unsigned euler = 0;
+ unsigned num_simplices = 0;
+ // we use a reference to a simplex instead of a copy
+ // value here because a simplex is a set of integers
+ // and copying it cost time
+ for(const Simplex & s : complex.star_simplex_range()){
+ ++num_simplices;
+ if(s.dimension()%2 == 0)
+ euler += 1;
+ else
+ euler -= 1;
+ }
+ std::cout << "Saw "<<num_vertices<<" vertices, "<<num_edges<<" edges and "<<num_simplices<<" simplices"<<std::endl;
+ std::cout << "The Euler Characteristic is "<<euler<<std::endl;
+ \endcode
+
+
+\verbatim
+./SkeletonBlockerIteration
+Saw 15 vertices, 105 edges and 32767 simplices
+The Euler Characteristic is 1
+ 0.537302s wall, 0.530000s user + 0.000000s system = 0.530000s CPU (98.6%)
+\endverbatim
+
+
+\subsection s Constructing a skeleton-blockers from a list of maximal faces or from a list of faces
+
+ \code{.cpp}
+ std::vector<Simplex> simplices;
+
+ //add 4 triangles of a tetrahedron 0123
+ simplices.push_back(Simplex(Vertex_handle(0),Vertex_handle(1),Vertex_handle(2)));
+ simplices.push_back(Simplex(Vertex_handle(1),Vertex_handle(2),Vertex_handle(3)));
+ simplices.push_back(Simplex(Vertex_handle(3),Vertex_handle(0),Vertex_handle(2)));
+ simplices.push_back(Simplex(Vertex_handle(3),Vertex_handle(0),Vertex_handle(1)));
+
+ Complex complex;
+ //get complex from top faces
+ make_complex_from_top_faces(complex,simplices.begin(),simplices.end());
+
+ std::cout << "Simplices:"<<std::endl;
+ for(const Simplex & s : complex.star_simplex_range())
+ std::cout << s << " ";
+ std::cout << std::endl;
+
+ //One blocker as simplex 0123 is not in the complex but all its proper faces are.
+ std::cout << "Blockers: "<<complex.blockers_to_string()<<std::endl;
+
+ //now build a complex from its full list of simplices
+ simplices.clear();
+ simplices.push_back(Simplex(Vertex_handle(0)));
+ simplices.push_back(Simplex(Vertex_handle(1)));
+ simplices.push_back(Simplex(Vertex_handle(2)));
+ simplices.push_back(Simplex(Vertex_handle(0),Vertex_handle(1)));
+ simplices.push_back(Simplex(Vertex_handle(1),Vertex_handle(2)));
+ simplices.push_back(Simplex(Vertex_handle(2),Vertex_handle(0)));
+ complex = Complex(simplices.begin(),simplices.end());
+
+ std::cout << "Simplices:"<<std::endl;
+ for(const Simplex & s : complex.star_simplex_range())
+ std::cout << s << " ";
+ std::cout << std::endl;
+
+ //One blocker as simplex 012 is not in the complex but all its proper faces are.
+ std::cout << "Blockers: "<<complex.blockers_to_string()<<std::endl;
+ \endcode
+\verbatim
+./SkeletonBlockerFromSimplices
+Simplices:
+{0} {0,1} {0,2} {0,3} {0,1,2} {0,1,3} {0,2,3} {1} {1,2} {1,3} {1,2,3} {2} {2,3} {3}
+Blockers: {0,1,2,3}
+
+Simplices:
+{0} {0,1} {0,2} {1} {1,2} {2}
+Blockers: {0,1,2}
+\endverbatim
+
+
+\section Acknowledgements
+The author wishes to thank Dominique Attali and André Lieutier for
+their collaboration to write the two initial papers
+\cite socg_blockers_2011,\cite blockers2012
+ about this data-structure
+ and also Dominique for leaving him use a prototype.
+
+
+\copyright GNU General Public License v3.
+*/
+/** @} */ // end defgroup
+
+} // namespace skeleton_blocker
+
+namespace skbl = skeleton_blocker;
+
+} // namespace Gudhi
+
+#endif // SKELETON_BLOCKER_H_
diff --git a/include/gudhi/Skeleton_blocker/Skeleton_blocker_complex_visitor.h b/include/gudhi/Skeleton_blocker/Skeleton_blocker_complex_visitor.h
new file mode 100644
index 00000000..32f40a4b
--- /dev/null
+++ b/include/gudhi/Skeleton_blocker/Skeleton_blocker_complex_visitor.h
@@ -0,0 +1,137 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef SKELETON_BLOCKER_SKELETON_BLOCKER_COMPLEX_VISITOR_H_
+#define SKELETON_BLOCKER_SKELETON_BLOCKER_COMPLEX_VISITOR_H_
+
+#include <gudhi/Skeleton_blocker/Skeleton_blocker_simplex.h>
+
+namespace Gudhi {
+
+namespace skeleton_blocker {
+// todo rajouter les const
+
+/**
+ *@class Skeleton_blocker_complex_visitor
+ *@brief Interface for a visitor of a simplicial complex.
+ */
+template<typename Vertex_handle>
+class Skeleton_blocker_complex_visitor {
+ public:
+ virtual ~Skeleton_blocker_complex_visitor() {}
+
+ virtual void on_add_vertex(Vertex_handle) = 0;
+ virtual void on_remove_vertex(Vertex_handle) = 0;
+
+ virtual void on_add_edge_without_blockers(Vertex_handle a, Vertex_handle b) = 0;
+ virtual void on_remove_edge(Vertex_handle a, Vertex_handle b) = 0;
+
+ /**
+ * @brief Called when an edge changes, during the contraction of
+ * an edge
+ */
+ virtual void on_changed_edge(Vertex_handle a, Vertex_handle b) = 0;
+
+ /**
+ * @brief Called when performing an edge contraction when
+ * an edge bx is replaced by an edge ax (not already present).
+ * Precisely, this methods is called this way in contract_edge :
+ * add_edge_without_blockers(a,x)
+ * on_swaped_edge(a,b,x)
+ * remove_edge(b,x)
+ */
+ virtual void on_swaped_edge(Vertex_handle a, Vertex_handle b,
+ Vertex_handle x) = 0;
+ virtual void on_add_blocker(
+ const Skeleton_blocker_simplex<Vertex_handle>&) = 0;
+ virtual void on_delete_blocker(
+ const Skeleton_blocker_simplex<Vertex_handle>*) = 0;
+};
+
+/**
+ *@class Dummy_complex_visitor
+ *@brief A dummy visitor of a simplicial complex that does nothing
+ *
+ */
+template<typename Vertex_handle>
+class Dummy_complex_visitor : public Skeleton_blocker_complex_visitor<
+ Vertex_handle> {
+ public:
+ void on_add_vertex(Vertex_handle) {
+ }
+ void on_remove_vertex(Vertex_handle) {
+ }
+ void on_add_edge_without_blockers(Vertex_handle a, Vertex_handle b) {
+ }
+ void on_remove_edge(Vertex_handle a, Vertex_handle b) {
+ }
+ void on_changed_edge(Vertex_handle a, Vertex_handle b) {
+ }
+ void on_swaped_edge(Vertex_handle a, Vertex_handle b, Vertex_handle x) {
+ }
+ void on_add_blocker(const Skeleton_blocker_simplex<Vertex_handle>&) {
+ }
+ void on_delete_blocker(const Skeleton_blocker_simplex<Vertex_handle>*) {
+ }
+};
+
+/**
+ *@class Print_complex_visitor
+ *@brief A visitor that prints the visit information.
+ *
+ */
+template<typename Vertex_handle>
+class Print_complex_visitor : public Skeleton_blocker_complex_visitor<
+ Vertex_handle> {
+ public:
+ void on_add_vertex(Vertex_handle v) {
+ std::cerr << "on_add_vertex:" << v << std::endl;
+ }
+ void on_remove_vertex(Vertex_handle v) {
+ std::cerr << "on_remove_vertex:" << v << std::endl;
+ }
+ void on_add_edge_without_blockers(Vertex_handle a, Vertex_handle b) {
+ std::cerr << "on_add_edge_without_blockers:" << a << "," << b << std::endl;
+ }
+ void on_remove_edge(Vertex_handle a, Vertex_handle b) {
+ std::cerr << "on_remove_edge:" << a << "," << b << std::endl;
+ }
+ void on_changed_edge(Vertex_handle a, Vertex_handle b) {
+ std::cerr << "on_changed_edge:" << a << "," << b << std::endl;
+ }
+ void on_swaped_edge(Vertex_handle a, Vertex_handle b, Vertex_handle x) {
+ std::cerr << "on_swaped_edge:" << a << "," << b << "," << x << std::endl;
+ }
+ void on_add_blocker(const Skeleton_blocker_simplex<Vertex_handle>& b) {
+ std::cerr << "on_add_blocker:" << b << std::endl;
+ }
+ void on_delete_blocker(const Skeleton_blocker_simplex<Vertex_handle>* b) {
+ std::cerr << "on_delete_blocker:" << b << std::endl;
+ }
+};
+
+} // namespace skeleton_blocker
+
+namespace skbl = skeleton_blocker;
+
+} // namespace Gudhi
+
+#endif // SKELETON_BLOCKER_SKELETON_BLOCKER_COMPLEX_VISITOR_H_
diff --git a/include/gudhi/Skeleton_blocker/Skeleton_blocker_link_superior.h b/include/gudhi/Skeleton_blocker/Skeleton_blocker_link_superior.h
new file mode 100644
index 00000000..3bfb5d11
--- /dev/null
+++ b/include/gudhi/Skeleton_blocker/Skeleton_blocker_link_superior.h
@@ -0,0 +1,79 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef SKELETON_BLOCKER_SKELETON_BLOCKER_LINK_SUPERIOR_H_
+#define SKELETON_BLOCKER_SKELETON_BLOCKER_LINK_SUPERIOR_H_
+
+#include <gudhi/Skeleton_blocker_link_complex.h>
+
+namespace Gudhi {
+
+namespace skeleton_blocker {
+
+template<class ComplexType> class Skeleton_blocker_sub_complex;
+
+/**
+ * \brief Class representing the link of a simplicial complex encoded by a skeleton/blockers pair.
+ * It computes only vertices greater than the simplex used to build the link.
+ */
+template<typename ComplexType>
+class Skeleton_blocker_link_superior : public Skeleton_blocker_link_complex<
+ ComplexType> {
+ typedef typename ComplexType::Edge_handle Edge_handle;
+
+ typedef typename ComplexType::boost_vertex_handle boost_vertex_handle;
+
+ public:
+ typedef typename ComplexType::Vertex_handle Vertex_handle;
+ typedef typename ComplexType::Root_vertex_handle Root_vertex_handle;
+ typedef typename ComplexType::Simplex Simplex;
+ typedef typename ComplexType::Root_simplex_handle Root_simplex_handle;
+ typedef typename ComplexType::BlockerMap BlockerMap;
+ typedef typename ComplexType::BlockerPair BlockerPair;
+ typedef typename ComplexType::BlockerMapIterator BlockerMapIterator;
+ typedef typename ComplexType::BlockerMapConstIterator BlockerMapConstIterator;
+ typedef typename ComplexType::Simplex::Simplex_vertex_const_iterator AddressSimplexConstIterator;
+ typedef typename ComplexType::Root_simplex_handle::Simplex_vertex_const_iterator IdSimplexConstIterator;
+
+ Skeleton_blocker_link_superior()
+ : Skeleton_blocker_link_complex<ComplexType>(true) {
+ }
+
+ Skeleton_blocker_link_superior(const ComplexType & parent_complex,
+ Simplex& alpha_parent_adress)
+ : Skeleton_blocker_link_complex<ComplexType>(parent_complex,
+ alpha_parent_adress, true) {
+ }
+
+ Skeleton_blocker_link_superior(const ComplexType & parent_complex,
+ Vertex_handle a_parent_adress)
+ : Skeleton_blocker_link_complex<ComplexType>(parent_complex,
+ a_parent_adress, true) {
+ }
+};
+
+} // namespace skeleton_blocker
+
+namespace skbl = skeleton_blocker;
+
+} // namespace Gudhi
+
+#endif // SKELETON_BLOCKER_SKELETON_BLOCKER_LINK_SUPERIOR_H_
diff --git a/include/gudhi/Skeleton_blocker/Skeleton_blocker_off_io.h b/include/gudhi/Skeleton_blocker/Skeleton_blocker_off_io.h
new file mode 100644
index 00000000..ba46c49e
--- /dev/null
+++ b/include/gudhi/Skeleton_blocker/Skeleton_blocker_off_io.h
@@ -0,0 +1,202 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef SKELETON_BLOCKER_SKELETON_BLOCKER_OFF_IO_H_
+#define SKELETON_BLOCKER_SKELETON_BLOCKER_OFF_IO_H_
+
+#include <gudhi/Off_reader.h>
+
+#include <string>
+#include <vector>
+#include <map>
+
+namespace Gudhi {
+
+namespace skeleton_blocker {
+
+/**
+ *@brief Off reader visitor that can be passed to Off_reader to read a Skeleton_blocker_complex.
+ */
+template<typename Complex>
+class Skeleton_blocker_off_flag_visitor_reader {
+ Complex& complex_;
+ typedef typename Complex::Vertex_handle Vertex_handle;
+ typedef typename Complex::Point Point;
+
+ const bool load_only_points_;
+
+ public:
+ explicit Skeleton_blocker_off_flag_visitor_reader(Complex& complex, bool load_only_points = false) :
+ complex_(complex),
+ load_only_points_(load_only_points) { }
+
+ void init(int dim, int num_vertices, int num_faces, int num_edges) {
+ // todo do an assert to check that this number are correctly read
+ // todo reserve size for vector points
+ }
+
+ void point(const std::vector<double>& point) {
+ complex_.add_vertex(Point(point.begin(), point.end()));
+ }
+
+ void maximal_face(const std::vector<int>& face) {
+ if (!load_only_points_) {
+ for (size_t i = 0; i < face.size(); ++i)
+ for (size_t j = i + 1; j < face.size(); ++j) {
+ complex_.add_edge_without_blockers(Vertex_handle(face[i]), Vertex_handle(face[j]));
+ }
+ }
+ }
+
+ void done() { }
+};
+
+/**
+ *@brief Off reader visitor that can be passed to Off_reader to read a Skeleton_blocker_complex.
+ */
+template<typename Complex>
+class Skeleton_blocker_off_visitor_reader {
+ Complex& complex_;
+ typedef typename Complex::Vertex_handle Vertex_handle;
+ typedef typename Complex::Simplex Simplex;
+ typedef typename Complex::Point Point;
+
+ const bool load_only_points_;
+ std::vector<Point> points_;
+ std::vector<Simplex> maximal_faces_;
+
+ public:
+ explicit Skeleton_blocker_off_visitor_reader(Complex& complex, bool load_only_points = false) :
+ complex_(complex),
+ load_only_points_(load_only_points) { }
+
+ void init(int dim, int num_vertices, int num_faces, int num_edges) {
+ maximal_faces_.reserve(num_faces);
+ points_.reserve(num_vertices);
+ }
+
+ void point(const std::vector<double>& point) {
+ points_.emplace_back(point.begin(), point.end());
+ }
+
+ void maximal_face(const std::vector<int>& face) {
+ if (!load_only_points_) {
+ Simplex s;
+ for (auto x : face)
+ s.add_vertex(Vertex_handle(x));
+ maximal_faces_.emplace_back(s);
+ }
+ }
+
+ void done() {
+ complex_ = make_complex_from_top_faces<Complex>(maximal_faces_.begin(), maximal_faces_.end(),
+ points_.begin(), points_.end() );
+ }
+};
+
+/**
+ *@brief Class that allows to load a Skeleton_blocker_complex from an off file.
+ */
+template<typename Complex>
+class Skeleton_blocker_off_reader {
+ public:
+ /**
+ * name_file : file to read
+ * read_complex : complex that will receive the file content
+ * read_only_points : specify true if only the points must be read
+ */
+ Skeleton_blocker_off_reader(const std::string & name_file, Complex& read_complex,
+ bool read_only_points = false, bool is_flag = false) : valid_(false) {
+ std::ifstream stream(name_file);
+ if (stream.is_open()) {
+ if (is_flag || read_only_points) {
+ Skeleton_blocker_off_flag_visitor_reader<Complex> off_visitor(read_complex, read_only_points);
+ Off_reader off_reader(stream);
+ valid_ = off_reader.read(off_visitor);
+ } else {
+ Skeleton_blocker_off_visitor_reader<Complex> off_visitor(read_complex, read_only_points);
+ Off_reader off_reader(stream);
+ valid_ = off_reader.read(off_visitor);
+ }
+ }
+ }
+
+ /**
+ * return true iff reading did not meet problems.
+ */
+ bool is_valid() const {
+ return valid_;
+ }
+
+ private:
+ bool valid_;
+};
+
+template<typename Complex>
+class Skeleton_blocker_off_writer {
+ public:
+ /**
+ * name_file : file where the off will be written
+ * save_complex : complex that be outputted in the file
+ * for now only save triangles.
+ */
+ Skeleton_blocker_off_writer(const std::string & name_file, const Complex& save_complex) {
+ typedef typename Complex::Vertex_handle Vertex_handle;
+
+ std::ofstream stream(name_file);
+ if (stream.is_open()) {
+ stream << "OFF\n";
+ size_t num_triangles = std::distance(save_complex.triangle_range().begin(), save_complex.triangle_range().end());
+ stream << save_complex.num_vertices() << " " << num_triangles << " 0 \n";
+
+ // in case the complex has deactivated some vertices, eg only has vertices 0 2 5 7 for instance
+ // we compute a map from 0 2 5 7 to 0 1 2 3
+ std::map<Vertex_handle, size_t> vertex_num;
+ size_t current_vertex = 0;
+
+ for (auto v : save_complex.vertex_range()) {
+ vertex_num[v] = current_vertex++;
+ const auto& pt(save_complex.point(v));
+ for (auto x : pt)
+ stream << x << " ";
+ stream << std::endl;
+ }
+
+ for (const auto & t : save_complex.triangle_range()) {
+ stream << "3 ";
+ for (auto x : t)
+ stream << vertex_num[x] << " ";
+ stream << std::endl;
+ }
+ stream.close();
+ } else {
+ std::cerr << "could not open file " << name_file << std::endl;
+ }
+ }
+};
+
+} // namespace skeleton_blocker
+
+namespace skbl = skeleton_blocker;
+
+} // namespace Gudhi
+
+#endif // SKELETON_BLOCKER_SKELETON_BLOCKER_OFF_IO_H_
diff --git a/include/gudhi/Skeleton_blocker/Skeleton_blocker_simple_geometric_traits.h b/include/gudhi/Skeleton_blocker/Skeleton_blocker_simple_geometric_traits.h
new file mode 100644
index 00000000..fb4a1106
--- /dev/null
+++ b/include/gudhi/Skeleton_blocker/Skeleton_blocker_simple_geometric_traits.h
@@ -0,0 +1,96 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef SKELETON_BLOCKER_SKELETON_BLOCKER_SIMPLE_GEOMETRIC_TRAITS_H_
+#define SKELETON_BLOCKER_SKELETON_BLOCKER_SIMPLE_GEOMETRIC_TRAITS_H_
+
+#include <gudhi/Skeleton_blocker/Skeleton_blocker_simple_traits.h>
+
+#include <string>
+#include <sstream>
+
+namespace Gudhi {
+
+namespace skeleton_blocker {
+
+/**
+ * @extends SkeletonBlockerGeometricDS
+ * @ingroup skbl
+ * @brief Simple traits that is a model of SkeletonBlockerGeometricDS and
+ * can be passed as a template argument to Skeleton_blocker_geometric_complex
+ */
+template<typename GeometryTrait>
+struct Skeleton_blocker_simple_geometric_traits :
+ public Skeleton_blocker_simple_traits {
+ public:
+ typedef GeometryTrait GT;
+ typedef typename GT::Point Point;
+ typedef typename Skeleton_blocker_simple_traits::Root_vertex_handle Root_vertex_handle;
+ typedef typename Skeleton_blocker_simple_traits::Graph_vertex Simple_vertex;
+
+ /**
+ * @brief Vertex with a point attached.
+ */
+ class Simple_geometric_vertex : public Simple_vertex {
+ template<class ComplexGeometricTraits> friend class Skeleton_blocker_geometric_complex;
+ private:
+ Point point_;
+
+ Point& point() {
+ return point_;
+ }
+ const Point& point() const {
+ return point_;
+ }
+ };
+
+ class Simple_geometric_edge :
+ public Skeleton_blocker_simple_traits::Graph_edge {
+ int index_;
+ public:
+ Simple_geometric_edge()
+ : Skeleton_blocker_simple_traits::Graph_edge(),
+ index_(-1) {
+ }
+ /**
+ * @brief Allows to modify the index of the edge.
+ * The indices of the edge are used to store heap information
+ * in the edge contraction algorithm.
+ */
+ int& index() {
+ return index_;
+ }
+ int index() const {
+ return index_;
+ }
+ };
+
+ typedef Simple_geometric_vertex Graph_vertex;
+ typedef Skeleton_blocker_simple_traits::Graph_edge Graph_edge;
+};
+
+} // namespace skeleton_blocker
+
+namespace skbl = skeleton_blocker;
+
+} // namespace Gudhi
+
+#endif // SKELETON_BLOCKER_SKELETON_BLOCKER_SIMPLE_GEOMETRIC_TRAITS_H_
diff --git a/include/gudhi/Skeleton_blocker/Skeleton_blocker_simple_traits.h b/include/gudhi/Skeleton_blocker/Skeleton_blocker_simple_traits.h
new file mode 100644
index 00000000..31bec3b6
--- /dev/null
+++ b/include/gudhi/Skeleton_blocker/Skeleton_blocker_simple_traits.h
@@ -0,0 +1,184 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef SKELETON_BLOCKER_SKELETON_BLOCKER_SIMPLE_TRAITS_H_
+#define SKELETON_BLOCKER_SKELETON_BLOCKER_SIMPLE_TRAITS_H_
+
+#include <gudhi/Skeleton_blocker/Skeleton_blocker_simplex.h>
+
+#include <string>
+#include <sstream>
+
+namespace Gudhi {
+
+namespace skeleton_blocker {
+
+/**
+ * @extends SkeletonBlockerDS
+ * @ingroup skbl
+ * @brief Simple traits that is a model of SkeletonBlockerDS and
+ * can be passed as a template argument to Skeleton_blocker_complex
+ */
+struct Skeleton_blocker_simple_traits {
+ /**
+ * @brief Global and local handle similar to <a href="http://www.boost.org/doc/libs/1_38_0/libs/graph/doc/subgraph.html">boost subgraphs</a>.
+ * Vertices are stored in a vector.
+ * For the root simplicial complex, the local and global descriptors are the same.
+ * For a subcomplex L and one of its vertices 'v', the local descriptor of 'v' is its position in
+ * the vertex vector of the subcomplex L whereas its global descriptor is the position of 'v'
+ * in the vertex vector of the root simplicial complex.
+ */
+ struct Root_vertex_handle {
+ typedef int boost_vertex_handle;
+ explicit Root_vertex_handle(boost_vertex_handle val = -1)
+ : vertex(val) {
+ }
+ boost_vertex_handle vertex;
+
+ bool operator!=(const Root_vertex_handle& other) const {
+ return !(this->vertex == other.vertex);
+ }
+
+ bool operator==(const Root_vertex_handle& other) const {
+ return this->vertex == other.vertex;
+ }
+
+ bool operator<(const Root_vertex_handle& other) const {
+ return this->vertex < other.vertex;
+ }
+
+ friend std::ostream& operator <<(std::ostream& o,
+ const Root_vertex_handle & v) {
+ o << v.vertex;
+ return o;
+ }
+ };
+
+ struct Vertex_handle {
+ typedef int boost_vertex_handle;
+ explicit Vertex_handle(boost_vertex_handle val = -1)
+ : vertex(val) {
+ }
+
+ operator int() const { return static_cast<int>(vertex); }
+
+ boost_vertex_handle vertex;
+
+ bool operator==(const Vertex_handle& other) const {
+ return this->vertex == other.vertex;
+ }
+
+ bool operator!=(const Vertex_handle& other) const {
+ return this->vertex != other.vertex;
+ }
+
+ bool operator<(const Vertex_handle& other) const {
+ return this->vertex < other.vertex;
+ }
+
+ friend std::ostream& operator <<(std::ostream& o, const Vertex_handle & v) {
+ o << v.vertex;
+ return o;
+ }
+ };
+
+ class Graph_vertex {
+ bool is_active_;
+ Root_vertex_handle id_;
+
+ public:
+ virtual ~Graph_vertex() {
+ }
+
+ void activate() {
+ is_active_ = true;
+ }
+ void deactivate() {
+ is_active_ = false;
+ }
+ bool is_active() const {
+ return is_active_;
+ }
+ void set_id(Root_vertex_handle i) {
+ id_ = i;
+ }
+ Root_vertex_handle get_id() const {
+ return id_;
+ }
+
+ virtual std::string to_string() const {
+ std::ostringstream res;
+ res << id_;
+ return res.str();
+ }
+
+ friend std::ostream& operator <<(std::ostream& o, const Graph_vertex & v) {
+ o << v.to_string();
+ return o;
+ }
+ };
+
+ class Graph_edge {
+ Root_vertex_handle a_;
+ Root_vertex_handle b_;
+ int index_;
+
+ public:
+ Graph_edge()
+ : a_(-1),
+ b_(-1),
+ index_(-1) {
+ }
+
+ int& index() {
+ return index_;
+ }
+ int index() const {
+ return index_;
+ }
+
+ void setId(Root_vertex_handle a, Root_vertex_handle b) {
+ a_ = a;
+ b_ = b;
+ }
+
+ Root_vertex_handle first() const {
+ return a_;
+ }
+
+ Root_vertex_handle second() const {
+ return b_;
+ }
+
+ friend std::ostream& operator <<(std::ostream& o, const Graph_edge & v) {
+ o << "(" << v.a_ << "," << v.b_ << " - id = " << v.index();
+ return o;
+ }
+ };
+};
+
+} // namespace skeleton_blocker
+
+namespace skbl = skeleton_blocker;
+
+} // namespace Gudhi
+
+#endif // SKELETON_BLOCKER_SKELETON_BLOCKER_SIMPLE_TRAITS_H_
diff --git a/include/gudhi/Skeleton_blocker/Skeleton_blocker_simplex.h b/include/gudhi/Skeleton_blocker/Skeleton_blocker_simplex.h
new file mode 100644
index 00000000..3c7f1dd5
--- /dev/null
+++ b/include/gudhi/Skeleton_blocker/Skeleton_blocker_simplex.h
@@ -0,0 +1,376 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SKELETON_BLOCKER_SKELETON_BLOCKER_SIMPLEX_H_
+#define SKELETON_BLOCKER_SKELETON_BLOCKER_SIMPLEX_H_
+
+#include <cassert>
+#include <iostream>
+#include <set>
+#include <vector>
+#include <initializer_list>
+#include <string>
+#include <algorithm>
+
+namespace Gudhi {
+
+namespace skeleton_blocker {
+
+/**
+ *@brief Abstract simplex used in Skeleton blockers data-structure.
+ *
+ * An abstract simplex is represented as an ordered set of T elements,
+ * each element representing a vertex.
+ *
+ * The element representing a vertex can be SkeletonBlockerDS::Vertex_handle or SkeletonBlockerDS::Root_vertex_handle.
+ *
+ *
+ */
+template<typename T>
+
+class Skeleton_blocker_simplex {
+ private:
+ std::set<T> simplex_set;
+
+ public:
+ typedef typename T::boost_vertex_handle boost_vertex_handle;
+
+ typedef T Vertex_handle;
+
+ typedef typename std::set<T>::const_iterator Simplex_vertex_const_iterator;
+ typedef typename std::set<T>::iterator Simplex_vertex_iterator;
+
+ /** @name Constructors / Destructors / Initialization
+ */
+ //@{
+
+ // Skeleton_blocker_simplex():simplex_set() {}
+ void clear() {
+ simplex_set.clear();
+ }
+
+ Skeleton_blocker_simplex(std::initializer_list<T>& list) {
+ std::for_each(list.begin(), list.end(), [&] (T const& elt) {
+ add_vertex(elt);
+ });
+ }
+
+ template<typename ... Args>
+ explicit Skeleton_blocker_simplex(Args ... args) {
+ add_vertices(args...);
+ }
+
+ template<typename ... Args>
+ void add_vertices(T v, Args ... args) {
+ add_vertex(v);
+ add_vertices(args...);
+ }
+
+ void add_vertices(T v) {
+ add_vertex(v);
+ }
+
+ void add_vertices() {
+ }
+
+ /**
+ * Initialize a simplex with a string such as {0,1,2}
+ */
+ explicit Skeleton_blocker_simplex(std::string token) {
+ clear();
+ if ((token[0] == '{') && (token[token.size() - 1] == '}')) {
+ token.erase(0, 1);
+ token.erase(token.size() - 1, 1);
+ while (token.size() != 0) {
+ int coma_position = token.find_first_of(',');
+ // cout << "coma_position:"<<coma_position<<endl;
+ std::string n = token.substr(0, coma_position);
+ // cout << "token:"<<token<<endl;
+ token.erase(0, n.size() + 1);
+ add_vertex((T) (atoi(n.c_str())));
+ }
+ }
+ }
+
+ //@}
+
+ /** @name Simplex manipulation
+ */
+ //@{
+ /**
+ * Add the vertex v to the simplex:
+ * Note that adding two times the same vertex is
+ * the same that adding it once.
+ */
+ void add_vertex(T v) {
+ simplex_set.insert(v);
+ }
+
+ /**
+ * Remove the vertex v from the simplex:
+ */
+ void remove_vertex(T v) {
+ simplex_set.erase(v);
+ }
+
+ /**
+ * Intersects the simplex with the simplex a.
+ */
+ void intersection(const Skeleton_blocker_simplex & a) {
+ std::vector<T> v;
+ v.reserve((std::min)(simplex_set.size(), a.simplex_set.size()));
+
+ set_intersection(simplex_set.begin(), simplex_set.end(),
+ a.simplex_set.begin(), a.simplex_set.end(),
+ std::back_inserter(v));
+ clear();
+ for (auto i : v)
+ simplex_set.insert(i);
+ }
+
+ /**
+ * Substracts a from the simplex.
+ */
+ void difference(const Skeleton_blocker_simplex & a) {
+ std::vector<T> v;
+ v.reserve(simplex_set.size());
+
+ set_difference(simplex_set.begin(), simplex_set.end(),
+ a.simplex_set.begin(), a.simplex_set.end(),
+ std::back_inserter(v));
+
+ clear();
+ for (auto i : v)
+ simplex_set.insert(i);
+ }
+
+ /**
+ * Add vertices of a to the simplex.
+ */
+ void union_vertices(const Skeleton_blocker_simplex & a) {
+ std::vector<T> v;
+ v.reserve(simplex_set.size() + a.simplex_set.size());
+
+ set_union(simplex_set.begin(), simplex_set.end(), a.simplex_set.begin(),
+ a.simplex_set.end(), std::back_inserter(v));
+ clear();
+ simplex_set.insert(v.begin(), v.end());
+ }
+
+ typename std::set<T>::const_iterator begin() const {
+ return simplex_set.cbegin();
+ }
+
+ typename std::set<T>::const_iterator end() const {
+ return simplex_set.cend();
+ }
+
+ typename std::set<T>::const_reverse_iterator rbegin() const {
+ return simplex_set.crbegin();
+ }
+
+ typename std::set<T>::const_reverse_iterator rend() const {
+ return simplex_set.crend();
+ }
+
+
+ typename std::set<T>::iterator begin() {
+ return simplex_set.begin();
+ }
+
+ typename std::set<T>::iterator end() {
+ return simplex_set.end();
+ }
+
+ //@}
+
+ /** @name Queries
+ */
+ //@{
+ /**
+ * Returns the dimension of the simplex.
+ */
+ int dimension() const {
+ return (simplex_set.size() - 1);
+ }
+
+ bool empty() const {
+ return simplex_set.empty();
+ }
+
+ /**
+ * Returns the first and smallest vertex of the simplex.
+ *
+ * Be careful : assumes the simplex is non-empty.
+ */
+ T first_vertex() const {
+ assert(!empty());
+ return *(begin());
+ }
+
+ /**
+ * Returns the last and greatest vertex of the simplex.
+ *
+ * Be careful : assumes the simplex is non-empty.
+ */
+ T last_vertex() const {
+ assert(!empty());
+ return *(simplex_set.rbegin());
+ }
+ /**
+ * @return true iff the simplex contains the simplex a.
+ */
+ bool contains(const Skeleton_blocker_simplex & a) const {
+ return includes(simplex_set.cbegin(), simplex_set.cend(),
+ a.simplex_set.cbegin(), a.simplex_set.cend());
+ }
+
+ /**
+ * @return true iff the simplex contains the difference \f$ a \setminus b \f$.
+ */
+ bool contains_difference(const Skeleton_blocker_simplex& a,
+ const Skeleton_blocker_simplex& b) const {
+ auto first1 = begin();
+ auto last1 = end();
+
+ auto first2 = a.begin();
+ auto last2 = a.end();
+
+ while (first2 != last2) {
+ // we ignore vertices of b
+ if (b.contains(*first2)) {
+ ++first2;
+ } else {
+ if ((first1 == last1) || (*first2 < *first1))
+ return false;
+ if (!(*first1 < *first2))
+ ++first2;
+ ++first1;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @return true iff the simplex contains the difference \f$ a \setminus \{ x \} \f$.
+ */
+ bool contains_difference(const Skeleton_blocker_simplex& a, T x) const {
+ auto first1 = begin();
+ auto last1 = end();
+
+ auto first2 = a.begin();
+ auto last2 = a.end();
+
+ while (first2 != last2) {
+ // we ignore vertices x
+ if (x == *first2) {
+ ++first2;
+ } else {
+ if ((first1 == last1) || (*first2 < *first1))
+ return false;
+ if (!(*first1 < *first2))
+ ++first2;
+ ++first1;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @return true iff the simplex contains the difference \f$ a \setminus \{ x,y \} \f$.
+ */
+ bool contains_difference(const Skeleton_blocker_simplex& a, T x, T y) const {
+ auto first1 = begin();
+ auto last1 = end();
+
+ auto first2 = a.begin();
+ auto last2 = a.end();
+
+ while (first2 != last2) {
+ // we ignore vertices of x,y
+ if (x == *first2 || y == *first2) {
+ ++first2;
+ } else {
+ if ((first1 == last1) || (*first2 < *first1))
+ return false;
+ if (!(*first1 < *first2))
+ ++first2;
+ ++first1;
+ }
+ }
+ return true;
+ }
+
+ bool contains(T v) const {
+ return (simplex_set.find(v) != simplex_set.end());
+ }
+
+ bool disjoint(const Skeleton_blocker_simplex& a) const {
+ std::vector<T> v;
+ v.reserve(std::min(simplex_set.size(), a.simplex_set.size()));
+
+ set_intersection(simplex_set.cbegin(), simplex_set.cend(),
+ a.simplex_set.cbegin(), a.simplex_set.cend(),
+ std::back_inserter(v));
+
+ return (v.size() == 0);
+ }
+
+ bool operator==(const Skeleton_blocker_simplex& other) const {
+ return (this->simplex_set == other.simplex_set);
+ }
+
+ bool operator!=(const Skeleton_blocker_simplex& other) const {
+ return (this->simplex_set != other.simplex_set);
+ }
+
+ bool operator<(const Skeleton_blocker_simplex& other) const {
+ return (std::lexicographical_compare(this->simplex_set.begin(),
+ this->simplex_set.end(), other.begin(),
+ other.end()));
+ }
+
+ //@}
+
+ friend std::ostream& operator <<(std::ostream& o,
+ const Skeleton_blocker_simplex & sigma) {
+ bool first = true;
+ o << "{";
+ for (auto i : sigma) {
+ if (first)
+ first = false;
+ else
+ o << ",";
+ o << i;
+ }
+ o << "}";
+ return o;
+ }
+};
+
+} // namespace skeleton_blocker
+
+namespace skbl = skeleton_blocker;
+
+} // namespace Gudhi
+
+#endif // SKELETON_BLOCKER_SKELETON_BLOCKER_SIMPLEX_H_
diff --git a/include/gudhi/Skeleton_blocker/Skeleton_blocker_sub_complex.h b/include/gudhi/Skeleton_blocker/Skeleton_blocker_sub_complex.h
new file mode 100644
index 00000000..196fe8c0
--- /dev/null
+++ b/include/gudhi/Skeleton_blocker/Skeleton_blocker_sub_complex.h
@@ -0,0 +1,292 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SKELETON_BLOCKER_SKELETON_BLOCKER_SUB_COMPLEX_H_
+#define SKELETON_BLOCKER_SKELETON_BLOCKER_SUB_COMPLEX_H_
+
+#include <gudhi/Skeleton_blocker_complex.h>
+#include <gudhi/Skeleton_blocker/Skeleton_blocker_simplex.h>
+#include <gudhi/Debug_utils.h>
+
+#include <map>
+#include <vector>
+
+namespace Gudhi {
+
+namespace skeleton_blocker {
+
+/**
+ * @brief Simplicial subcomplex of a complex represented by a skeleton/blockers pair.
+ * @extends Skeleton_blocker_complex
+ * @details Stores a subcomplex of a simplicial complex.
+ * To simplify explanations below, we will suppose that :
+ * - K is the root simplicial complex
+ * - L is a subcomplex of K.
+ *
+ * One vertex of K may exists in L but with a different address.
+ * To be able to locate the vertices in K from vertices of L, the class
+ * stores a map 'adresses' between vertices of K and vertices of L.
+ *
+ * Note that the type for handle of vertices of L is 'Vertex_handle' and
+ * the type for handle of vertices of K is 'Root_vertex_handle'.
+ *
+ * The template ComplexType is type of the root complex. It allows to know
+ * if the subcomplex is geometric or not.
+ * It has to be either 'Skeleton_blockers_complex' or 'Skeleton_blockers_geometric_complex'.
+ *
+ */
+template<typename ComplexType>
+class Skeleton_blocker_sub_complex : public ComplexType {
+ protected:
+ template<class T> friend class Skeleton_blocker_link_complex;
+
+ typedef typename ComplexType::Graph Graph;
+ typedef typename ComplexType::Edge_handle Edge_handle;
+
+ typedef typename ComplexType::boost_vertex_handle boost_vertex_handle;
+
+ public:
+ using ComplexType::add_vertex;
+ using ComplexType::add_edge_without_blockers;
+ using ComplexType::add_blocker;
+
+ typedef typename ComplexType::Vertex_handle Vertex_handle;
+ typedef typename ComplexType::Root_vertex_handle Root_vertex_handle;
+ typedef typename ComplexType::Simplex Simplex;
+ typedef typename ComplexType::Root_simplex_handle Root_simplex_handle;
+
+ protected:
+ /**
+ * @brief Determines whether all proper faces of simplex 'sigma' belong to 'link1' \cup 'link2'
+ * where 'link1' and 'link2' are subcomplexes of the same complex of type ComplexType
+ */
+ typedef std::map<Root_vertex_handle, Vertex_handle> IdAddressMap;
+ typedef typename IdAddressMap::value_type AddressPair;
+ typedef typename IdAddressMap::iterator IdAddressMapIterator;
+ typedef typename IdAddressMap::const_iterator IdAddressMapConstIterator;
+ std::map<Root_vertex_handle, Vertex_handle> adresses;
+
+ public:
+ /**
+ * Add a vertex 'global' of K to L. When added to L, this vertex will receive
+ * another number, addresses(global), its local adress.
+ * return the adress where the vertex lay on L.
+ * The vertex corresponding to 'global' must not be already present
+ * in the complex.
+ */
+ Vertex_handle add_vertex(Root_vertex_handle global) {
+ assert(!this->contains_vertex(global));
+ Vertex_handle address(boost::add_vertex(this->skeleton));
+ this->num_vertices_++;
+ (*this)[address].activate();
+ (*this)[address].set_id(global);
+ adresses.insert(AddressPair(global, address));
+ this->degree_.push_back(0);
+ return address;
+ }
+
+ /**
+ * Add an edge (v1_root,v2_root) to the sub-complex.
+ * It assumes that both vertices corresponding to v1_root and v2_root are present
+ * in the sub-complex.
+ */
+ void add_edge_without_blockers(Root_vertex_handle v1_root, Root_vertex_handle v2_root) {
+ auto v1_sub(this->get_address(v1_root));
+ auto v2_sub(this->get_address(v2_root));
+ assert(v1_sub && v2_sub);
+ this->ComplexType::add_edge_without_blockers(*v1_sub, *v2_sub);
+ }
+
+ /**
+ * Add a blocker to the sub-complex.
+ * It assumes that all vertices of blocker_root are present
+ * in the sub-complex.
+ */
+ void add_blocker(const Root_simplex_handle& blocker_root) {
+ auto blocker_sub = this->get_address(blocker_root);
+ assert(blocker_sub);
+ this->add_blocker(new Simplex(*blocker_sub));
+ }
+
+ public:
+ /**
+ * Constructs the restricted complex of 'parent_complex' to
+ * vertices of 'simplex'.
+ */
+ void make_restricted_complex(const ComplexType & parent_complex,
+ const Simplex& simplex) {
+ this->clear();
+ // add vertices to the sub complex
+ for (auto x : simplex) {
+ assert(parent_complex.contains_vertex(x));
+ auto x_local = this->add_vertex(parent_complex[x].get_id());
+ (*this)[x_local] = parent_complex[x];
+ }
+
+ // add edges to the sub complex
+ for (auto x : simplex) {
+ // x_neigh is the neighbor of x intersected with vertices_simplex
+ Simplex x_neigh;
+ parent_complex.add_neighbours(x, x_neigh, true);
+ x_neigh.intersection(simplex);
+ for (auto y : x_neigh) {
+ this->add_edge_without_blockers(parent_complex[x].get_id(), parent_complex[y].get_id());
+ }
+ }
+
+ // add blockers to the sub complex
+ for (auto blocker : parent_complex.const_blocker_range()) {
+ // check if it is the first time we encounter the blocker
+ if (simplex.contains(*blocker)) {
+ Root_simplex_handle blocker_root(parent_complex.get_id(*(blocker)));
+ Simplex blocker_restr(
+ *(this->get_simplex_address(blocker_root)));
+ this->add_blocker(new Simplex(blocker_restr));
+ }
+ }
+ }
+
+ void clear() {
+ adresses.clear();
+ ComplexType::clear();
+ }
+
+ /**
+ * Compute the local vertex in L corresponding to the vertex global in K.
+ * runs in O(log n) if n = num_vertices()
+ */
+ boost::optional<Vertex_handle> get_address(Root_vertex_handle global) const {
+ boost::optional < Vertex_handle > res;
+ IdAddressMapConstIterator it = adresses.find(global);
+ if (it == adresses.end())
+ res.reset();
+ else
+ res = (*it).second;
+ return res;
+ }
+
+ // /**
+ // * Allocates a simplex in L corresponding to the simplex s in K
+ // * with its local adresses and returns an AddressSimplex.
+ // */
+ // boost::optional<Simplex> get_address(const Root_simplex_handle & s) const;
+
+// private:
+ /**
+ * same as get_address except that it will return a simplex in any case.
+ * The vertices that were not found are not added.
+ */
+ // @remark should be private but problem with VS
+ std::vector<boost::optional<Vertex_handle> > get_addresses(
+ const Root_simplex_handle & s) const {
+ std::vector < boost::optional<Vertex_handle> > res;
+ for (auto i : s) {
+ res.push_back(get_address(i));
+ }
+ return res;
+ }
+};
+
+/**
+ * @remark remarque perte de temps a creer un nouveau simplexe a chaque fois
+ * alors qu'on pourrait utiliser a la place de 'addresses_sigma_in_link'
+ * un simplex avec des valeurs sp�ciales ComplexDS::null_vertex par exemple
+ * pour indiquer qu'un vertex n'appartient pas au complex
+ */
+template<typename ComplexType>
+bool proper_face_in_union(
+ Skeleton_blocker_sub_complex<ComplexType> & link,
+ std::vector<boost::optional<typename ComplexType::Vertex_handle> > & addresses_sigma_in_link,
+ int vertex_to_be_ignored) {
+ // we test that all vertices of 'addresses_sigma_in_link' but 'vertex_to_be_ignored'
+ // are in link1 if it is the case we construct the corresponding simplex
+ bool vertices_sigma_are_in_link = true;
+ typename ComplexType::Simplex sigma_in_link;
+ for (int i = 0; i < addresses_sigma_in_link.size(); ++i) {
+ if (i != vertex_to_be_ignored) {
+ if (!addresses_sigma_in_link[i]) {
+ vertices_sigma_are_in_link = false;
+ break;
+ } else {
+ sigma_in_link.add_vertex(*addresses_sigma_in_link[i]);
+ }
+ }
+ }
+ // If one of vertices of the simplex is not in the complex then it returns false
+ // Otherwise, it tests if the simplex is in the complex
+ return vertices_sigma_are_in_link && link.contains(sigma_in_link);
+}
+
+/*
+ template<typename ComplexType>
+ bool
+ proper_faces_in_union(Skeleton_blocker_simplex<typename ComplexType::Root_vertex_handle> & sigma, Skeleton_blocker_sub_complex<ComplexType> & link1, Skeleton_blocker_sub_complex<ComplexType> & link2)
+ {
+ typedef typename ComplexType::Vertex_handle Vertex_handle;
+ std::vector<boost::optional<Vertex_handle> > addresses_sigma_in_link1 = link1.get_addresses(sigma);
+ std::vector<boost::optional<Vertex_handle> > addresses_sigma_in_link2 = link2.get_addresses(sigma);
+
+ for (int current_index = 0; current_index < addresses_sigma_in_link1.size(); ++current_index)
+ {
+
+ if (!proper_face_in_union(link1, addresses_sigma_in_link1, current_index)
+ && !proper_face_in_union(link2, addresses_sigma_in_link2, current_index)){
+ return false;
+ }
+ }
+ return true;
+ }*/
+
+// Remark: this function should be friend in order to leave get_adresses private
+// however doing so seemes currently not possible due to a visual studio bug c2668
+// "the compiler does not support partial ordering of template functions as specified in the C++ Standard"
+// http://www.serkey.com/error-c2668-ambiguous-call-to-overloaded-function-bb45ft.html
+template<typename ComplexType>
+bool proper_faces_in_union(
+ Skeleton_blocker_simplex<typename ComplexType::Root_vertex_handle> & sigma,
+ Skeleton_blocker_sub_complex<ComplexType> & link1,
+ Skeleton_blocker_sub_complex<ComplexType> & link2) {
+ typedef typename ComplexType::Vertex_handle Vertex_handle;
+ std::vector < boost::optional<Vertex_handle> > addresses_sigma_in_link1 =
+ link1.get_addresses(sigma);
+ std::vector < boost::optional<Vertex_handle> > addresses_sigma_in_link2 =
+ link2.get_addresses(sigma);
+
+ for (int current_index = 0; current_index < addresses_sigma_in_link1.size();
+ ++current_index) {
+ if (!proper_face_in_union(link1, addresses_sigma_in_link1, current_index)
+ && !proper_face_in_union(link2, addresses_sigma_in_link2,
+ current_index)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace skeleton_blocker
+
+namespace skbl = skeleton_blocker;
+
+} // namespace Gudhi
+
+#endif // SKELETON_BLOCKER_SKELETON_BLOCKER_SUB_COMPLEX_H_
+
diff --git a/include/gudhi/Skeleton_blocker/internal/Top_faces.h b/include/gudhi/Skeleton_blocker/internal/Top_faces.h
new file mode 100644
index 00000000..39d95661
--- /dev/null
+++ b/include/gudhi/Skeleton_blocker/internal/Top_faces.h
@@ -0,0 +1,72 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef SKELETON_BLOCKER_INTERNAL_TOP_FACES_H_
+#define SKELETON_BLOCKER_INTERNAL_TOP_FACES_H_
+
+#include <list>
+#include <vector>
+#include <set>
+
+namespace Gudhi {
+
+namespace skeleton_blocker {
+
+template<typename SimplexHandle>
+std::list<SimplexHandle> subfaces(SimplexHandle top_face) {
+ std::list<SimplexHandle> res;
+ if (top_face.dimension() == -1) return res;
+ if (top_face.dimension() == 0) {
+ res.push_back(top_face);
+ return res;
+ } else {
+ auto first_vertex = top_face.first_vertex();
+ top_face.remove_vertex(first_vertex);
+ res = subfaces(top_face);
+ std::list<SimplexHandle> copy = res;
+ for (auto& simplex : copy) {
+ simplex.add_vertex(first_vertex);
+ }
+ res.push_back(SimplexHandle(first_vertex));
+ res.splice(res.end(), copy);
+ return res;
+ }
+}
+
+/**
+ * add all faces of top_face in simplices_per_dimension
+ */
+template<typename SimplexHandle>
+void register_faces(std::vector< std::set<SimplexHandle> >& simplices_per_dimension,
+ const SimplexHandle& top_face) {
+ std::list<SimplexHandle> subfaces_list = subfaces(top_face);
+ for (auto& simplex : subfaces_list) {
+ simplices_per_dimension[simplex.dimension()].insert(simplex);
+ }
+}
+
+} // namespace skeleton_blocker
+
+namespace skbl = skeleton_blocker;
+
+} // namespace Gudhi
+
+#endif // SKELETON_BLOCKER_INTERNAL_TOP_FACES_H_
diff --git a/include/gudhi/Skeleton_blocker/internal/Trie.h b/include/gudhi/Skeleton_blocker/internal/Trie.h
new file mode 100644
index 00000000..cdc47b8a
--- /dev/null
+++ b/include/gudhi/Skeleton_blocker/internal/Trie.h
@@ -0,0 +1,270 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Méditerranée (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#ifndef SKELETON_BLOCKER_INTERNAL_TRIE_H_
+#define SKELETON_BLOCKER_INTERNAL_TRIE_H_
+
+#include <memory>
+#include <vector>
+#include <deque>
+#include <set>
+
+namespace Gudhi {
+
+namespace skeleton_blocker {
+
+template<typename SimplexHandle>
+struct Trie {
+ typedef SimplexHandle Simplex;
+ typedef typename SimplexHandle::Vertex_handle Vertex_handle;
+
+ Vertex_handle v;
+ std::vector<std::shared_ptr<Trie> > childs;
+ // std::vector<std::unique_ptr<Trie> > childs; -> use of deleted function
+ private:
+ const Trie* parent_;
+
+ public:
+ Trie() : parent_(0) { }
+
+ Trie(Vertex_handle v_) : v(v_), parent_(0) { }
+
+ Trie(Vertex_handle v_, Trie* parent) : v(v_), parent_(parent) { }
+
+ bool operator==(const Trie& other) const {
+ return (v == other.v);
+ }
+
+ void add_child(Trie* child) {
+ if (child) {
+ std::shared_ptr<Trie> ptr_to_add(child);
+ childs.push_back(ptr_to_add);
+ child->parent_ = this;
+ }
+ }
+
+ typedef typename Simplex::Simplex_vertex_const_iterator Simplex_vertex_const_iterator;
+
+ Trie* make_trie(Simplex_vertex_const_iterator s_it, Simplex_vertex_const_iterator s_end) {
+ if (s_it == s_end) {
+ return 0;
+ } else {
+ Trie* res = new Trie(*s_it);
+ Trie* child = make_trie(++s_it, s_end);
+ res->add_child(child);
+ return res;
+ }
+ }
+
+ private:
+ // go down recursively in the tree while advancing the simplex iterator.
+ // when it reaches a leaf, it inserts the remaining that is not present
+ void add_simplex_helper(Simplex_vertex_const_iterator s_it, Simplex_vertex_const_iterator s_end) {
+ assert(*s_it == v);
+ ++s_it;
+ if (s_it == s_end) return;
+ if (!is_leaf()) {
+ for (auto child : childs) {
+ if (child->v == *s_it)
+ return child->add_simplex_helper(s_it, s_end);
+ }
+ // s_it is not found and needs to be inserted
+ }
+ // not leaf -> remaining of s needs to be inserted
+ Trie * son_with_what_remains_of_s(make_trie(s_it, s_end));
+ add_child(son_with_what_remains_of_s);
+ return;
+ }
+
+ void maximal_faces_helper(std::vector<Simplex>& res) const {
+ if (is_leaf()) res.push_back(simplex());
+ else
+ for (auto child : childs)
+ child->maximal_faces_helper(res);
+ }
+
+ public:
+ /**
+ * adds the simplex to the trie
+ */
+ void add_simplex(const Simplex& s) {
+ if (s.empty()) return;
+ assert(v == s.first_vertex());
+ add_simplex_helper(s.begin(), s.end());
+ }
+
+ std::vector<Simplex> maximal_faces() const {
+ std::vector<Simplex> res;
+ maximal_faces_helper(res);
+ return res;
+ }
+
+ /**
+ * Goes to the root in the trie to consitute simplex
+ */
+ void add_vertices_up_to_the_root(Simplex& res) const {
+ res.add_vertex(v);
+ if (parent_)
+ parent_->add_vertices_up_to_the_root(res);
+ }
+
+ Simplex simplex() const {
+ Simplex res;
+ add_vertices_up_to_the_root(res);
+ return res;
+ }
+
+ bool is_leaf() const {
+ return childs.empty();
+ }
+
+ bool is_root() const {
+ return parent_ == 0;
+ }
+
+ const Trie* parent() {
+ return parent_;
+ }
+
+ void remove_leaf() {
+ assert(is_leaf);
+ if (!is_root())
+ parent_->childs.erase(this);
+ }
+
+ /**
+ * true iff the simplex corresponds to one node in the trie
+ */
+ bool contains(const Simplex& s) const {
+ Trie const* current = this;
+ if (s.empty()) return true;
+ if (current->v != s.first_vertex()) return false;
+ auto s_pos = s.begin();
+ ++s_pos;
+ while (s_pos != s.end() && current != 0) {
+ bool found = false;
+ for (const auto child : current->childs) {
+ if (child->v == *s_pos) {
+ ++s_pos;
+ current = child.get();
+ found = true;
+ break;
+ }
+ }
+ if (!found) return false;
+ }
+ return current != 0;
+ }
+
+ Trie* go_bottom_left() {
+ if (is_leaf())
+ return this;
+ else
+ return (*childs.begin())->go_bottom_left();
+ }
+
+ friend std::ostream& operator<<(std::ostream& stream, const Trie& trie) {
+ stream << "T( " << trie.v << " ";
+ for (auto t : trie.childs)
+ stream << *t;
+ stream << ")";
+ return stream;
+ }
+};
+
+template<typename SimplexHandle>
+struct Tries {
+ typedef typename SimplexHandle::Vertex_handle Vertex_handle;
+ typedef SimplexHandle Simplex;
+
+ typedef Trie<Simplex> STrie;
+
+ template<typename SimpleHandleOutputIterator>
+ Tries(unsigned num_vertices, SimpleHandleOutputIterator simplex_begin, SimpleHandleOutputIterator simplex_end) :
+ cofaces_(num_vertices, 0) {
+ for (auto i = 0u; i < num_vertices; ++i)
+ cofaces_[i] = new STrie(Vertex_handle(i));
+ for (auto s_it = simplex_begin; s_it != simplex_end; ++s_it) {
+ if (s_it->dimension() >= 1)
+ cofaces_[s_it->first_vertex()]->add_simplex(*s_it);
+ }
+ }
+
+ ~Tries() {
+ for (STrie* t : cofaces_)
+ delete t;
+ }
+
+ // return a simplex that consists in all u such uv is an edge and u>v
+
+ Simplex positive_neighbors(Vertex_handle v) const {
+ Simplex res;
+ for (auto child : cofaces_[v]->childs)
+ res.add_vertex(child->v);
+ return res;
+ }
+
+ bool contains(const Simplex& s) const {
+ auto first_v = s.first_vertex();
+ return cofaces_[first_v]->contains(s);
+ }
+
+ friend std::ostream& operator<<(std::ostream& stream, const Tries& tries) {
+ for (auto trie : tries.cofaces_)
+ stream << *trie << std::endl;
+ return stream;
+ }
+
+ // init_next_dimension must be called first
+
+ std::vector<Simplex> next_dimension_simplices() const {
+ std::vector<Simplex> res;
+ while (!to_see_.empty() && to_see_.front()->simplex().dimension() == current_dimension_) {
+ res.emplace_back(to_see_.front()->simplex());
+ for (auto child : to_see_.front()->childs)
+ to_see_.push_back(child.get());
+ to_see_.pop_front();
+ }
+ ++current_dimension_;
+ return res;
+ }
+
+ void init_next_dimension() const {
+ for (auto trie : cofaces_)
+ to_see_.push_back(trie);
+ }
+
+ private:
+ mutable std::deque<STrie*> to_see_;
+ mutable unsigned current_dimension_ = 0;
+ std::vector<STrie*> cofaces_;
+};
+
+} // namespace skeleton_blocker
+
+namespace skbl = skeleton_blocker;
+
+} // namespace Gudhi
+
+#endif // SKELETON_BLOCKER_INTERNAL_TRIE_H_
diff --git a/include/gudhi/Skeleton_blocker/iterators/Skeleton_blockers_blockers_iterators.h b/include/gudhi/Skeleton_blocker/iterators/Skeleton_blockers_blockers_iterators.h
new file mode 100644
index 00000000..4dbc9ed3
--- /dev/null
+++ b/include/gudhi/Skeleton_blocker/iterators/Skeleton_blockers_blockers_iterators.h
@@ -0,0 +1,133 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef SKELETON_BLOCKER_ITERATORS_SKELETON_BLOCKERS_BLOCKERS_ITERATORS_H_
+#define SKELETON_BLOCKER_ITERATORS_SKELETON_BLOCKERS_BLOCKERS_ITERATORS_H_
+
+#include <boost/iterator/iterator_facade.hpp>
+
+namespace Gudhi {
+
+namespace skeleton_blocker {
+
+/**
+ * @brief Iterator through the blockers of a vertex.
+ */
+// ReturnType = const Simplex* or Simplex*
+// MapIteratorType = BlockerMapConstIterator or BlockerMapIterator
+
+template<typename MapIteratorType, typename ReturnType>
+class Blocker_iterator_internal : public boost::iterator_facade<
+Blocker_iterator_internal<MapIteratorType, ReturnType>,
+ReturnType,
+boost::forward_traversal_tag,
+ReturnType
+> {
+ private:
+ MapIteratorType current_position;
+ MapIteratorType end_of_map;
+
+ public:
+ Blocker_iterator_internal() : current_position() { }
+
+ Blocker_iterator_internal(MapIteratorType position, MapIteratorType end_of_map_) :
+ current_position(position), end_of_map(end_of_map_) { }
+
+ bool equal(const Blocker_iterator_internal& other) const {
+ return current_position == other.current_position;
+ }
+
+ void increment() {
+ goto_next_blocker();
+ }
+
+ ReturnType dereference() const {
+ return (current_position->second);
+ }
+
+ private:
+ /**
+ * Let the current pair be (v,sigma) where v is a vertex and sigma is a blocker.
+ * If v is not the first vertex of sigma then we already have seen sigma as a blocker
+ * and we look for the next one.
+ */
+ void goto_next_blocker() {
+ do {
+ ++current_position;
+ } while (!(current_position == end_of_map) && !first_time_blocker_is_seen());
+ }
+
+ bool first_time_blocker_is_seen() const {
+ return current_position->first == current_position->second->first_vertex();
+ }
+};
+
+/**
+ * @brief Iterator through the blockers of a vertex
+ */
+// ReturnType = const Simplex* or Simplex*
+// MapIteratorType = BlockerMapConstIterator or BlockerMapIterator
+
+template<typename MapIteratorType, typename ReturnType>
+class Blocker_iterator_around_vertex_internal : public boost::iterator_facade<
+Blocker_iterator_around_vertex_internal<MapIteratorType, ReturnType>,
+ReturnType,
+boost::forward_traversal_tag,
+ReturnType
+> {
+ private:
+ MapIteratorType current_position_;
+
+ public:
+ Blocker_iterator_around_vertex_internal() : current_position_() { }
+
+ Blocker_iterator_around_vertex_internal(MapIteratorType position) :
+ current_position_(position) { }
+
+ Blocker_iterator_around_vertex_internal& operator=(Blocker_iterator_around_vertex_internal other) {
+ this->current_position_ = other.current_position_;
+ return *this;
+ }
+
+ bool equal(const Blocker_iterator_around_vertex_internal& other) const {
+ return current_position_ == other.current_position_;
+ }
+
+ void increment() {
+ current_position_++;
+ }
+
+ ReturnType dereference() const {
+ return (current_position_->second);
+ }
+
+ MapIteratorType current_position() {
+ return this->current_position_;
+ }
+};
+
+} // namespace skeleton_blocker
+
+namespace skbl = skeleton_blocker;
+
+} // namespace Gudhi
+
+#endif // SKELETON_BLOCKER_ITERATORS_SKELETON_BLOCKERS_BLOCKERS_ITERATORS_H_
diff --git a/include/gudhi/Skeleton_blocker/iterators/Skeleton_blockers_edges_iterators.h b/include/gudhi/Skeleton_blocker/iterators/Skeleton_blockers_edges_iterators.h
new file mode 100644
index 00000000..15618932
--- /dev/null
+++ b/include/gudhi/Skeleton_blocker/iterators/Skeleton_blockers_edges_iterators.h
@@ -0,0 +1,146 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef SKELETON_BLOCKER_ITERATORS_SKELETON_BLOCKERS_EDGES_ITERATORS_H_
+#define SKELETON_BLOCKER_ITERATORS_SKELETON_BLOCKERS_EDGES_ITERATORS_H_
+
+#include <boost/iterator/iterator_facade.hpp>
+#include <boost/graph/adjacency_list.hpp>
+
+#include <utility> // for pair<>
+
+namespace Gudhi {
+
+namespace skeleton_blocker {
+
+template<typename SkeletonBlockerComplex>
+class Edge_around_vertex_iterator : public boost::iterator_facade <Edge_around_vertex_iterator<SkeletonBlockerComplex>
+, typename SkeletonBlockerComplex::Edge_handle const, boost::forward_traversal_tag
+, typename SkeletonBlockerComplex::Edge_handle const> {
+ friend class boost::iterator_core_access;
+
+ typedef SkeletonBlockerComplex Complex;
+ typedef typename Complex::boost_adjacency_iterator boost_adjacency_iterator;
+ typedef typename Complex::Vertex_handle Vertex_handle;
+ typedef typename Complex::Edge_handle Edge_handle;
+
+ private:
+ const Complex* complex;
+ Vertex_handle v;
+
+ boost_adjacency_iterator current_;
+ boost_adjacency_iterator end_;
+
+ public:
+ Edge_around_vertex_iterator() : complex(NULL) { }
+
+ Edge_around_vertex_iterator(const Complex* complex_, Vertex_handle v_) :
+ complex(complex_),
+ v(v_) {
+ tie(current_, end_) = adjacent_vertices(v.vertex, complex->skeleton);
+ }
+
+ /**
+ * returns an iterator to the end
+ */
+ Edge_around_vertex_iterator(const Complex* complex_, Vertex_handle v_, int end) :
+ complex(complex_),
+ v(v_) {
+ tie(current_, end_) = adjacent_vertices(v.vertex, complex->skeleton);
+ set_end();
+ }
+
+ bool equal(const Edge_around_vertex_iterator& other) const {
+ return (complex == other.complex) && (v == other.v) && (current_ == other.current_) && (end_ == other.end_);
+ }
+
+ void increment() {
+ if (current_ != end_)
+ ++current_;
+ }
+
+ Edge_handle dereference() const {
+ return *(*complex)[std::make_pair(v, static_cast<Vertex_handle> (*current_))];
+ }
+
+ private:
+ // remove this ugly hack
+ void set_end() {
+ current_ = end_;
+ }
+};
+
+/**
+ *@brief Iterator on the edges of a simplicial complex.
+ *
+ */
+template<typename SkeletonBlockerComplex>
+class Edge_iterator : public boost::iterator_facade <Edge_iterator<SkeletonBlockerComplex>
+, typename SkeletonBlockerComplex::Edge_handle const
+, boost::forward_traversal_tag
+, typename SkeletonBlockerComplex::Edge_handle const> {
+ friend class boost::iterator_core_access;
+
+ public:
+ typedef SkeletonBlockerComplex Complex;
+ typedef typename Complex::boost_edge_iterator boost_edge_iterator;
+ typedef typename Complex::Edge_handle Edge_handle;
+
+ const Complex* complex;
+ std::pair<boost_edge_iterator, boost_edge_iterator> edge_iterator;
+
+ Edge_iterator() : complex(NULL) { }
+
+ Edge_iterator(const SkeletonBlockerComplex* complex_) :
+ complex(complex_),
+ edge_iterator(boost::edges(complex_->skeleton)) { }
+
+ /**
+ * return an iterator to the end
+ */
+ Edge_iterator(const SkeletonBlockerComplex* complex_, int end) :
+ complex(complex_),
+ edge_iterator(boost::edges(complex_->skeleton)) {
+ edge_iterator.first = edge_iterator.second;
+ }
+
+ bool equal(const Edge_iterator& other) const {
+ return (complex == other.complex) && (edge_iterator == other.edge_iterator);
+ }
+
+ void increment() {
+ if (edge_iterator.first != edge_iterator.second) {
+ ++(edge_iterator.first);
+ }
+ }
+
+ Edge_handle dereference() const {
+ return (*(edge_iterator.first));
+ }
+};
+
+} // namespace skeleton_blocker
+
+namespace skbl = skeleton_blocker;
+
+} // namespace Gudhi
+
+#endif // SKELETON_BLOCKER_ITERATORS_SKELETON_BLOCKERS_EDGES_ITERATORS_H_
diff --git a/include/gudhi/Skeleton_blocker/iterators/Skeleton_blockers_iterators.h b/include/gudhi/Skeleton_blocker/iterators/Skeleton_blockers_iterators.h
new file mode 100644
index 00000000..cc3ed276
--- /dev/null
+++ b/include/gudhi/Skeleton_blocker/iterators/Skeleton_blockers_iterators.h
@@ -0,0 +1,32 @@
+ /* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SKELETON_BLOCKER_ITERATORS_SKELETON_BLOCKERS_ITERATORS_H_
+#define SKELETON_BLOCKER_ITERATORS_SKELETON_BLOCKERS_ITERATORS_H_
+
+#include <gudhi/Skeleton_blocker/iterators/Skeleton_blockers_vertices_iterators.h>
+#include <gudhi/Skeleton_blocker/iterators/Skeleton_blockers_edges_iterators.h>
+#include <gudhi/Skeleton_blocker/iterators/Skeleton_blockers_blockers_iterators.h>
+#include <gudhi/Skeleton_blocker/iterators/Skeleton_blockers_triangles_iterators.h>
+#include <gudhi/Skeleton_blocker/iterators/Skeleton_blockers_simplices_iterators.h>
+
+#endif // SKELETON_BLOCKER_ITERATORS_SKELETON_BLOCKERS_ITERATORS_H_
diff --git a/include/gudhi/Skeleton_blocker/iterators/Skeleton_blockers_simplices_iterators.h b/include/gudhi/Skeleton_blocker/iterators/Skeleton_blockers_simplices_iterators.h
new file mode 100644
index 00000000..3b941be5
--- /dev/null
+++ b/include/gudhi/Skeleton_blocker/iterators/Skeleton_blockers_simplices_iterators.h
@@ -0,0 +1,400 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef SKELETON_BLOCKER_ITERATORS_SKELETON_BLOCKERS_SIMPLICES_ITERATORS_H_
+#define SKELETON_BLOCKER_ITERATORS_SKELETON_BLOCKERS_SIMPLICES_ITERATORS_H_
+
+#include <gudhi/Skeleton_blocker_link_complex.h>
+#include <gudhi/Skeleton_blocker/Skeleton_blocker_link_superior.h>
+#include <gudhi/Skeleton_blocker/internal/Trie.h>
+#include <gudhi/Debug_utils.h>
+
+#include <boost/iterator/iterator_facade.hpp>
+
+#include <memory>
+#include <list>
+#include <iostream>
+
+namespace Gudhi {
+
+namespace skeleton_blocker {
+
+/**
+ * Link may be Skeleton_blocker_link_complex<SkeletonBlockerComplex> to iterate over all
+ * simplices around a vertex OR
+ * Skeleton_blocker_superior_link_complex<SkeletonBlockerComplex> to iterate over all
+ * superior vertices around a vertex.
+ * The iteration is done by computing a trie with the link and doing a breadth-first traversal
+ * of the trie.
+ */
+template<typename SkeletonBlockerComplex, typename Link>
+class Simplex_around_vertex_iterator :
+public boost::iterator_facade < Simplex_around_vertex_iterator<SkeletonBlockerComplex, Link>
+, typename SkeletonBlockerComplex::Simplex
+, boost::forward_traversal_tag
+, typename SkeletonBlockerComplex::Simplex
+> {
+ friend class boost::iterator_core_access;
+ typedef SkeletonBlockerComplex Complex;
+ typedef typename Complex::Vertex_handle Vertex_handle;
+ typedef typename Complex::Edge_handle Edge_handle;
+ typedef typename Complex::Simplex Simplex;
+
+ // Link_vertex_handle == Complex_Vertex_handle but this renaming helps avoiding confusion
+ typedef typename Link::Vertex_handle Link_vertex_handle;
+
+ typedef typename Gudhi::skeleton_blocker::Trie<Simplex> Trie;
+
+ private:
+ const Complex* complex;
+ Vertex_handle v;
+ std::shared_ptr<Link> link_v;
+ std::shared_ptr<Trie> trie;
+ std::list<Trie*> nodes_to_be_seen; // todo deque
+
+ public:
+ Simplex_around_vertex_iterator() : complex(0) {}
+
+ Simplex_around_vertex_iterator(const Complex* complex_, Vertex_handle v_) :
+ complex(complex_),
+ v(v_),
+ link_v(new Link(*complex_, v_)),
+ trie(new Trie(v_)) {
+ compute_trie_and_nodes_to_be_seen();
+ }
+
+ // todo avoid useless copy
+ // todo currently just work if copy begin iterator
+ Simplex_around_vertex_iterator(const Simplex_around_vertex_iterator& other) :
+ complex(other.complex),
+ v(other.v),
+ link_v(other.link_v),
+ trie(other.trie),
+ nodes_to_be_seen(other.nodes_to_be_seen) {
+ if (!other.is_end()) {}
+ }
+
+ /**
+ * returns an iterator to the end
+ */
+ Simplex_around_vertex_iterator(const Complex* complex_, Vertex_handle v_, bool end) :
+ complex(complex_),
+ v(v_) {
+ set_end();
+ }
+
+ private:
+ void compute_trie_and_nodes_to_be_seen() {
+ // once we go through every simplices passing through v0
+ // we remove v0. That way, it prevents from passing a lot of times
+ // though edges leaving v0.
+ // another solution would have been to provides an adjacency iterator
+ // to superior vertices that avoids lower ones.
+ while (!link_v->empty()) {
+ auto v0 = *(link_v->vertex_range().begin());
+ trie->add_child(build_trie(v0, trie.get()));
+ link_v->remove_vertex(v0);
+ }
+ nodes_to_be_seen.push_back(trie.get());
+ }
+
+ Trie* build_trie(Link_vertex_handle link_vh, Trie* parent) {
+ Trie* res = new Trie(parent_vertex(link_vh), parent);
+ for (Link_vertex_handle nv : link_v->vertex_range(link_vh)) {
+ if (link_vh < nv) {
+ Simplex simplex_node_plus_nv(res->simplex());
+ simplex_node_plus_nv.add_vertex(parent_vertex(nv));
+ if (complex->contains(simplex_node_plus_nv)) {
+ res->add_child(build_trie(nv, res));
+ }
+ }
+ }
+ return res;
+ }
+
+ bool is_node_in_complex(Trie* trie) {
+ return true;
+ }
+
+ Vertex_handle parent_vertex(Link_vertex_handle link_vh) const {
+ return complex->convert_handle_from_another_complex(*link_v, link_vh);
+ }
+
+ public:
+ friend std::ostream& operator<<(std::ostream& stream, const Simplex_around_vertex_iterator& savi) {
+ stream << savi.trie << std::endl;
+ stream << "(" << savi.nodes_to_be_seen.size() << ") nodes to see\n";
+ return stream;
+ }
+
+ bool equal(const Simplex_around_vertex_iterator& other) const {
+ bool same_complex = (complex == other.complex);
+ if (!same_complex)
+ return false;
+
+ if (!(v == other.v))
+ return false;
+
+ bool both_empty = nodes_to_be_seen.empty() && other.nodes_to_be_seen.empty();
+ if (both_empty)
+ return true;
+
+ bool both_non_empty = !nodes_to_be_seen.empty() && !other.nodes_to_be_seen.empty();
+
+ if (!both_non_empty) return false; // one is empty the other is not
+
+ bool same_node = (**(nodes_to_be_seen.begin()) == **(other.nodes_to_be_seen.begin()));
+ return same_node;
+ }
+
+ void increment() {
+ assert(!is_end());
+ Trie* first_node = nodes_to_be_seen.front();
+
+ nodes_to_be_seen.pop_front();
+
+ for (auto childs : first_node->childs) {
+ nodes_to_be_seen.push_back(childs.get());
+ }
+ }
+
+ Simplex dereference() const {
+ assert(!nodes_to_be_seen.empty());
+ Trie* first_node = nodes_to_be_seen.front();
+ return first_node->simplex();
+ }
+
+ Simplex get_trie_address() const {
+ assert(!nodes_to_be_seen.empty());
+ return nodes_to_be_seen.front();
+ }
+
+ private:
+ void set_end() {
+ nodes_to_be_seen.clear();
+ }
+
+ bool is_end() const {
+ return nodes_to_be_seen.empty();
+ }
+};
+
+template<typename SkeletonBlockerComplex>
+class Simplex_iterator :
+public boost::iterator_facade < Simplex_iterator<SkeletonBlockerComplex>
+, typename SkeletonBlockerComplex::Simplex
+, boost::forward_traversal_tag
+, typename SkeletonBlockerComplex::Simplex
+> {
+ typedef Skeleton_blocker_link_superior<SkeletonBlockerComplex> Link;
+
+ friend class boost::iterator_core_access;
+
+ template<class SkBlDS> friend class Skeleton_blocker_complex;
+
+ typedef SkeletonBlockerComplex Complex;
+ typedef typename Complex::Vertex_handle Vertex_handle;
+ typedef typename Complex::Edge_handle Edge_handle;
+ typedef typename Complex::Simplex Simplex;
+ typedef typename Complex::Complex_vertex_iterator Complex_vertex_iterator;
+ typedef typename Link::Vertex_handle Link_vertex_handle;
+
+ private:
+ const Complex* complex_;
+ Complex_vertex_iterator current_vertex_;
+
+ typedef Simplex_around_vertex_iterator<SkeletonBlockerComplex, Link> SAVI;
+ SAVI current_simplex_around_current_vertex_;
+ SAVI simplices_around_current_vertex_end_;
+
+ public:
+ Simplex_iterator() : complex_(0) { }
+
+ Simplex_iterator(const Complex* complex) :
+ complex_(complex),
+ current_vertex_(complex->vertex_range().begin()),
+ current_simplex_around_current_vertex_(complex, *current_vertex_),
+ simplices_around_current_vertex_end_(complex, *current_vertex_, true) {
+ // should not be called if the complex is empty
+ assert(!complex->empty());
+ }
+
+ private:
+ // todo return to private
+ Simplex_iterator(const Complex* complex, bool end) :
+ complex_(complex) {
+ set_end();
+ }
+
+ public:
+ Simplex_iterator(const Simplex_iterator& other)
+ :
+ complex_(other.complex_),
+ current_vertex_(other.current_vertex_),
+ current_simplex_around_current_vertex_(other.current_simplex_around_current_vertex_),
+ simplices_around_current_vertex_end_(other.simplices_around_current_vertex_end_) { }
+
+ friend Simplex_iterator make_begin_iterator(const Complex* complex) {
+ if (complex->empty())
+ return make_end_simplex_iterator(complex);
+ else
+ return Simplex_iterator(complex);
+ }
+
+ friend Simplex_iterator make_end_simplex_iterator(const Complex* complex) {
+ return Simplex_iterator(complex, true);
+ }
+
+ bool equal(const Simplex_iterator& other) const {
+ if (complex_ != other.complex_) return false;
+ if (current_vertex_ != other.current_vertex_) return false;
+ if (is_end() && other.is_end()) return true;
+ if (current_simplex_around_current_vertex_ != other.current_simplex_around_current_vertex_)
+ return false;
+ return true;
+ }
+
+ void increment() {
+ if (current_simplex_around_current_vertex_ != simplices_around_current_vertex_end_) {
+ current_simplex_around_current_vertex_.increment();
+ if (current_simplex_around_current_vertex_ == simplices_around_current_vertex_end_)
+ goto_next_vertex();
+ } else {
+ goto_next_vertex();
+ }
+ }
+
+ void goto_next_vertex() {
+ current_vertex_.increment();
+ if (!is_end()) {
+ current_simplex_around_current_vertex_ = SAVI(complex_, *current_vertex_);
+ simplices_around_current_vertex_end_ = SAVI(complex_, *current_vertex_, true);
+ }
+ }
+
+ Simplex dereference() const {
+ return current_simplex_around_current_vertex_.dereference();
+ }
+
+ private:
+ void set_end() {
+ current_vertex_ = complex_->vertex_range().end();
+ }
+
+ bool is_end() const {
+ return (current_vertex_ == complex_->vertex_range().end());
+ }
+};
+
+/**
+ * Iterator through the maximal faces of the coboundary of a simplex.
+ */
+template<typename SkeletonBlockerComplex, typename Link>
+class Simplex_coboundary_iterator :
+public boost::iterator_facade < Simplex_coboundary_iterator<SkeletonBlockerComplex, Link>
+, typename SkeletonBlockerComplex::Simplex, boost::forward_traversal_tag, typename SkeletonBlockerComplex::Simplex> {
+ friend class boost::iterator_core_access;
+ typedef SkeletonBlockerComplex Complex;
+ typedef typename Complex::Vertex_handle Vertex_handle;
+ typedef typename Complex::Edge_handle Edge_handle;
+ typedef typename Complex::Simplex Simplex;
+ typedef typename Complex::Complex_vertex_iterator Complex_vertex_iterator;
+
+ // Link_vertex_handle == Complex_Vertex_handle but this renaming helps avoiding confusion
+ typedef typename Link::Vertex_handle Link_vertex_handle;
+
+ private:
+ const Complex* complex;
+ const Simplex& sigma;
+ std::shared_ptr<Link> link;
+ Complex_vertex_iterator current_vertex;
+ Complex_vertex_iterator link_vertex_end;
+
+ public:
+ Simplex_coboundary_iterator() : complex(0) {}
+
+ Simplex_coboundary_iterator(const Complex* complex_, const Simplex& sigma_) :
+ complex(complex_),
+ sigma(sigma_),
+ //need only vertices of the link hence the true flag
+ link(new Link(*complex_, sigma_, false, true)) {
+ auto link_vertex_range = link->vertex_range();
+ current_vertex = link_vertex_range.begin();
+ link_vertex_end = link_vertex_range.end();
+ }
+
+ Simplex_coboundary_iterator(const Simplex_coboundary_iterator& other) :
+ complex(other.complex),
+ sigma(other.sigma),
+ link(other.link),
+ current_vertex(other.current_vertex),
+ link_vertex_end(other.link_vertex_end) { }
+
+ // returns an iterator to the end
+ Simplex_coboundary_iterator(const Complex* complex_,const Simplex& sigma_, bool end) :
+ complex(complex_),
+ sigma(sigma_) {
+ // to represent an end iterator without having to build a useless link, we use
+ // the convection that link is not initialized.
+ }
+
+ private:
+ Vertex_handle parent_vertex(Link_vertex_handle link_vh) const {
+ return complex->convert_handle_from_another_complex(*link, link_vh);
+ }
+
+public:
+ friend std::ostream& operator<<(std::ostream& stream, const Simplex_coboundary_iterator& sci) {
+ return stream;
+ }
+
+ // assume that iterator points to the same complex and comes from the same simplex
+ bool equal(const Simplex_coboundary_iterator& other) const {
+ assert(complex == other.complex && sigma == other.sigma);
+ if(is_end()) return other.is_end();
+ if(other.is_end()) return is_end();
+ return *current_vertex == *(other.current_vertex);
+ }
+
+ void increment() {
+ ++current_vertex;
+ }
+
+ Simplex dereference() const {
+ Simplex res(sigma);
+ res.add_vertex(parent_vertex(*current_vertex));
+ return res;
+ }
+
+private:
+ bool is_end() const {
+ return !link || current_vertex == link_vertex_end;
+ }
+};
+
+
+} // namespace skeleton_blocker
+
+namespace skbl = skeleton_blocker;
+
+} // namespace Gudhi
+
+#endif // SKELETON_BLOCKER_ITERATORS_SKELETON_BLOCKERS_SIMPLICES_ITERATORS_H_
diff --git a/include/gudhi/Skeleton_blocker/iterators/Skeleton_blockers_triangles_iterators.h b/include/gudhi/Skeleton_blocker/iterators/Skeleton_blockers_triangles_iterators.h
new file mode 100644
index 00000000..b2dd9a21
--- /dev/null
+++ b/include/gudhi/Skeleton_blocker/iterators/Skeleton_blockers_triangles_iterators.h
@@ -0,0 +1,222 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef SKELETON_BLOCKER_ITERATORS_SKELETON_BLOCKERS_TRIANGLES_ITERATORS_H_
+#define SKELETON_BLOCKER_ITERATORS_SKELETON_BLOCKERS_TRIANGLES_ITERATORS_H_
+
+#include <boost/iterator/iterator_facade.hpp>
+#include <memory>
+
+namespace Gudhi {
+
+namespace skeleton_blocker {
+
+/**
+ * \brief Iterator over the triangles that are
+ * adjacent to a vertex of the simplicial complex.
+ * \remark Will be removed soon -> dont look
+ */
+template<typename Complex, typename LinkType>
+class Triangle_around_vertex_iterator : public boost::iterator_facade
+< Triangle_around_vertex_iterator <Complex, LinkType>
+, typename Complex::Simplex const
+, boost::forward_traversal_tag
+, typename Complex::Simplex const> {
+ friend class boost::iterator_core_access;
+ template<typename T> friend class Triangle_iterator;
+ private:
+ typedef typename LinkType::Vertex_handle Vertex_handle;
+ typedef typename LinkType::Root_vertex_handle Root_vertex_handle;
+ typedef typename LinkType::Simplex Simplex;
+ typedef typename Complex::Complex_edge_iterator Complex_edge_iterator_;
+
+ const Complex* complex_;
+ Vertex_handle v_;
+ std::shared_ptr<LinkType> link_;
+ Complex_edge_iterator_ current_edge_;
+ bool is_end_;
+
+ public:
+ Triangle_around_vertex_iterator(const Complex* complex, Vertex_handle v) :
+ complex_(complex), v_(v), link_(new LinkType(*complex, v_)),
+ current_edge_(link_->edge_range().begin()),
+ is_end_(current_edge_ == link_->edge_range().end()) { }
+
+ /**
+ * @brief ugly hack to get an iterator to the end
+ */
+ Triangle_around_vertex_iterator(const Complex* complex, Vertex_handle v, bool is_end) :
+ complex_(complex), v_(v), link_(0), is_end_(true) { }
+
+ /**
+ * @brief ugly hack to get an iterator to the end
+ */
+ Triangle_around_vertex_iterator() :
+ complex_(0), v_(-1), link_(0), is_end_(true) { }
+
+ Triangle_around_vertex_iterator(const Triangle_around_vertex_iterator& other) {
+ v_ = other.v_;
+ complex_ = other.complex_;
+ is_end_ = other.is_end_;
+
+ if (!is_end_) {
+ link_ = other.link_;
+ current_edge_ = other.current_edge_;
+ }
+ }
+
+ bool equal(const Triangle_around_vertex_iterator& other) const {
+ return (complex_ == other.complex_) && ((finished() && other.finished()) || current_edge_ == other.current_edge_);
+ }
+
+ Simplex dereference() const {
+ Root_vertex_handle v1 = (*link_)[*current_edge_].first();
+ Root_vertex_handle v2 = (*link_)[*current_edge_].second();
+ return Simplex(v_, *(complex_->get_address(v1)), *(complex_->get_address(v2)));
+ }
+
+ void increment() {
+ ++current_edge_;
+ }
+
+ private:
+ bool finished() const {
+ return is_end_ || (current_edge_ == link_->edge_range().end());
+ }
+};
+
+/**
+ * \brief Iterator over the triangles of the
+ * simplicial complex.
+ * \remark Will be removed soon -> dont look
+ *
+ */
+template<typename SkeletonBlockerComplex>
+class Triangle_iterator : public boost::iterator_facade<
+Triangle_iterator <SkeletonBlockerComplex>,
+typename SkeletonBlockerComplex::Simplex const
+, boost::forward_traversal_tag
+, typename SkeletonBlockerComplex::Simplex const> {
+ friend class boost::iterator_core_access;
+ private:
+ typedef typename SkeletonBlockerComplex::Vertex_handle Vertex_handle;
+ typedef typename SkeletonBlockerComplex::Root_vertex_handle Root_vertex_handle;
+ typedef typename SkeletonBlockerComplex::Simplex Simplex;
+ typedef typename SkeletonBlockerComplex::Superior_triangle_around_vertex_iterator STAVI;
+ typedef typename SkeletonBlockerComplex::Complex_vertex_iterator Complex_vertex_iterator;
+
+ const SkeletonBlockerComplex* complex_;
+ Complex_vertex_iterator current_vertex_;
+ STAVI current_triangle_;
+ bool is_end_;
+
+ public:
+ /*
+ * @remark assume that the complex is non-empty
+ */
+ Triangle_iterator(const SkeletonBlockerComplex* complex) :
+ complex_(complex),
+ current_vertex_(complex->vertex_range().begin()),
+ current_triangle_(complex, *current_vertex_), // xxx this line is problematic is the complex is empty
+ is_end_(false) {
+ assert(!complex->empty());
+ gotoFirstTriangle();
+ }
+
+ private:
+ // goto to the first triangle or to the end if none
+ void gotoFirstTriangle() {
+ if (!is_finished() && current_triangle_.finished()) {
+ goto_next_vertex();
+ }
+ }
+
+ public:
+ /**
+ * @brief ugly hack to get an iterator to the end
+ * @remark assume that the complex is non-empty
+ */
+ Triangle_iterator(const SkeletonBlockerComplex* complex, bool is_end) :
+ complex_(complex),
+ current_vertex_(complex->vertex_range().end()),
+ current_triangle_(), // xxx this line is problematic is the complex is empty
+ is_end_(true) { }
+
+ Triangle_iterator& operator=(const Triangle_iterator & other) {
+ complex_ = other.complex_;
+ Complex_vertex_iterator current_vertex_;
+ STAVI current_triangle_;
+ return *this;
+ }
+
+ bool equal(const Triangle_iterator& other) const {
+ bool both_are_finished = is_finished() && other.is_finished();
+ bool both_arent_finished = !is_finished() && !other.is_finished();
+ // if the two iterators are not finished, they must have the same state
+ return (complex_ == other.complex_) && (both_are_finished || ((both_arent_finished) &&
+ current_vertex_ == other.current_vertex_ && current_triangle_ == other.current_triangle_));
+ }
+
+ Simplex dereference() const {
+ return *current_triangle_;
+ }
+
+ private:
+ // goto the next vertex that has a triangle pending or the
+ // end vertex iterator if none exists
+ void goto_next_vertex() {
+ assert(current_triangle_.finished()); // we mush have consume all triangles passing through the vertex
+ assert(!is_finished()); // we must not be done
+
+ ++current_vertex_;
+
+ if (!is_finished()) {
+ current_triangle_ = STAVI(complex_, *current_vertex_);
+ if (current_triangle_.finished())
+ goto_next_vertex();
+ }
+ }
+
+ public:
+ void increment() {
+ if (!current_triangle_.finished()) {
+ ++current_triangle_; // problem here
+ if (current_triangle_.finished())
+ goto_next_vertex();
+ } else {
+ assert(!is_finished());
+ goto_next_vertex();
+ }
+ }
+
+ private:
+ bool is_finished() const {
+ return is_end_ || current_vertex_ == complex_->vertex_range().end();
+ }
+};
+
+} // namespace skeleton_blocker
+
+namespace skbl = skeleton_blocker;
+
+} // namespace Gudhi
+
+#endif // SKELETON_BLOCKER_ITERATORS_SKELETON_BLOCKERS_TRIANGLES_ITERATORS_H_
diff --git a/include/gudhi/Skeleton_blocker/iterators/Skeleton_blockers_vertices_iterators.h b/include/gudhi/Skeleton_blocker/iterators/Skeleton_blockers_vertices_iterators.h
new file mode 100644
index 00000000..f06cab71
--- /dev/null
+++ b/include/gudhi/Skeleton_blocker/iterators/Skeleton_blockers_vertices_iterators.h
@@ -0,0 +1,174 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef SKELETON_BLOCKER_ITERATORS_SKELETON_BLOCKERS_VERTICES_ITERATORS_H_
+#define SKELETON_BLOCKER_ITERATORS_SKELETON_BLOCKERS_VERTICES_ITERATORS_H_
+
+#include <boost/iterator/iterator_facade.hpp>
+
+#include <utility> // for pair<>
+
+namespace Gudhi {
+
+namespace skeleton_blocker {
+
+/**
+ *@brief Iterator on the vertices of a simplicial complex
+ *@remark The increment operator go to the next active vertex.
+ *@remark Incrementation increases Vertex_handle.
+ */
+template<typename SkeletonBlockerComplex>
+class Vertex_iterator : public boost::iterator_facade< Vertex_iterator <SkeletonBlockerComplex>
+, typename SkeletonBlockerComplex::Vertex_handle const
+, boost::forward_traversal_tag
+, typename SkeletonBlockerComplex::Vertex_handle const> {
+ friend class boost::iterator_core_access;
+
+ typedef typename SkeletonBlockerComplex::boost_vertex_iterator boost_vertex_iterator;
+ typedef typename SkeletonBlockerComplex::Vertex_handle Vertex_handle;
+ private:
+ const SkeletonBlockerComplex* complex;
+ std::pair<boost_vertex_iterator, boost_vertex_iterator> vertexIterator;
+
+
+ public:
+ Vertex_iterator() : complex(NULL) { }
+
+ Vertex_iterator(const SkeletonBlockerComplex* complex_) :
+ complex(complex_),
+ vertexIterator(vertices(complex_->skeleton)) {
+ if (!finished() && !is_active()) {
+ goto_next_valid();
+ }
+ }
+
+ /**
+ * return an iterator to the end.
+ */
+ Vertex_iterator(const SkeletonBlockerComplex* complex_, int end) :
+ complex(complex_), vertexIterator(vertices(complex_->skeleton)) {
+ vertexIterator.first = vertexIterator.second;
+ }
+
+ public:
+ void increment() {
+ goto_next_valid();
+ }
+
+ Vertex_handle dereference() const {
+ return (Vertex_handle(*(vertexIterator.first)));
+ }
+
+ bool equal(const Vertex_iterator& other) const {
+ return vertexIterator == other.vertexIterator && complex == other.complex;
+ }
+
+ bool operator<(const Vertex_iterator& other) const {
+ return dereference() < other.dereference();
+ }
+
+ private:
+ bool finished() const {
+ return vertexIterator.first == vertexIterator.second;
+ }
+
+ void goto_next_valid() {
+ ++vertexIterator.first;
+ if (!finished() && !is_active()) {
+ goto_next_valid();
+ }
+ }
+
+ bool is_active() const {
+ return ((*complex)[Vertex_handle(*vertexIterator.first)]).is_active();
+ }
+};
+
+template<typename SkeletonBlockerComplex>
+class Neighbors_vertices_iterator: public boost::iterator_facade < Neighbors_vertices_iterator<SkeletonBlockerComplex>
+, typename SkeletonBlockerComplex::Vertex_handle const
+, boost::forward_traversal_tag
+, typename SkeletonBlockerComplex::Vertex_handle const> {
+ friend class boost::iterator_core_access;
+
+ typedef SkeletonBlockerComplex Complex;
+ typedef typename Complex::boost_adjacency_iterator boost_adjacency_iterator;
+ typedef typename Complex::Vertex_handle Vertex_handle;
+ typedef typename Complex::Edge_handle Edge_handle;
+
+ private:
+ const Complex* complex;
+ Vertex_handle v;
+
+ boost_adjacency_iterator current_;
+ boost_adjacency_iterator end_;
+
+ public:
+ // boost_adjacency_iterator ai, ai_end;
+ // for (tie(ai, ai_end) = adjacent_vertices(v.vertex, skeleton); ai != ai_end; ++ai) {
+
+ Neighbors_vertices_iterator() : complex(NULL) { }
+
+ Neighbors_vertices_iterator(const Complex* complex_, Vertex_handle v_) :
+ complex(complex_),
+ v(v_) {
+ tie(current_, end_) = adjacent_vertices(v.vertex, complex->skeleton);
+ }
+
+ /**
+ * returns an iterator to the end
+ */
+ Neighbors_vertices_iterator(const Complex* complex_, Vertex_handle v_, int end) :
+ complex(complex_),
+ v(v_) {
+ tie(current_, end_) = adjacent_vertices(v.vertex, complex->skeleton);
+ set_end();
+ }
+
+ void increment() {
+ if (current_ != end_)
+ ++current_;
+ }
+
+ Vertex_handle dereference() const {
+ return (Vertex_handle(*current_));
+ }
+
+ bool equal(const Neighbors_vertices_iterator& other) const {
+ return (complex == other.complex) && (v == other.v) && (current_ == other.current_) && (end_ == other.end_);
+ }
+
+ private:
+ // todo remove this ugly hack
+ void set_end() {
+ current_ = end_;
+ }
+};
+
+} // namespace skeleton_blocker
+
+namespace skbl = skeleton_blocker;
+
+} // namespace Gudhi
+
+#endif // SKELETON_BLOCKER_ITERATORS_SKELETON_BLOCKERS_VERTICES_ITERATORS_H_
+
+
diff --git a/include/gudhi/Skeleton_blocker_complex.h b/include/gudhi/Skeleton_blocker_complex.h
new file mode 100644
index 00000000..7a6d1d50
--- /dev/null
+++ b/include/gudhi/Skeleton_blocker_complex.h
@@ -0,0 +1,1606 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SKELETON_BLOCKER_COMPLEX_H_
+#define SKELETON_BLOCKER_COMPLEX_H_
+
+#include <gudhi/Skeleton_blocker/iterators/Skeleton_blockers_iterators.h>
+#include <gudhi/Skeleton_blocker_link_complex.h>
+#include <gudhi/Skeleton_blocker/Skeleton_blocker_link_superior.h>
+#include <gudhi/Skeleton_blocker/Skeleton_blocker_sub_complex.h>
+#include <gudhi/Skeleton_blocker/Skeleton_blocker_simplex.h>
+#include <gudhi/Skeleton_blocker/Skeleton_blocker_complex_visitor.h>
+#include <gudhi/Skeleton_blocker/internal/Top_faces.h>
+#include <gudhi/Skeleton_blocker/internal/Trie.h>
+#include <gudhi/Debug_utils.h>
+
+#include <boost/graph/adjacency_list.hpp>
+#include <boost/graph/connected_components.hpp>
+#include <boost/iterator/transform_iterator.hpp>
+#include <boost/range/adaptor/map.hpp>
+
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <memory>
+#include <map>
+#include <list>
+#include <set>
+#include <vector>
+#include <string>
+#include <algorithm>
+#include <utility>
+
+namespace Gudhi {
+
+namespace skeleton_blocker {
+
+/**
+ *@class Skeleton_blocker_complex
+ *@brief Abstract Simplicial Complex represented with a skeleton/blockers pair.
+ *@ingroup skbl
+ */
+template<class SkeletonBlockerDS>
+class Skeleton_blocker_complex {
+ template<class ComplexType> friend class Vertex_iterator;
+ template<class ComplexType> friend class Neighbors_vertices_iterator;
+ template<class ComplexType> friend class Edge_iterator;
+ template<class ComplexType> friend class Edge_around_vertex_iterator;
+
+ template<class ComplexType> friend class Skeleton_blocker_link_complex;
+ template<class ComplexType> friend class Skeleton_blocker_link_superior;
+ template<class ComplexType> friend class Skeleton_blocker_sub_complex;
+
+ public:
+ /**
+ * @brief The type of stored vertex node, specified by the template SkeletonBlockerDS
+ */
+ typedef typename SkeletonBlockerDS::Graph_vertex Graph_vertex;
+
+ /**
+ * @brief The type of stored edge node, specified by the template SkeletonBlockerDS
+ */
+ typedef typename SkeletonBlockerDS::Graph_edge Graph_edge;
+
+ typedef typename SkeletonBlockerDS::Root_vertex_handle Root_vertex_handle;
+
+ /**
+ * @brief The type of an handle to a vertex of the complex.
+ */
+ typedef typename SkeletonBlockerDS::Vertex_handle Vertex_handle;
+ typedef typename Root_vertex_handle::boost_vertex_handle boost_vertex_handle;
+
+ /**
+ * @brief A ordered set of integers that represents a simplex.
+ */
+ typedef Skeleton_blocker_simplex<Vertex_handle> Simplex;
+ typedef Skeleton_blocker_simplex<Root_vertex_handle> Root_simplex_handle;
+
+ /**
+ * @brief Handle to a blocker of the complex.
+ */
+ typedef Simplex* Blocker_handle;
+
+ typedef typename Root_simplex_handle::Simplex_vertex_const_iterator Root_simplex_iterator;
+ typedef typename Simplex::Simplex_vertex_const_iterator Simplex_handle_iterator;
+
+ protected:
+ typedef typename boost::adjacency_list<boost::setS, // edges
+ boost::vecS, // vertices
+ boost::undirectedS, Graph_vertex, Graph_edge> Graph;
+ // todo/remark : edges are not sorted, it heavily penalizes computation for SuperiorLink
+ // (eg Link with greater vertices)
+ // that burdens simplex iteration / complex initialization via list of simplices.
+ // to avoid that, one should modify the graph by storing two lists of adjacency for every
+ // vertex, the one with superior and the one with lower vertices, that way there is
+ // no more extra cost for computation of SuperiorLink
+ typedef typename boost::graph_traits<Graph>::vertex_iterator boost_vertex_iterator;
+ typedef typename boost::graph_traits<Graph>::edge_iterator boost_edge_iterator;
+
+ protected:
+ typedef typename boost::graph_traits<Graph>::adjacency_iterator boost_adjacency_iterator;
+
+ public:
+ /**
+ * @brief Handle to an edge of the complex.
+ */
+ typedef typename boost::graph_traits<Graph>::edge_descriptor Edge_handle;
+
+ protected:
+ typedef std::multimap<Vertex_handle, Simplex *> BlockerMap;
+ typedef typename std::multimap<Vertex_handle, Simplex *>::value_type BlockerPair;
+ typedef typename std::multimap<Vertex_handle, Simplex *>::iterator BlockerMapIterator;
+ typedef typename std::multimap<Vertex_handle, Simplex *>::const_iterator BlockerMapConstIterator;
+
+ protected:
+ size_t num_vertices_;
+ size_t num_blockers_;
+
+ typedef Skeleton_blocker_complex_visitor<Vertex_handle> Visitor;
+ // typedef Visitor* Visitor_ptr;
+ Visitor* visitor;
+
+ /**
+ * @details If 'x' is a Vertex_handle of a vertex in the complex then degree[x] = d is its degree.
+ *
+ * This quantity is updated when adding/removing edge.
+ *
+ * This is useful because the operation
+ * list.size() is done in linear time.
+ */
+ std::vector<boost_vertex_handle> degree_;
+ Graph skeleton; /** 1-skeleton of the simplicial complex. */
+
+ /** Each vertex can access to the blockers passing through it. */
+ BlockerMap blocker_map_;
+
+ public:
+ /////////////////////////////////////////////////////////////////////////////
+ /** @name Constructors, Destructors
+ */
+ //@{
+
+ /**
+ *@brief constructs a simplicial complex with a given number of vertices and a visitor.
+ */
+ explicit Skeleton_blocker_complex(size_t num_vertices_ = 0, Visitor* visitor_ = NULL)
+ : visitor(visitor_) {
+ clear();
+ for (size_t i = 0; i < num_vertices_; ++i) {
+ add_vertex();
+ }
+ }
+
+ private:
+ // typedef Trie<Skeleton_blocker_complex<SkeletonBlockerDS>> STrie;
+ typedef Trie<Simplex> STrie;
+
+ public:
+ /**
+ * @brief Constructor with a list of simplices.
+ * @details is_flag_complex indicates if the complex is a flag complex or not (to know if blockers have to be computed or not).
+ */
+ template<typename SimpleHandleOutputIterator>
+ Skeleton_blocker_complex(SimpleHandleOutputIterator simplices_begin, SimpleHandleOutputIterator simplices_end,
+ bool is_flag_complex = false, Visitor* visitor_ = NULL)
+ : num_vertices_(0),
+ num_blockers_(0),
+ visitor(visitor_) {
+ add_vertices_and_edges(simplices_begin, simplices_end);
+
+ if (!is_flag_complex)
+ // need to compute blockers
+ add_blockers(simplices_begin, simplices_end);
+ }
+
+ private:
+ /**
+ * Add vertices and edges of a simplex in one pass
+ */
+ template<typename SimpleHandleOutputIterator>
+ void add_vertices_and_edges(SimpleHandleOutputIterator simplices_begin, SimpleHandleOutputIterator simplices_end) {
+ std::vector<std::pair<Vertex_handle, Vertex_handle>> edges;
+ // first pass to add vertices and edges
+ int num_vertex = -1;
+ for (auto s_it = simplices_begin; s_it != simplices_end; ++s_it) {
+ if (s_it->dimension() == 0) num_vertex = (std::max)(num_vertex, s_it->first_vertex().vertex);
+ if (s_it->dimension() == 1) edges.emplace_back(s_it->first_vertex(), s_it->last_vertex());
+ }
+ while (num_vertex-- >= 0) add_vertex();
+
+ for (const auto& e : edges)
+ add_edge_without_blockers(e.first, e.second);
+ }
+
+
+ template<typename SimpleHandleOutputIterator>
+ void add_blockers(SimpleHandleOutputIterator simplices_begin, SimpleHandleOutputIterator simplices_end) {
+ Tries<Simplex> tries(num_vertices(), simplices_begin, simplices_end);
+ tries.init_next_dimension();
+ auto simplices(tries.next_dimension_simplices());
+
+ while (!simplices.empty()) {
+ simplices = tries.next_dimension_simplices();
+ for (auto& sigma : simplices) {
+ // common_positive_neighbors is the set of vertices u such that
+ // for all s in sigma, us is an edge and u>s
+ Simplex common_positive_neighbors(tries.positive_neighbors(sigma.last_vertex()));
+ for (auto sigma_it = sigma.rbegin(); sigma_it != sigma.rend(); ++sigma_it)
+ if (sigma_it != sigma.rbegin())
+ common_positive_neighbors.intersection(tries.positive_neighbors(*sigma_it));
+
+ for (auto x : common_positive_neighbors) {
+ // first test that all edges sx are here for all s in sigma
+ bool all_edges_here = true;
+ for (auto s : sigma)
+ if (!contains_edge(x, s)) {
+ all_edges_here = false;
+ break;
+ }
+ if (!all_edges_here) continue;
+
+ // all edges of sigma \cup x are here
+ // we have a blocker if all proper faces of sigma \cup x
+ // are in the complex and if sigma \cup x is not in the complex
+ // the first is equivalent at checking if blocks(sigma \cup x) is true
+ // as all blockers of lower dimension have already been computed
+ sigma.add_vertex(x);
+ if (!tries.contains(sigma) && !blocks(sigma))
+ add_blocker(sigma);
+ sigma.remove_vertex(x);
+ }
+ }
+ }
+ }
+
+ public:
+ // We cannot use the default copy constructor since we need
+ // to make a copy of each of the blockers
+
+ Skeleton_blocker_complex(const Skeleton_blocker_complex& copy) {
+ visitor = NULL;
+ degree_ = copy.degree_;
+ skeleton = Graph(copy.skeleton);
+ num_vertices_ = copy.num_vertices_;
+
+ num_blockers_ = 0;
+ // we copy the blockers
+ for (auto blocker : copy.const_blocker_range()) {
+ add_blocker(*blocker);
+ }
+ }
+
+ /**
+ */
+ Skeleton_blocker_complex& operator=(const Skeleton_blocker_complex& copy) {
+ clear();
+ visitor = NULL;
+ degree_ = copy.degree_;
+ skeleton = Graph(copy.skeleton);
+ num_vertices_ = copy.num_vertices_;
+
+ num_blockers_ = 0;
+ // we copy the blockers
+ for (auto blocker : copy.const_blocker_range())
+ add_blocker(*blocker);
+ return *this;
+ }
+
+ /**
+ * return true if both complexes have the same simplices.
+ */
+ bool operator==(const Skeleton_blocker_complex& other) const {
+ if (other.num_vertices() != num_vertices()) return false;
+ if (other.num_edges() != num_edges()) return false;
+ if (other.num_blockers() != num_blockers()) return false;
+
+ for (auto v : vertex_range())
+ if (!other.contains_vertex(v)) return false;
+
+ for (auto e : edge_range())
+ if (!other.contains_edge(first_vertex(e), second_vertex(e))) return false;
+
+ for (const auto b : const_blocker_range())
+ if (!other.contains_blocker(*b)) return false;
+
+ return true;
+ }
+
+ bool operator!=(const Skeleton_blocker_complex& other) const {
+ return !(*this == other);
+ }
+
+ /**
+ * The destructor delete all blockers allocated.
+ */
+ virtual ~Skeleton_blocker_complex() {
+ clear();
+ }
+
+ /**
+ * @details Clears the simplicial complex. After a call to this function,
+ * blockers are destroyed. The 1-skeleton and the set of blockers
+ * are both empty.
+ */
+ virtual void clear() {
+ // xxx for now the responsabilty of freeing the visitor is for
+ // the user
+ visitor = NULL;
+
+ degree_.clear();
+ num_vertices_ = 0;
+
+ remove_blockers();
+
+ skeleton.clear();
+ }
+
+ /**
+ *@brief allows to change the visitor.
+ */
+ void set_visitor(Visitor* other_visitor) {
+ visitor = other_visitor;
+ }
+
+ //@}
+
+ /////////////////////////////////////////////////////////////////////////////
+ /** @name Vertices operations
+ */
+ //@{
+ public:
+ /**
+ * @brief Return a local Vertex_handle of a vertex given a global one.
+ * @remark Assume that the vertex is present in the complex.
+ */
+ Vertex_handle operator[](Root_vertex_handle global) const {
+ auto local(get_address(global));
+ assert(local);
+ return *local;
+ }
+
+ /**
+ * @brief Return the vertex node associated to local Vertex_handle.
+ * @remark Assume that the vertex is present in the complex.
+ */
+ Graph_vertex& operator[](Vertex_handle address) {
+ assert(
+ 0 <= address.vertex && address.vertex < boost::num_vertices(skeleton));
+ return skeleton[address.vertex];
+ }
+
+ /**
+ * @brief Return the vertex node associated to local Vertex_handle.
+ * @remark Assume that the vertex is present in the complex.
+ */
+ const Graph_vertex& operator[](Vertex_handle address) const {
+ assert(
+ 0 <= address.vertex && address.vertex < boost::num_vertices(skeleton));
+ return skeleton[address.vertex];
+ }
+
+ /**
+ * @brief Adds a vertex to the simplicial complex and returns its Vertex_handle.
+ * @remark Vertex representation is contiguous.
+ */
+ Vertex_handle add_vertex() {
+ Vertex_handle address(boost::add_vertex(skeleton));
+ num_vertices_++;
+ (*this)[address].activate();
+ // safe since we now that we are in the root complex and the field 'address' and 'id'
+ // are identical for every vertices
+ (*this)[address].set_id(Root_vertex_handle(address.vertex));
+ degree_.push_back(0);
+ if (visitor)
+ visitor->on_add_vertex(address);
+ return address;
+ }
+
+ /**
+ * @brief Remove a vertex from the simplicial complex
+ * @remark It just deactivates the vertex with a boolean flag but does not
+ * remove it from vertices from complexity issues.
+ */
+ void remove_vertex(Vertex_handle address) {
+ assert(contains_vertex(address));
+ // We remove b
+ boost::clear_vertex(address.vertex, skeleton);
+ (*this)[address].deactivate();
+ num_vertices_--;
+ degree_[address.vertex] = -1;
+ if (visitor)
+ visitor->on_remove_vertex(address);
+ }
+
+ /**
+ */
+ bool contains_vertex(Vertex_handle u) const {
+ if (u.vertex < 0 || u.vertex >= boost::num_vertices(skeleton))
+ return false;
+ return (*this)[u].is_active();
+ }
+
+ /**
+ */
+ bool contains_vertex(Root_vertex_handle u) const {
+ boost::optional<Vertex_handle> address = get_address(u);
+ return address && (*this)[*address].is_active();
+ }
+
+ /**
+ * @return true iff the simplicial complex contains all vertices
+ * of simplex sigma
+ */
+ bool contains_vertices(const Simplex & sigma) const {
+ for (auto vertex : sigma)
+ if (!contains_vertex(vertex))
+ return false;
+ return true;
+ }
+
+ /**
+ * @brief Given an Id return the address of the vertex having this Id in the complex.
+ * @remark For a simplicial complex, the address is the id but it may not be the case for a SubComplex.
+ */
+ virtual boost::optional<Vertex_handle> get_address(
+ Root_vertex_handle id) const {
+ boost::optional<Vertex_handle> res;
+ if (id.vertex < boost::num_vertices(skeleton))
+ res = Vertex_handle(id.vertex); // xxx
+ return res;
+ }
+
+ /**
+ * return the id of a vertex of adress local present in the graph
+ */
+ Root_vertex_handle get_id(Vertex_handle local) const {
+ assert(0 <= local.vertex && local.vertex < boost::num_vertices(skeleton));
+ return (*this)[local].get_id();
+ }
+
+ /**
+ * @brief Convert an address of a vertex of a complex to the address in
+ * the current complex.
+ * @details
+ * If the current complex is a sub (or sup) complex of 'other', it converts
+ * the address of a vertex v expressed in 'other' to the address of the vertex
+ * v in the current one.
+ * @remark this methods uses Root_vertex_handle to identify the vertex and
+ * assumes the vertex is present in the current complex.
+ */
+ Vertex_handle convert_handle_from_another_complex(const Skeleton_blocker_complex& other,
+ Vertex_handle vh_in_other) const {
+ auto vh_in_current_complex = get_address(other.get_id(vh_in_other));
+ assert(vh_in_current_complex);
+ return *vh_in_current_complex;
+ }
+
+ /**
+ * @brief return the graph degree of a vertex.
+ */
+ int degree(Vertex_handle local) const {
+ assert(0 <= local.vertex && local.vertex < boost::num_vertices(skeleton));
+ return degree_[local.vertex];
+ }
+
+ //@}
+
+ /////////////////////////////////////////////////////////////////////////////
+ /** @name Edges operations
+ */
+ //@{
+ public:
+ /**
+ * @brief return an edge handle if the two vertices forms
+ * an edge in the complex
+ */
+ boost::optional<Edge_handle> operator[](
+ const std::pair<Vertex_handle, Vertex_handle>& ab) const {
+ boost::optional<Edge_handle> res;
+ std::pair<Edge_handle, bool> edge_pair(
+ boost::edge(ab.first.vertex, ab.second.vertex, skeleton));
+ if (edge_pair.second)
+ res = edge_pair.first;
+ return res;
+ }
+
+ /**
+ * @brief returns the stored node associated to an edge
+ */
+ Graph_edge& operator[](Edge_handle edge_handle) {
+ return skeleton[edge_handle];
+ }
+
+ /**
+ * @brief returns the stored node associated to an edge
+ */
+ const Graph_edge& operator[](Edge_handle edge_handle) const {
+ return skeleton[edge_handle];
+ }
+
+ /**
+ * @brief returns the first vertex of an edge
+ * @details it assumes that the edge is present in the complex
+ */
+ Vertex_handle first_vertex(Edge_handle edge_handle) const {
+ return static_cast<Vertex_handle> (source(edge_handle, skeleton));
+ }
+
+ /**
+ * @brief returns the first vertex of an edge
+ * @details it assumes that the edge is present in the complex
+ */
+ Vertex_handle second_vertex(Edge_handle edge_handle) const {
+ return static_cast<Vertex_handle> (target(edge_handle, skeleton));
+ }
+
+ /**
+ * @brief returns the simplex made with the two vertices of the edge
+ * @details it assumes that the edge is present in the complex
+
+ */
+ Simplex get_vertices(Edge_handle edge_handle) const {
+ auto edge((*this)[edge_handle]);
+ return Simplex((*this)[edge.first()], (*this)[edge.second()]);
+ }
+
+ /**
+ * @brief Adds an edge between vertices a and b.
+ * @details For instance, the complex contains edge 01 and 12, then calling
+ * add_edge with vertex 0 and 2 will create a complex containing
+ * the edges 01, 12, 20 but not the triangle 012 (and hence this complex
+ * will contains a blocker 012).
+ */
+ Edge_handle add_edge(Vertex_handle a, Vertex_handle b) {
+ // if the edge is already there we musnt go further
+ // as we may add blockers that should not be here
+ if (contains_edge(a, b))
+ return *((*this)[std::make_pair(a, b)]);
+ auto res = add_edge_without_blockers(a, b);
+ add_blockers_after_simplex_insertion(Simplex(a, b));
+ return res;
+ }
+
+ /**
+ * @brief Adds all edges of s in the complex.
+ */
+ void add_edge(const Simplex& s) {
+ for (auto i = s.begin(); i != s.end(); ++i)
+ for (auto j = i; ++j != s.end(); /**/)
+ add_edge(*i, *j);
+ }
+
+ /**
+ * @brief Adds an edge between vertices a and b without blockers.
+ * @details For instance, the complex contains edge 01 and 12, then calling
+ * add_edge with vertex 0 and 2 will create a complex containing
+ * the triangle 012.
+ */
+ Edge_handle add_edge_without_blockers(Vertex_handle a, Vertex_handle b) {
+ assert(contains_vertex(a) && contains_vertex(b));
+ assert(a != b);
+
+ auto edge_handle((*this)[std::make_pair(a, b)]);
+ if (!edge_handle) {
+ edge_handle = boost::add_edge(a.vertex, b.vertex, skeleton).first;
+ (*this)[*edge_handle].setId(get_id(a), get_id(b));
+ degree_[a.vertex]++;
+ degree_[b.vertex]++;
+ if (visitor)
+ visitor->on_add_edge_without_blockers(a, b);
+ }
+ return *edge_handle;
+ }
+
+
+ /**
+ * @brief Adds all edges of s in the complex without adding blockers.
+ */
+ void add_edge_without_blockers(Simplex s) {
+ for (auto i = s.begin(); i != s.end(); ++i) {
+ for (auto j = i; ++j != s.end(); /**/)
+ add_edge_without_blockers(*i, *j);
+ }
+ }
+
+ /**
+ * @brief Removes an edge from the simplicial complex and all its cofaces.
+ * @details returns the former Edge_handle representing the edge
+ */
+ virtual Edge_handle remove_edge(Vertex_handle a, Vertex_handle b) {
+ bool found;
+ Edge_handle edge;
+ tie(edge, found) = boost::edge(a.vertex, b.vertex, skeleton);
+ if (found) {
+ if (visitor)
+ visitor->on_remove_edge(a, b);
+ boost::remove_edge(a.vertex, b.vertex, skeleton);
+ degree_[a.vertex]--;
+ degree_[b.vertex]--;
+ }
+ return edge;
+ }
+
+ /**
+ * @brief Removes edge and its cofaces from the simplicial complex.
+ */
+ void remove_edge(Edge_handle edge) {
+ assert(contains_vertex(first_vertex(edge)));
+ assert(contains_vertex(second_vertex(edge)));
+ remove_edge(first_vertex(edge), second_vertex(edge));
+ }
+
+ /**
+ * @brief The complex is reduced to its set of vertices.
+ * All the edges and blockers are removed.
+ */
+ void keep_only_vertices() {
+ remove_blockers();
+
+ for (auto u : vertex_range()) {
+ while (this->degree(u) > 0) {
+ Vertex_handle v(*(adjacent_vertices(u.vertex, this->skeleton).first));
+ this->remove_edge(u, v);
+ }
+ }
+ }
+
+ /**
+ * @return true iff the simplicial complex contains an edge between
+ * vertices a and b
+ */
+ bool contains_edge(Vertex_handle a, Vertex_handle b) const {
+ // if (a.vertex<0 || b.vertex <0) return false;
+ return boost::edge(a.vertex, b.vertex, skeleton).second;
+ }
+
+ /**
+ * @return true iff the simplicial complex contains all vertices
+ * and all edges of simplex sigma
+ */
+ bool contains_edges(const Simplex & sigma) const {
+ for (auto i = sigma.begin(); i != sigma.end(); ++i) {
+ if (!contains_vertex(*i))
+ return false;
+ for (auto j = i; ++j != sigma.end();) {
+ if (!contains_edge(*i, *j))
+ return false;
+ }
+ }
+ return true;
+ }
+ //@}
+
+ /////////////////////////////////////////////////////////////////////////////
+ /** @name Blockers operations
+ */
+ //@{
+
+ /**
+ * @brief Adds the simplex to the set of blockers and
+ * returns a Blocker_handle toward it if was not present before and 0 otherwise.
+ */
+ Blocker_handle add_blocker(const Simplex& blocker) {
+ assert(blocker.dimension() > 1);
+ if (contains_blocker(blocker)) {
+ return 0;
+ } else {
+ if (visitor)
+ visitor->on_add_blocker(blocker);
+ Blocker_handle blocker_pt = new Simplex(blocker);
+ num_blockers_++;
+ auto vertex = blocker_pt->begin();
+ while (vertex != blocker_pt->end()) {
+ blocker_map_.insert(BlockerPair(*vertex, blocker_pt));
+ ++vertex;
+ }
+ return blocker_pt;
+ }
+ }
+
+ protected:
+ /**
+ * @brief Adds the simplex to the set of blockers
+ */
+ void add_blocker(Blocker_handle blocker) {
+ if (contains_blocker(*blocker)) {
+ // std::cerr << "ATTEMPT TO ADD A BLOCKER ALREADY THERE ---> BLOCKER IGNORED" << endl;
+ return;
+ } else {
+ if (visitor)
+ visitor->on_add_blocker(*blocker);
+ num_blockers_++;
+ auto vertex = blocker->begin();
+ while (vertex != blocker->end()) {
+ blocker_map_.insert(BlockerPair(*vertex, blocker));
+ ++vertex;
+ }
+ }
+ }
+
+ protected:
+ /**
+ * Removes sigma from the blocker map of vertex v
+ */
+ void remove_blocker(const Blocker_handle sigma, Vertex_handle v) {
+ Complex_blocker_around_vertex_iterator blocker;
+ for (blocker = blocker_range(v).begin(); blocker != blocker_range(v).end();
+ ++blocker) {
+ if (*blocker == sigma)
+ break;
+ }
+ if (*blocker != sigma) {
+ std::cerr
+ << "bug ((*blocker).second == sigma) ie try to remove a blocker not present\n";
+ assert(false);
+ } else {
+ blocker_map_.erase(blocker.current_position());
+ }
+ }
+
+ public:
+ /**
+ * @brief Removes the simplex from the set of blockers.
+ * @remark sigma has to belongs to the set of blockers
+ */
+ void remove_blocker(const Blocker_handle sigma) {
+ for (auto vertex : *sigma)
+ remove_blocker(sigma, vertex);
+ num_blockers_--;
+ }
+
+ /**
+ * @brief Remove all blockers, in other words, it expand the simplicial
+ * complex to the smallest flag complex that contains it.
+ */
+ void remove_blockers() {
+ // Desallocate the blockers
+ while (!blocker_map_.empty()) {
+ delete_blocker(blocker_map_.begin()->second);
+ }
+ num_blockers_ = 0;
+ blocker_map_.clear();
+ }
+
+ protected:
+ /**
+ * Removes the simplex sigma from the set of blockers.
+ * sigma has to belongs to the set of blockers
+ *
+ * @remark contrarily to delete_blockers does not call the destructor
+ */
+ void remove_blocker(const Simplex& sigma) {
+ assert(contains_blocker(sigma));
+ for (auto vertex : sigma)
+ remove_blocker(sigma, vertex);
+ num_blockers_--;
+ }
+
+ public:
+ /**
+ * Removes the simplex s from the set of blockers
+ * and desallocate s.
+ */
+ void delete_blocker(Blocker_handle sigma) {
+ if (visitor)
+ visitor->on_delete_blocker(sigma);
+ remove_blocker(sigma);
+ delete sigma;
+ }
+
+ /**
+ * @return true iff s is a blocker of the simplicial complex
+ */
+ bool contains_blocker(const Blocker_handle s) const {
+ if (s->dimension() < 2)
+ return false;
+
+ Vertex_handle a = s->first_vertex();
+
+ for (const auto blocker : const_blocker_range(a)) {
+ if (s == *blocker)
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @return true iff s is a blocker of the simplicial complex
+ */
+ bool contains_blocker(const Simplex & s) const {
+ if (s.dimension() < 2)
+ return false;
+
+ Vertex_handle a = s.first_vertex();
+
+ for (auto blocker : const_blocker_range(a)) {
+ if (s == *blocker)
+ return true;
+ }
+ return false;
+ }
+
+ private:
+ /**
+ * @return true iff a blocker of the simplicial complex
+ * is a face of sigma.
+ */
+ bool blocks(const Simplex & sigma) const {
+ for (auto s : sigma)
+ for (auto blocker : const_blocker_range(s))
+ if (sigma.contains(*blocker))
+ return true;
+ return false;
+ }
+
+ //@}
+
+ protected:
+ /**
+ * @details Adds to simplex the neighbours of v e.g. \f$ n \leftarrow n \cup N(v) \f$.
+ * If keep_only_superior is true then only vertices that are greater than v are added.
+ */
+ virtual void add_neighbours(Vertex_handle v, Simplex & n,
+ bool keep_only_superior = false) const {
+ boost_adjacency_iterator ai, ai_end;
+ for (tie(ai, ai_end) = adjacent_vertices(v.vertex, skeleton); ai != ai_end;
+ ++ai) {
+ if (keep_only_superior) {
+ if (*ai > v.vertex) {
+ n.add_vertex(Vertex_handle(*ai));
+ }
+ } else {
+ n.add_vertex(Vertex_handle(*ai));
+ }
+ }
+ }
+
+ /**
+ * @details Add to simplex res all vertices which are
+ * neighbours of alpha: ie \f$ res \leftarrow res \cup N(alpha) \f$.
+ *
+ * If 'keep_only_superior' is true then only vertices that are greater than alpha are added.
+ * todo revoir
+ *
+ */
+ virtual void add_neighbours(const Simplex &alpha, Simplex & res,
+ bool keep_only_superior = false) const {
+ res.clear();
+ auto alpha_vertex = alpha.begin();
+ add_neighbours(*alpha_vertex, res, keep_only_superior);
+ for (alpha_vertex = (alpha.begin())++; alpha_vertex != alpha.end();
+ ++alpha_vertex)
+ keep_neighbours(*alpha_vertex, res, keep_only_superior);
+ }
+
+ /**
+ * @details Remove from simplex n all vertices which are
+ * not neighbours of v e.g. \f$ res \leftarrow res \cap N(v) \f$.
+ * If 'keep_only_superior' is true then only vertices that are greater than v are keeped.
+ */
+ virtual void keep_neighbours(Vertex_handle v, Simplex& res,
+ bool keep_only_superior = false) const {
+ Simplex nv;
+ add_neighbours(v, nv, keep_only_superior);
+ res.intersection(nv);
+ }
+
+ /**
+ * @details Remove from simplex all vertices which are
+ * neighbours of v eg \f$ res \leftarrow res \setminus N(v) \f$.
+ * If 'keep_only_superior' is true then only vertices that are greater than v are added.
+ */
+ virtual void remove_neighbours(Vertex_handle v, Simplex & res,
+ bool keep_only_superior = false) const {
+ Simplex nv;
+ add_neighbours(v, nv, keep_only_superior);
+ res.difference(nv);
+ }
+
+ public:
+ typedef Skeleton_blocker_link_complex<Skeleton_blocker_complex> Link_complex;
+
+ /**
+ * Constructs the link of 'simplex' with points coordinates.
+ */
+ Link_complex link(Vertex_handle v) const {
+ return Link_complex(*this, Simplex(v));
+ }
+
+ /**
+ * Constructs the link of 'simplex' with points coordinates.
+ */
+ Link_complex link(Edge_handle edge) const {
+ return Link_complex(*this, edge);
+ }
+
+ /**
+ * Constructs the link of 'simplex' with points coordinates.
+ */
+ Link_complex link(const Simplex& simplex) const {
+ return Link_complex(*this, simplex);
+ }
+
+ /**
+ * @brief Compute the local vertices of 's' in the current complex
+ * If one of them is not present in the complex then the return value is uninitialized.
+ *
+ *
+ */
+ // xxx rename get_address et place un using dans sub_complex
+
+ boost::optional<Simplex> get_simplex_address(
+ const Root_simplex_handle& s) const {
+ boost::optional<Simplex> res;
+
+ Simplex s_address;
+ // Root_simplex_const_iterator i;
+ for (auto i = s.begin(); i != s.end(); ++i) {
+ boost::optional<Vertex_handle> address = get_address(*i);
+ if (!address)
+ return res;
+ else
+ s_address.add_vertex(*address);
+ }
+ res = s_address;
+ return res;
+ }
+
+ /**
+ * @brief returns a simplex with vertices which are the id of vertices of the
+ * argument.
+ */
+ Root_simplex_handle get_id(const Simplex& local_simplex) const {
+ Root_simplex_handle global_simplex;
+ for (auto x = local_simplex.begin(); x != local_simplex.end(); ++x) {
+ global_simplex.add_vertex(get_id(*x));
+ }
+ return global_simplex;
+ }
+
+ /**
+ * @brief returns true iff the simplex s belongs to the simplicial
+ * complex.
+ */
+ virtual bool contains(const Simplex & s) const {
+ if (s.dimension() == -1) {
+ return false;
+ } else if (s.dimension() == 0) {
+ return contains_vertex(s.first_vertex());
+ } else {
+ return (contains_edges(s) && !blocks(s));
+ }
+ }
+
+ /*
+ * @brief returnrs true iff the complex is empty.
+ */
+ bool empty() const {
+ return num_vertices() == 0;
+ }
+
+ /*
+ * @brief returns the number of vertices in the complex.
+ */
+ int num_vertices() const {
+ // remark boost::num_vertices(skeleton) counts deactivated vertices
+ return num_vertices_;
+ }
+
+ /*
+ * @brief returns the number of edges in the complex.
+ * @details currently in O(n)
+ */
+ // todo cache the value
+
+ int num_edges() const {
+ return boost::num_edges(skeleton);
+ }
+
+ int num_triangles() const {
+ auto triangles = triangle_range();
+ return std::distance(triangles.begin(), triangles.end());
+ }
+
+
+ /*
+ * @brief returns the number of simplices of a given dimension in the complex.
+ */
+ size_t num_simplices() const {
+ auto simplices = complex_simplex_range();
+ return std::distance(simplices.begin(), simplices.end());
+ }
+
+ /*
+ * @brief returns the number of simplices of a given dimension in the complex.
+ */
+ size_t num_simplices(unsigned dimension) const {
+ // TODO(DS): iterator on k-simplices
+ size_t res = 0;
+ for (const auto& s : complex_simplex_range())
+ if (s.dimension() == dimension)
+ ++res;
+ return res;
+ }
+
+ /*
+ * @brief returns the number of blockers in the complex.
+ */
+ size_t num_blockers() const {
+ return num_blockers_;
+ }
+
+ /*
+ * @brief returns true iff the graph of the 1-skeleton of the complex is complete.
+ */
+ bool complete() const {
+ return (num_vertices() * (num_vertices() - 1)) / 2 == num_edges();
+ }
+
+ /**
+ * @brief returns the number of connected components in the graph of the 1-skeleton.
+ */
+ int num_connected_components() const {
+ int num_vert_collapsed = skeleton.vertex_set().size() - num_vertices();
+ std::vector<int> component(skeleton.vertex_set().size());
+ return boost::connected_components(this->skeleton, &component[0])
+ - num_vert_collapsed;
+ }
+
+ /**
+ * @brief %Test if the complex is a cone.
+ * @details Runs in O(n) where n is the number of vertices.
+ */
+ bool is_cone() const {
+ if (num_vertices() == 0)
+ return false;
+ if (num_vertices() == 1)
+ return true;
+ for (auto vi : vertex_range()) {
+ // xxx todo faire une methode bool is_in_blocker(Vertex_handle)
+ if (blocker_map_.find(vi) == blocker_map_.end()) {
+ // no blocker passes through the vertex, we just need to
+ // check if the current vertex is linked to all others vertices of the complex
+ if (degree_[vi.vertex] == num_vertices() - 1)
+ return true;
+ }
+ }
+ return false;
+ }
+
+ //@}
+ /** @name Simplification operations
+ */
+ //@{
+
+ /**
+ * Returns true iff the blocker 'sigma' is popable.
+ * To define popable, let us call 'L' the complex that
+ * consists in the current complex without the blocker 'sigma'.
+ * A blocker 'sigma' is then "popable" if the link of 'sigma'
+ * in L is reducible.
+ *
+ */
+ bool is_popable_blocker(Blocker_handle sigma) const;
+
+ /**
+ * Removes all the popable blockers of the complex and delete them.
+ * @returns the number of popable blockers deleted
+ */
+ void remove_popable_blockers();
+
+ /**
+ * Removes all the popable blockers of the complex passing through v and delete them.
+ */
+ void remove_popable_blockers(Vertex_handle v);
+
+ /**
+ * @brief Removes all the popable blockers of the complex passing through v and delete them.
+ * Also remove popable blockers in the neighborhood if they became popable.
+ *
+ */
+ void remove_all_popable_blockers(Vertex_handle v);
+
+ /**
+ * Remove the star of the vertex 'v'
+ */
+ void remove_star(Vertex_handle v);
+
+ private:
+ /**
+ * after removing the star of a simplex, blockers sigma that contains this simplex must be removed.
+ * Furthermore, all simplices tau of the form sigma \setminus simplex_to_be_removed must be added
+ * whenever the dimension of tau is at least 2.
+ */
+ void update_blockers_after_remove_star_of_vertex_or_edge(const Simplex& simplex_to_be_removed);
+
+ public:
+ /**
+ * Remove the star of the edge connecting vertices a and b.
+ * @returns the number of blocker that have been removed
+ */
+ void remove_star(Vertex_handle a, Vertex_handle b);
+
+ /**
+ * Remove the star of the edge 'e'.
+ */
+ void remove_star(Edge_handle e);
+
+ /**
+ * Remove the star of the simplex 'sigma' which needs to belong to the complex
+ */
+ void remove_star(const Simplex& sigma);
+
+ /**
+ * @brief add a simplex and all its faces.
+ * @details the simplex must have dimension greater than one (otherwise use add_vertex or add_edge_without_blockers).
+ */
+ void add_simplex(const Simplex& sigma);
+
+ private:
+ void add_blockers_after_simplex_insertion(Simplex s);
+
+ /**
+ * remove all blockers that contains sigma
+ */
+ void remove_blocker_containing_simplex(const Simplex& sigma);
+
+ /**
+ * remove all blockers that contains sigma
+ */
+ void remove_blocker_include_in_simplex(const Simplex& sigma);
+
+ public:
+ enum simplifiable_status {
+ NOT_HOMOTOPY_EQ, MAYBE_HOMOTOPY_EQ, HOMOTOPY_EQ
+ };
+
+ simplifiable_status is_remove_star_homotopy_preserving(const Simplex& simplex) {
+ // todo write a virtual method 'link' in Skeleton_blocker_complex which will be overloaded by the current one of
+ // Skeleton_blocker_geometric_complex
+ // then call it there to build the link and return the value of link.is_contractible()
+ return MAYBE_HOMOTOPY_EQ;
+ }
+
+ enum contractible_status {
+ NOT_CONTRACTIBLE, MAYBE_CONTRACTIBLE, CONTRACTIBLE
+ };
+
+ /**
+ * @brief %Test if the complex is reducible using a strategy defined in the class
+ * (by default it tests if the complex is a cone)
+ * @details Note that NO could be returned if some invariant ensures that the complex
+ * is not a point (for instance if the euler characteristic is different from 1).
+ * This function will surely have to return MAYBE in some case because the
+ * associated problem is undecidable but it in practice, it can often
+ * be solved with the help of geometry.
+ */
+ virtual contractible_status is_contractible() const {
+ if (this->is_cone()) {
+ return CONTRACTIBLE;
+ } else {
+ return MAYBE_CONTRACTIBLE;
+ }
+ }
+ //@}
+
+ /** @name Edge contraction operations
+ */
+ //@{
+
+ /**
+ * @return If ignore_popable_blockers is true
+ * then the result is true iff the link condition at edge ab is satisfied
+ * or equivalently iff no blocker contains ab.
+ * If ignore_popable_blockers is false then the
+ * result is true iff all blocker containing ab are popable.
+ */
+ bool link_condition(Vertex_handle a, Vertex_handle b, bool ignore_popable_blockers = false) const {
+ for (auto blocker : this->const_blocker_range(a))
+ if (blocker->contains(b)) {
+ // false if ignore_popable_blockers is false
+ // otherwise the blocker has to be popable
+ return ignore_popable_blockers && is_popable_blocker(blocker);
+ }
+ return true;
+ }
+
+ /**
+ * @return If ignore_popable_blockers is true
+ * then the result is true iff the link condition at edge ab is satisfied
+ * or equivalently iff no blocker contains ab.
+ * If ignore_popable_blockers is false then the
+ * result is true iff all blocker containing ab are popable.
+ */
+ bool link_condition(Edge_handle e, bool ignore_popable_blockers = false) const {
+ const Graph_edge& edge = (*this)[e];
+ assert(this->get_address(edge.first()));
+ assert(this->get_address(edge.second()));
+ Vertex_handle a(*this->get_address(edge.first()));
+ Vertex_handle b(*this->get_address(edge.second()));
+ return link_condition(a, b, ignore_popable_blockers);
+ }
+
+ protected:
+ /**
+ * Compute simplices beta such that a.beta is an order 0 blocker
+ * that may be used to construct a new blocker after contracting ab.
+ * It requires that the link condition is satisfied.
+ */
+ void tip_blockers(Vertex_handle a, Vertex_handle b, std::vector<Simplex> & buffer) const;
+
+ private:
+ /**
+ * @brief "Replace" the edge 'bx' by the edge 'ax'.
+ * Assume that the edge 'bx' was present whereas 'ax' was not.
+ * Precisely, it does not replace edges, but remove 'bx' and then add 'ax'.
+ * The visitor 'on_swaped_edge' is called just after edge 'ax' had been added
+ * and just before edge 'bx' had been removed. That way, it can
+ * eventually access to information of 'ax'.
+ */
+ void swap_edge(Vertex_handle a, Vertex_handle b, Vertex_handle x);
+
+ private:
+ /**
+ * @brief removes all blockers passing through the edge 'ab'
+ */
+ void delete_blockers_around_vertex(Vertex_handle v);
+
+ /**
+ * @brief removes all blockers passing through the edge 'ab'
+ */
+ void delete_blockers_around_edge(Vertex_handle a, Vertex_handle b);
+
+ public:
+ /**
+ * Contracts the edge.
+ * @remark If the link condition Link(ab) = Link(a) inter Link(b) is not satisfied,
+ * it removes first all blockers passing through 'ab'
+ */
+ void contract_edge(Edge_handle edge) {
+ contract_edge(this->first_vertex(edge), this->second_vertex(edge));
+ }
+
+ /**
+ * Contracts the edge connecting vertices a and b.
+ * @remark If the link condition Link(ab) = Link(a) inter Link(b) is not satisfied,
+ * it removes first all blockers passing through 'ab'
+ */
+ void contract_edge(Vertex_handle a, Vertex_handle b);
+
+ private:
+ void get_blockers_to_be_added_after_contraction(Vertex_handle a, Vertex_handle b,
+ std::set<Simplex>& blockers_to_add);
+ /**
+ * delete all blockers that passes through a or b
+ */
+ void delete_blockers_around_vertices(Vertex_handle a, Vertex_handle b);
+ void update_edges_after_contraction(Vertex_handle a, Vertex_handle b);
+ void notify_changed_edges(Vertex_handle a);
+ //@}
+
+ public:
+ /////////////////////////////////////////////////////////////////////////////
+ /** @name Vertex iterators
+ */
+ //@{
+ typedef Vertex_iterator<Skeleton_blocker_complex> Complex_vertex_iterator;
+
+ //
+ // Range over the vertices of the simplicial complex.
+ // Methods .begin() and .end() return a Complex_vertex_iterator.
+ //
+ typedef boost::iterator_range<Complex_vertex_iterator> Complex_vertex_range;
+
+ /**
+ * @brief Returns a Complex_vertex_range over all vertices of the complex
+ */
+ Complex_vertex_range vertex_range() const {
+ auto begin = Complex_vertex_iterator(this);
+ auto end = Complex_vertex_iterator(this, 0);
+ return Complex_vertex_range(begin, end);
+ }
+
+ typedef Neighbors_vertices_iterator<Skeleton_blocker_complex> Complex_neighbors_vertices_iterator;
+
+
+ typedef boost::iterator_range<Complex_neighbors_vertices_iterator> Complex_neighbors_vertices_range;
+
+ /**
+ * @brief Returns a Complex_edge_range over all edges of the simplicial complex that passes trough v
+ */
+ Complex_neighbors_vertices_range vertex_range(Vertex_handle v) const {
+ auto begin = Complex_neighbors_vertices_iterator(this, v);
+ auto end = Complex_neighbors_vertices_iterator(this, v, 0);
+ return Complex_neighbors_vertices_range(begin, end);
+ }
+
+ //@}
+
+ /** @name Edge iterators
+ */
+ //@{
+
+ typedef Edge_iterator<Skeleton_blocker_complex> Complex_edge_iterator;
+
+
+ typedef boost::iterator_range<Complex_edge_iterator> Complex_edge_range;
+
+ /**
+ * @brief Returns a Complex_edge_range over all edges of the simplicial complex
+ */
+ Complex_edge_range edge_range() const {
+ auto begin = Complex_edge_iterator(this);
+ auto end = Complex_edge_iterator(this, 0);
+ return Complex_edge_range(begin, end);
+ }
+
+
+ typedef Edge_around_vertex_iterator<Skeleton_blocker_complex> Complex_edge_around_vertex_iterator;
+
+
+ typedef boost::iterator_range <Complex_edge_around_vertex_iterator> Complex_edge_around_vertex_range;
+
+ /**
+ * @brief Returns a Complex_edge_range over all edges of the simplicial complex that passes
+ * through 'v'
+ */
+ Complex_edge_around_vertex_range edge_range(Vertex_handle v) const {
+ auto begin = Complex_edge_around_vertex_iterator(this, v);
+ auto end = Complex_edge_around_vertex_iterator(this, v, 0);
+ return Complex_edge_around_vertex_range(begin, end);
+ }
+
+ //@}
+
+ /** @name Triangles iterators
+ */
+ //@{
+ private:
+ typedef Skeleton_blocker_link_complex<Skeleton_blocker_complex<SkeletonBlockerDS> > Link;
+ typedef Skeleton_blocker_link_superior<Skeleton_blocker_complex<SkeletonBlockerDS> > Superior_link;
+
+ public:
+ typedef Triangle_around_vertex_iterator<Skeleton_blocker_complex, Superior_link>
+ Superior_triangle_around_vertex_iterator;
+ typedef boost::iterator_range < Triangle_around_vertex_iterator<Skeleton_blocker_complex, Link> >
+ Complex_triangle_around_vertex_range;
+
+ /**
+ * @brief Range over triangles around a vertex of the simplicial complex.
+ * Methods .begin() and .end() return a Triangle_around_vertex_iterator.
+ *
+ */
+ Complex_triangle_around_vertex_range triangle_range(Vertex_handle v) const {
+ auto begin = Triangle_around_vertex_iterator<Skeleton_blocker_complex, Link>(this, v);
+ auto end = Triangle_around_vertex_iterator<Skeleton_blocker_complex, Link>(this, v, 0);
+ return Complex_triangle_around_vertex_range(begin, end);
+ }
+
+ typedef boost::iterator_range<Triangle_iterator<Skeleton_blocker_complex> > Complex_triangle_range;
+ typedef Triangle_iterator<Skeleton_blocker_complex> Complex_triangle_iterator;
+
+ /**
+ * @brief Range over triangles of the simplicial complex.
+ * Methods .begin() and .end() return a Triangle_around_vertex_iterator.
+ *
+ */
+ Complex_triangle_range triangle_range() const {
+ auto end = Triangle_iterator<Skeleton_blocker_complex>(this, 0);
+ if (empty()) {
+ return Complex_triangle_range(end, end);
+ } else {
+ auto begin = Triangle_iterator<Skeleton_blocker_complex>(this);
+ return Complex_triangle_range(begin, end);
+ }
+ }
+
+ //@}
+
+ /** @name Simplices iterators
+ */
+ //@{
+ typedef Simplex_around_vertex_iterator<Skeleton_blocker_complex, Link> Complex_simplex_around_vertex_iterator;
+
+ /**
+ * @brief Range over the simplices of the simplicial complex around a vertex.
+ * Methods .begin() and .end() return a Complex_simplex_around_vertex_iterator.
+ */
+ typedef boost::iterator_range < Complex_simplex_around_vertex_iterator > Complex_simplex_around_vertex_range;
+
+ /**
+ * @brief Returns a Complex_simplex_around_vertex_range over all the simplices around a vertex of the complex
+ */
+ Complex_simplex_around_vertex_range star_simplex_range(Vertex_handle v) const {
+ assert(contains_vertex(v));
+ return Complex_simplex_around_vertex_range(
+ Complex_simplex_around_vertex_iterator(this, v),
+ Complex_simplex_around_vertex_iterator(this, v, true));
+ }
+ typedef Simplex_coboundary_iterator<Skeleton_blocker_complex, Link> Complex_simplex_coboundary_iterator;
+
+ /**
+ * @brief Range over the simplices of the coboundary of a simplex.
+ * Methods .begin() and .end() return a Complex_simplex_coboundary_iterator.
+ */
+ typedef boost::iterator_range < Complex_simplex_coboundary_iterator > Complex_coboundary_range;
+
+ /**
+ * @brief Returns a Complex_simplex_coboundary_iterator over the simplices of the coboundary of a simplex.
+ */
+ Complex_coboundary_range coboundary_range(const Simplex& s) const {
+ assert(contains(s));
+ return Complex_coboundary_range(Complex_simplex_coboundary_iterator(this, s),
+ Complex_simplex_coboundary_iterator(this, s, true));
+ }
+
+ // typedef Simplex_iterator<Skeleton_blocker_complex,Superior_link> Complex_simplex_iterator;
+ typedef Simplex_iterator<Skeleton_blocker_complex> Complex_simplex_iterator;
+
+ typedef boost::iterator_range < Complex_simplex_iterator > Complex_simplex_range;
+
+ /**
+ * @brief Returns a Complex_simplex_range over all the simplices of the complex
+ */
+ Complex_simplex_range complex_simplex_range() const {
+ Complex_simplex_iterator end(this, true);
+ if (empty()) {
+ return Complex_simplex_range(end, end);
+ } else {
+ Complex_simplex_iterator begin(this);
+ return Complex_simplex_range(begin, end);
+ }
+ }
+
+ //@}
+
+ /** @name Blockers iterators
+ */
+ //@{
+ private:
+ /**
+ * @brief Iterator over the blockers adjacent to a vertex
+ */
+ typedef Blocker_iterator_around_vertex_internal<
+ typename std::multimap<Vertex_handle, Simplex *>::iterator,
+ Blocker_handle>
+ Complex_blocker_around_vertex_iterator;
+
+ /**
+ * @brief Iterator over (constant) blockers adjacent to a vertex
+ */
+ typedef Blocker_iterator_around_vertex_internal<
+ typename std::multimap<Vertex_handle, Simplex *>::const_iterator,
+ const Blocker_handle>
+ Const_complex_blocker_around_vertex_iterator;
+
+ typedef boost::iterator_range <Complex_blocker_around_vertex_iterator> Complex_blocker_around_vertex_range;
+ typedef boost::iterator_range <Const_complex_blocker_around_vertex_iterator>
+ Const_complex_blocker_around_vertex_range;
+
+ public:
+ /**
+ * @brief Returns a range of the blockers of the complex passing through a vertex
+ */
+ Complex_blocker_around_vertex_range blocker_range(Vertex_handle v) {
+ auto begin = Complex_blocker_around_vertex_iterator(blocker_map_.lower_bound(v));
+ auto end = Complex_blocker_around_vertex_iterator(blocker_map_.upper_bound(v));
+ return Complex_blocker_around_vertex_range(begin, end);
+ }
+
+ /**
+ * @brief Returns a range of the blockers of the complex passing through a vertex
+ */
+ Const_complex_blocker_around_vertex_range const_blocker_range(Vertex_handle v) const {
+ auto begin = Const_complex_blocker_around_vertex_iterator(blocker_map_.lower_bound(v));
+ auto end = Const_complex_blocker_around_vertex_iterator(blocker_map_.upper_bound(v));
+ return Const_complex_blocker_around_vertex_range(begin, end);
+ }
+
+ private:
+ /**
+ * @brief Iterator over the blockers.
+ */
+ typedef Blocker_iterator_internal<
+ typename std::multimap<Vertex_handle, Simplex *>::iterator,
+ Blocker_handle>
+ Complex_blocker_iterator;
+
+ /**
+ * @brief Iterator over the (constant) blockers.
+ */
+ typedef Blocker_iterator_internal<
+ typename std::multimap<Vertex_handle, Simplex *>::const_iterator,
+ const Blocker_handle>
+ Const_complex_blocker_iterator;
+
+ typedef boost::iterator_range <Complex_blocker_iterator> Complex_blocker_range;
+ typedef boost::iterator_range <Const_complex_blocker_iterator> Const_complex_blocker_range;
+
+ public:
+ /**
+ * @brief Returns a range of the blockers of the complex
+ */
+ Complex_blocker_range blocker_range() {
+ auto begin = Complex_blocker_iterator(blocker_map_.begin(), blocker_map_.end());
+ auto end = Complex_blocker_iterator(blocker_map_.end(), blocker_map_.end());
+ return Complex_blocker_range(begin, end);
+ }
+
+ /**
+ * @brief Returns a range of the blockers of the complex
+ */
+ Const_complex_blocker_range const_blocker_range() const {
+ auto begin = Const_complex_blocker_iterator(blocker_map_.begin(), blocker_map_.end());
+ auto end = Const_complex_blocker_iterator(blocker_map_.end(), blocker_map_.end());
+ return Const_complex_blocker_range(begin, end);
+ }
+
+ //@}
+
+ /////////////////////////////////////////////////////////////////////////////
+ /** @name Print and IO methods
+ */
+ //@{
+ public:
+ std::string to_string() const {
+ std::ostringstream stream;
+ stream << num_vertices() << " vertices:\n" << vertices_to_string() << std::endl;
+ stream << num_edges() << " edges:\n" << edges_to_string() << std::endl;
+ stream << num_blockers() << " blockers:\n" << blockers_to_string() << std::endl;
+ return stream.str();
+ }
+
+ std::string vertices_to_string() const {
+ std::ostringstream stream;
+ for (auto vertex : vertex_range()) {
+ stream << "{" << (*this)[vertex].get_id() << "} ";
+ }
+ stream << std::endl;
+ return stream.str();
+ }
+
+ std::string edges_to_string() const {
+ std::ostringstream stream;
+ for (auto edge : edge_range())
+ stream << "{" << (*this)[edge].first() << "," << (*this)[edge].second() << "} ";
+ stream << std::endl;
+ return stream.str();
+ }
+
+ std::string blockers_to_string() const {
+ std::ostringstream stream;
+
+ for (auto b : const_blocker_range())
+ stream << *b << std::endl;
+ return stream.str();
+ }
+ //@}
+};
+
+/**
+ * build a simplicial complex from a collection
+ * of top faces.
+ * return the total number of simplices
+ */
+template<typename Complex, typename SimplexHandleIterator>
+Complex make_complex_from_top_faces(SimplexHandleIterator simplices_begin, SimplexHandleIterator simplices_end,
+ bool is_flag_complex = false) {
+ // TODO(DS): use add_simplex instead! should be more efficient and more elegant :)
+ typedef typename Complex::Simplex Simplex;
+ std::vector<Simplex> simplices;
+ for (auto top_face = simplices_begin; top_face != simplices_end; ++top_face) {
+ auto subfaces_topface = subfaces(*top_face);
+ simplices.insert(simplices.end(), subfaces_topface.begin(), subfaces_topface.end());
+ }
+ return Complex(simplices.begin(), simplices.end(), is_flag_complex);
+}
+
+} // namespace skeleton_blocker
+
+namespace skbl = skeleton_blocker;
+
+} // namespace Gudhi
+
+#include "Skeleton_blocker_simplifiable_complex.h"
+
+#endif // SKELETON_BLOCKER_COMPLEX_H_
diff --git a/include/gudhi/Skeleton_blocker_contractor.h b/include/gudhi/Skeleton_blocker_contractor.h
new file mode 100644
index 00000000..df884c93
--- /dev/null
+++ b/include/gudhi/Skeleton_blocker_contractor.h
@@ -0,0 +1,582 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SKELETON_BLOCKER_CONTRACTOR_H_
+#define SKELETON_BLOCKER_CONTRACTOR_H_
+
+// todo remove the queue to be independent from cgald
+#include <gudhi/Contraction/CGAL_queue/Modifiable_priority_queue.h>
+
+#include <gudhi/Contraction/Edge_profile.h>
+#include <gudhi/Contraction/policies/Cost_policy.h>
+#include <gudhi/Contraction/policies/Edge_length_cost.h>
+#include <gudhi/Contraction/policies/Placement_policy.h>
+#include <gudhi/Contraction/policies/First_vertex_placement.h>
+#include <gudhi/Contraction/policies/Valid_contraction_policy.h>
+#include <gudhi/Contraction/policies/Dummy_valid_contraction.h> // xxx remove
+#include <gudhi/Contraction/policies/Link_condition_valid_contraction.h> // xxx remove
+#include <gudhi/Contraction/policies/Contraction_visitor.h>
+
+#include <gudhi/Skeleton_blocker/Skeleton_blocker_complex_visitor.h>
+#include <gudhi/Debug_utils.h>
+
+
+#include <boost/scoped_array.hpp>
+#include <boost/scoped_ptr.hpp>
+
+#include <memory>
+#include <cassert>
+#include <list>
+#include <utility> // for pair
+#include <vector>
+
+namespace Gudhi {
+
+namespace contraction {
+
+template <class Profile>
+Placement_policy<Profile>* make_first_vertex_placement() {
+ return new First_vertex_placement<Profile>();
+}
+
+template <class Profile>
+Valid_contraction_policy<Profile>* make_link_valid_contraction() {
+ return new Link_condition_valid_contraction<Profile>();
+}
+
+/**
+ *@brief Visitor to remove popable blockers after an edge contraction.
+ */
+template <class Profile>
+class Contraction_visitor_remove_popable : public Contraction_visitor<Profile> {
+ public:
+ typedef typename Profile::Point Point;
+ typedef typename Profile::Complex Complex;
+ typedef typename Complex::Vertex_handle Vertex_handle;
+
+ void on_contracted(const Profile &profile, boost::optional< Point > placement) override {
+ profile.complex().remove_all_popable_blockers(profile.v0_handle());
+ }
+};
+
+template <class Profile>
+Contraction_visitor<Profile>* make_remove_popable_blockers_visitor() {
+ return new Contraction_visitor_remove_popable<Profile>();
+}
+
+/**
+ *@class Skeleton_blocker_contractor
+ *@brief Class that allows to contract iteratively edges of a simplicial complex.
+ *@ingroup contr
+ *
+ * @details The simplification algorithm consists in iteratively picking the
+ * edge with lowest cost and performing an edge contraction if the contraction is valid.
+ * This class is policy based (and much inspired from the edge collapse package of CGAL http://doc.cgal.org/latest/Surface_mesh_simplification/index.html).
+ *
+ * Policies that can be changed are :
+ * - the cost policy : how much cost an edge contraction
+ * - the placement policy : where will be placed the contraction point
+ * - the valid contraction policy : is the contraction valid. For instance, it can be
+ * a topological condition (link condition) or a geometrical condition (normals messed up).
+ *
+ */
+template<class GeometricSimplifiableComplex, class EdgeProfile = Edge_profile<GeometricSimplifiableComplex>>
+class Skeleton_blocker_contractor : private skeleton_blocker::Dummy_complex_visitor<
+typename GeometricSimplifiableComplex::Vertex_handle> {
+ GeometricSimplifiableComplex& complex_;
+
+ public:
+ typedef typename GeometricSimplifiableComplex::Graph_vertex Graph_vertex;
+ typedef typename GeometricSimplifiableComplex::Vertex_handle Vertex_handle;
+ typedef typename GeometricSimplifiableComplex::Simplex Simplex;
+ typedef typename GeometricSimplifiableComplex::Root_vertex_handle Root_vertex_handle;
+ typedef typename GeometricSimplifiableComplex::Graph_edge Graph_edge;
+ typedef typename GeometricSimplifiableComplex::Edge_handle Edge_handle;
+ typedef typename GeometricSimplifiableComplex::Point Point;
+
+ typedef EdgeProfile Profile;
+
+
+ typedef Cost_policy<Profile> Cost_policy_;
+ typedef Placement_policy<Profile> Placement_policy_;
+ typedef Valid_contraction_policy<Profile> Valid_contraction_policy_;
+ typedef Contraction_visitor<EdgeProfile> Contraction_visitor_;
+ typedef Edge_profile_factory<EdgeProfile> Edge_profile_factory_;
+
+
+
+ typedef boost::optional<double> Cost_type;
+ typedef boost::optional<Point> Placement_type;
+
+ typedef size_t size_type;
+
+ typedef Skeleton_blocker_contractor Self;
+
+ private:
+ struct Compare_id {
+ Compare_id() : algorithm_(0) { }
+
+ Compare_id(Self const* aAlgorithm) : algorithm_(aAlgorithm) { }
+
+ bool operator()(Edge_handle a, Edge_handle b) const {
+ return algorithm_->get_undirected_edge_id(a) < algorithm_->get_undirected_edge_id(b);
+ }
+
+ Self const* algorithm_;
+ };
+
+ struct Compare_cost {
+ Compare_cost() : algorithm_(0) { }
+
+ Compare_cost(Self const* aAlgorithm) : algorithm_(aAlgorithm) { }
+
+ bool operator()(Edge_handle a, Edge_handle b) const {
+ // NOTE: A cost is an optional<> value.
+ // Absent optionals are ordered first; that is, "none < T" and "T > none" for any defined T != none.
+ // In consequence, edges with undefined costs will be promoted to the top of the priority queue and popped out
+ // first.
+ return algorithm_->get_data(a).cost() < algorithm_->get_data(b).cost();
+ }
+
+ Self const* algorithm_;
+ };
+
+ struct Undirected_edge_id : boost::put_get_helper<size_type, Undirected_edge_id> {
+ typedef boost::readable_property_map_tag category;
+ typedef size_type value_type;
+ typedef size_type reference;
+ typedef Edge_handle key_type;
+
+ Undirected_edge_id() : algorithm_(0) { }
+
+ Undirected_edge_id(Self const* aAlgorithm) : algorithm_(aAlgorithm) { }
+
+ size_type operator[](Edge_handle e) const {
+ return algorithm_->get_undirected_edge_id(e);
+ }
+
+ Self const* algorithm_;
+ };
+
+ typedef CGAL::Modifiable_priority_queue<Edge_handle, Compare_cost, Undirected_edge_id> PQ;
+ typedef typename PQ::handle pq_handle;
+
+
+ // An Edge_data is associated with EVERY edge in the complex (collapsible or not).
+ // It relates the edge with the PQ-handle needed to update the priority queue
+ // It also relates the edge with a policy-based cache
+
+ class Edge_data {
+ public:
+ Edge_data() : PQHandle_(), cost_() { }
+
+ Cost_type const& cost() const {
+ return cost_;
+ }
+
+ Cost_type & cost() {
+ return cost_;
+ }
+
+ pq_handle PQ_handle() const {
+ return PQHandle_;
+ }
+
+ bool is_in_PQ() const {
+ return PQHandle_ != PQ::null_handle();
+ }
+
+ void set_PQ_handle(pq_handle h) {
+ PQHandle_ = h;
+ }
+
+ void reset_PQ_handle() {
+ PQHandle_ = PQ::null_handle();
+ }
+
+ private:
+ pq_handle PQHandle_;
+ Cost_type cost_;
+ };
+ typedef Edge_data* Edge_data_ptr;
+ typedef boost::scoped_array<Edge_data> Edge_data_array;
+
+ int get_undirected_edge_id(Edge_handle edge) const {
+ return complex_[edge].index();
+ }
+
+ const Edge_data& get_data(Edge_handle edge) const {
+ return edge_data_array_[get_undirected_edge_id(edge)];
+ }
+
+ Edge_data& get_data(Edge_handle edge) {
+ return edge_data_array_[get_undirected_edge_id(edge)];
+ }
+
+ Cost_type get_cost(const Profile & profile) const {
+ return (*cost_policy_)(profile, get_placement(profile));
+ }
+
+ Profile create_profile(Edge_handle edge) const {
+ if (edge_profile_factory_)
+ return edge_profile_factory_->make_profile(complex_, edge);
+ else
+ return Profile(complex_, edge);
+ }
+
+ void insert_in_PQ(Edge_handle edge, Edge_data& data) {
+ data.set_PQ_handle(heap_PQ_->push(edge));
+ ++current_num_edges_heap_;
+ }
+
+ void update_in_PQ(Edge_handle edge, Edge_data& data) {
+ data.set_PQ_handle(heap_PQ_->update(edge, data.PQ_handle()));
+ }
+
+ void remove_from_PQ(Edge_handle edge, Edge_data& data) {
+ data.set_PQ_handle(heap_PQ_->erase(edge, data.PQ_handle()));
+ --current_num_edges_heap_;
+ }
+
+ boost::optional<Edge_handle> pop_from_PQ() {
+ boost::optional<Edge_handle> edge = heap_PQ_->extract_top();
+ if (edge)
+ get_data(*edge).reset_PQ_handle();
+ return edge;
+ }
+
+ private:
+ /**
+ * @brief Collect edges.
+ *
+ * Iterates over all edges of the simplicial complex and
+ * 1) inserts them in the priority queue sorted according to the Cost policy.
+ * 2) set the id() field of each edge
+ */
+ void collect_edges() {
+ //
+ // Loop over all the edges in the complex in the heap
+ //
+ size_type size = complex_.num_edges();
+ DBG("Collecting edges ...");
+ DBGMSG("num edges :", size);
+
+ edge_data_array_.reset(new Edge_data[size]);
+
+ heap_PQ_.reset(new PQ(size, Compare_cost(this), Undirected_edge_id(this)));
+
+ std::size_t id = 0;
+
+ // xxx do a parralel for
+ for (auto edge : complex_.edge_range()) {
+ complex_[edge].index() = id++;
+ Profile const& profile = create_profile(edge);
+ Edge_data& data = get_data(edge);
+ data.cost() = get_cost(profile);
+ ++initial_num_edges_heap_;
+ insert_in_PQ(edge, data);
+ if (contraction_visitor_) contraction_visitor_->on_collected(profile, data.cost());
+ }
+
+ DBG("Edges collected.");
+ }
+
+ bool should_stop(double lCost, const Profile &profile) const {
+ return false;
+ }
+
+ boost::optional<Point> get_placement(const Profile& profile) const {
+ return (*placement_policy_)(profile);
+ }
+
+ bool is_contraction_valid(Profile const& profile, Placement_type placement) const {
+ if (!valid_contraction_policy_) return true;
+ return (*valid_contraction_policy_)(profile, placement);
+ }
+
+
+ public:
+ /**
+ * \brief Contract edges.
+ *
+ * While the heap is not empty, it extracts the edge with the minimum
+ * cost in the heap then try to contract it.
+ * It stops when the Stop policy says so or when the number of contractions
+ * given by 'num_max_contractions' is reached (if this number is positive).
+ */
+ void contract_edges(int num_max_contractions = -1) {
+ DBG("\n\nContract edges");
+ int num_contraction = 0;
+
+ bool unspecified_num_contractions = (num_max_contractions == -1);
+ //
+ // Pops and processes each edge from the PQ
+ //
+ boost::optional<Edge_handle> edge;
+ while ((edge = pop_from_PQ()) && ((num_contraction < num_max_contractions) || (unspecified_num_contractions))) {
+ Profile const& profile = create_profile(*edge);
+ Cost_type cost(get_data(*edge).cost());
+ if (contraction_visitor_) contraction_visitor_->on_selected(profile, cost, 0, 0);
+
+ DBGMSG("\n\n---- Pop edge - num vertices :", complex_.num_vertices());
+
+ if (cost) {
+ DBGMSG("sqrt(cost):", std::sqrt(*cost));
+ if (should_stop(*cost, profile)) {
+ if (contraction_visitor_) contraction_visitor_->on_stop_condition_reached();
+ DBG("should_stop");
+ break;
+ }
+ Placement_type placement = get_placement(profile);
+ if (is_contraction_valid(profile, placement) && placement) {
+ DBG("contraction_valid");
+ contract_edge(profile, placement);
+ ++num_contraction;
+ } else {
+ DBG("contraction not valid");
+ if (contraction_visitor_) contraction_visitor_->on_non_valid(profile);
+ }
+ } else {
+ DBG("uncomputable cost");
+ }
+ }
+ if (contraction_visitor_) contraction_visitor_->on_stop_condition_reached();
+ }
+
+ bool is_in_heap(Edge_handle edge) const {
+ if (heap_PQ_->empty()) {
+ return false;
+ } else {
+ return edge_data_array_[get_undirected_edge_id(edge)].is_in_PQ();
+ }
+ }
+
+ bool is_heap_empty() const {
+ return heap_PQ_->empty();
+ }
+
+ /**
+ * @brief Returns an Edge_handle and a Placement_type. This pair consists in
+ * the edge with the lowest cost in the heap together with its placement.
+ * The returned value is initialized iff the heap is non-empty.
+ */
+ boost::optional<std::pair<Edge_handle, Placement_type > > top_edge() {
+ boost::optional<std::pair<Edge_handle, Placement_type > > res;
+
+ if (!heap_PQ_->empty()) {
+ auto edge = heap_PQ_->top();
+ Profile const& profile = create_profile(edge);
+ Placement_type placement = get_placement(profile);
+ res = std::make_pair(edge, placement);
+ DBGMSG("top edge:", complex_[edge]);
+ }
+ return res;
+ }
+
+ /**
+ * @brief Constructor with default policies.
+ *
+ * @details The default cost, placement, valid and visitor policies
+ * are respectively : the edge length, the first point, the link condition
+ */
+ Skeleton_blocker_contractor(GeometricSimplifiableComplex& complex)
+ : complex_(complex),
+ cost_policy_(new Edge_length_cost<Profile>),
+ placement_policy_(new First_vertex_placement<Profile>),
+ valid_contraction_policy_(new Link_condition_valid_contraction<Profile>),
+ contraction_visitor_(new Contraction_visitor_()),
+ edge_profile_factory_(0),
+ initial_num_edges_heap_(0),
+ current_num_edges_heap_(0) {
+ complex_.set_visitor(this);
+ if (contraction_visitor_) contraction_visitor_->on_started(complex);
+ collect_edges();
+ }
+
+ /**
+ * @brief Constructor with customed policies.
+ * @remark Policies destruction is handle by the class with smart pointers.
+ */
+ Skeleton_blocker_contractor(GeometricSimplifiableComplex& complex,
+ Cost_policy_ *cost_policy,
+ Placement_policy_ * placement_policy = new First_vertex_placement<Profile>,
+ Valid_contraction_policy_ * valid_contraction_policy =
+ new Link_condition_valid_contraction<Profile>,
+ Contraction_visitor_* contraction_visitor = new Contraction_visitor_(),
+ Edge_profile_factory_* edge_profile_factory = NULL) :
+ complex_(complex),
+ cost_policy_(cost_policy),
+ placement_policy_(placement_policy),
+ valid_contraction_policy_(valid_contraction_policy),
+ contraction_visitor_(contraction_visitor),
+ edge_profile_factory_(edge_profile_factory),
+ initial_num_edges_heap_(0),
+ current_num_edges_heap_(0) {
+ complex_.set_visitor(this);
+ if (contraction_visitor) contraction_visitor->on_started(complex);
+ collect_edges();
+ }
+
+ ~Skeleton_blocker_contractor() {
+ complex_.set_visitor(0);
+ }
+
+ private:
+ void contract_edge(const Profile& profile, Placement_type placement) {
+ if (contraction_visitor_) contraction_visitor_->on_contracting(profile, placement);
+
+ assert(complex_.contains_vertex(profile.v0_handle()));
+ assert(complex_.contains_vertex(profile.v1_handle()));
+ assert(placement);
+
+ profile.complex().point(profile.v0_handle()) = *placement;
+
+ // remark : this is not necessary since v1 will be deactivated
+ // profile.complex().point(profile.v1_handle()) = *placement;
+
+ complex_.contract_edge(profile.v0_handle(), profile.v1_handle());
+
+ assert(complex_.contains_vertex(profile.v0_handle()));
+ assert(!complex_.contains_vertex(profile.v1_handle()));
+
+ update_changed_edges();
+
+ // the visitor could do something as complex_.remove_popable_blockers();
+ if (contraction_visitor_) contraction_visitor_->on_contracted(profile, placement);
+ }
+
+ private:
+ // every time the visitor's method on_changed_edge is called, it adds an
+ // edge to changed_edges_
+ std::vector< Edge_handle > changed_edges_;
+
+ /**
+ * @brief we update the cost and the position in the heap of an edge that has
+ * been changed
+ */
+ inline void on_changed_edge(Vertex_handle a, Vertex_handle b) override {
+ boost::optional<Edge_handle> ab(complex_[std::make_pair(a, b)]);
+ assert(ab);
+ changed_edges_.push_back(*ab);
+ }
+
+ void update_changed_edges() {
+ // xxx do a parralel for
+ DBG("update edges");
+
+ // sequential loop
+ for (auto ab : changed_edges_) {
+ // 1-get the Edge_handle corresponding to ab
+ // 2-change the data in mEdgeArray[ab.id()]
+ // 3-update the heap
+ Edge_data& data = get_data(ab);
+ Profile const& profile = create_profile(ab);
+ data.cost() = get_cost(profile);
+ if (data.is_in_PQ()) {
+ update_in_PQ(ab, data);
+ } else {
+ insert_in_PQ(ab, data);
+ }
+ }
+ changed_edges_.clear();
+ }
+
+
+ private:
+ void on_remove_edge(Vertex_handle a, Vertex_handle b) override {
+ boost::optional<Edge_handle> ab((complex_[std::make_pair(a, b)]));
+ assert(ab);
+ Edge_data& lData = get_data(*ab);
+ if (lData.is_in_PQ()) {
+ remove_from_PQ(*ab, lData);
+ }
+ }
+
+ private:
+ /**
+ * @brief Called when the edge 'ax' has been added while the edge 'bx'
+ * is still there but will be removed on next instruction.
+ * We assign the index of 'bx' to the edge index of 'ax'
+ */
+ void on_swaped_edge(Vertex_handle a, Vertex_handle b, Vertex_handle x) override {
+ boost::optional<Edge_handle> ax(complex_[std::make_pair(a, x)]);
+ boost::optional<Edge_handle> bx(complex_[std::make_pair(b, x)]);
+ assert(ax && bx);
+ complex_[*ax].index() = complex_[*bx].index();
+ }
+
+ private:
+ /**
+ * @brief Called when a blocker is removed.
+ * All the edges that passes through the blocker may be edge-contractible
+ * again and are thus reinserted in the heap.
+ */
+ void on_delete_blocker(const Simplex * blocker) override {
+ // we go for all pairs xy that belongs to the blocker
+ // note that such pairs xy are necessarily edges of the complex
+ // by definition of a blocker
+
+ // todo uniqument utile pour la link condition
+ // laisser a l'utilisateur ? booleen update_heap_on_removed_blocker?
+ Simplex blocker_copy(*blocker);
+ for (auto x = blocker_copy.begin(); x != blocker_copy.end(); ++x) {
+ for (auto y = x; ++y != blocker_copy.end();) {
+ auto edge_descr(complex_[std::make_pair(*x, *y)]);
+ assert(edge_descr);
+ Edge_data& data = get_data(*edge_descr);
+ Profile const& profile = create_profile(*edge_descr);
+ data.cost() = get_cost(profile);
+
+ // If the edge is already in the heap
+ // its priority has not changed.
+ // If the edge is not present, we reinsert it
+ // remark : we could also reinsert the edge
+ // only if it is valid
+ if (!data.is_in_PQ()) {
+ insert_in_PQ(*edge_descr, data);
+ }
+ }
+ }
+ }
+
+
+ private:
+ std::shared_ptr<Cost_policy_> cost_policy_;
+ std::shared_ptr<Placement_policy_> placement_policy_;
+ std::shared_ptr<Valid_contraction_policy_> valid_contraction_policy_;
+ std::shared_ptr<Contraction_visitor_> contraction_visitor_;
+
+ // in case the user wants to do something special when the edge profile
+ // are created (for instance add some info)
+ std::shared_ptr<Edge_profile_factory_> edge_profile_factory_;
+ Edge_data_array edge_data_array_;
+
+ boost::scoped_ptr<PQ> heap_PQ_;
+ int initial_num_edges_heap_;
+ int current_num_edges_heap_;
+};
+
+} // namespace contraction
+
+} // namespace Gudhi
+
+#endif // SKELETON_BLOCKER_CONTRACTOR_H_
diff --git a/include/gudhi/Skeleton_blocker_geometric_complex.h b/include/gudhi/Skeleton_blocker_geometric_complex.h
new file mode 100644
index 00000000..1130ca9f
--- /dev/null
+++ b/include/gudhi/Skeleton_blocker_geometric_complex.h
@@ -0,0 +1,226 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef SKELETON_BLOCKER_GEOMETRIC_COMPLEX_H_
+#define SKELETON_BLOCKER_GEOMETRIC_COMPLEX_H_
+
+#include <gudhi/Skeleton_blocker_complex.h>
+#include <gudhi/Skeleton_blocker/Skeleton_blocker_sub_complex.h>
+#include <gudhi/Debug_utils.h>
+
+namespace Gudhi {
+
+namespace skeleton_blocker {
+
+/**
+ * @brief Class that represents a geometric complex that can be simplified.
+ * The class allows access to points of vertices.
+ * @ingroup skbl
+ */
+template<typename SkeletonBlockerGeometricDS>
+class Skeleton_blocker_geometric_complex :
+public Skeleton_blocker_complex<SkeletonBlockerGeometricDS> {
+ public:
+ typedef typename SkeletonBlockerGeometricDS::GT GT;
+
+ typedef Skeleton_blocker_complex<SkeletonBlockerGeometricDS> SimplifiableSkeletonblocker;
+
+ typedef typename SimplifiableSkeletonblocker::Vertex_handle Vertex_handle;
+ typedef typename SimplifiableSkeletonblocker::Root_vertex_handle Root_vertex_handle;
+ typedef typename SimplifiableSkeletonblocker::Edge_handle Edge_handle;
+ typedef typename SimplifiableSkeletonblocker::Simplex Simplex;
+
+ typedef typename SimplifiableSkeletonblocker::Graph_vertex Graph_vertex;
+
+ typedef typename SkeletonBlockerGeometricDS::Point Point;
+
+ Skeleton_blocker_geometric_complex() { }
+
+ /**
+ * constructor given a list of points
+ */
+ template<typename PointIterator>
+ explicit Skeleton_blocker_geometric_complex(int num_vertices, PointIterator begin, PointIterator end) {
+ for (auto point = begin; point != end; ++point)
+ add_vertex(*point);
+ }
+
+ /**
+ * @brief Constructor with a list of simplices.
+ * @details is_flag_complex indicates if the complex is a flag complex or not (to know if blockers have to be
+ * computed or not).
+ */
+ template<typename SimpleHandleOutputIterator, typename PointIterator>
+ Skeleton_blocker_geometric_complex(
+ SimpleHandleOutputIterator simplex_begin, SimpleHandleOutputIterator simplex_end,
+ PointIterator points_begin, PointIterator points_end,
+ bool is_flag_complex = false)
+ : Skeleton_blocker_complex<SkeletonBlockerGeometricDS>(simplex_begin, simplex_end, is_flag_complex) {
+ unsigned current = 0;
+ for (auto point = points_begin; point != points_end; ++point)
+ (*this)[Vertex_handle(current++)].point() = Point(point->begin(), point->end());
+ }
+
+ /**
+ * @brief Constructor with a list of simplices.
+ * Points of every vertex are the point constructed with default constructor.
+ * @details is_flag_complex indicates if the complex is a flag complex or not (to know if blockers have to be computed or not).
+ */
+ template<typename SimpleHandleOutputIterator>
+ Skeleton_blocker_geometric_complex(
+ SimpleHandleOutputIterator simplex_begin, SimpleHandleOutputIterator simplex_end,
+ bool is_flag_complex = false)
+ : Skeleton_blocker_complex<SkeletonBlockerGeometricDS>(simplex_begin, simplex_end, is_flag_complex) { }
+
+ /**
+ * @brief Add a vertex to the complex with a default constructed associated point.
+ */
+ Vertex_handle add_vertex() {
+ return SimplifiableSkeletonblocker::add_vertex();
+ }
+
+ /**
+ * @brief Add a vertex to the complex with its associated point.
+ */
+ Vertex_handle add_vertex(const Point& point) {
+ Vertex_handle ad = SimplifiableSkeletonblocker::add_vertex();
+ (*this)[ad].point() = point;
+ return ad;
+ }
+
+ /**
+ * @brief Returns the Point associated to the vertex v.
+ */
+ const Point& point(Vertex_handle v) const {
+ assert(this->contains_vertex(v));
+ return (*this)[v].point();
+ }
+
+ /**
+ * @brief Returns the Point associated to the vertex v.
+ */
+ Point& point(Vertex_handle v) {
+ assert(this->contains_vertex(v));
+ return (*this)[v].point();
+ }
+
+ const Point& point(Root_vertex_handle global_v) const {
+ Vertex_handle local_v((*this)[global_v]);
+ assert(this->contains_vertex(local_v));
+ return (*this)[local_v].point();
+ }
+
+ Point& point(Root_vertex_handle global_v) {
+ Vertex_handle local_v((*this)[global_v]);
+ assert(this->contains_vertex(local_v));
+ return (*this)[local_v].point();
+ }
+
+ typedef Skeleton_blocker_link_complex<Skeleton_blocker_geometric_complex> Geometric_link;
+
+ /**
+ * Constructs the link of 'simplex' with points coordinates.
+ */
+ Geometric_link link(Vertex_handle v) const {
+ Geometric_link link(*this, Simplex(v));
+ // we now add the point info
+ add_points_to_link(link);
+ return link;
+ }
+
+ /**
+ * Constructs the link of 'simplex' with points coordinates.
+ */
+ Geometric_link link(const Simplex& simplex) const {
+ Geometric_link link(*this, simplex);
+ // we now add the point info
+ add_points_to_link(link);
+ return link;
+ }
+
+ /**
+ * Constructs the link of 'simplex' with points coordinates.
+ */
+ Geometric_link link(Edge_handle edge) const {
+ Geometric_link link(*this, edge);
+ // we now add the point info
+ add_points_to_link(link);
+ return link;
+ }
+
+ typedef Skeleton_blocker_link_complex<Skeleton_blocker_complex<SkeletonBlockerGeometricDS>> Abstract_link;
+
+ /**
+ * Constructs the abstract link of v (without points coordinates).
+ */
+ Abstract_link abstract_link(Vertex_handle v) const {
+ return Abstract_link(*this, Simplex(v));
+ }
+
+ /**
+ * Constructs the link of 'simplex' with points coordinates.
+ */
+ Abstract_link abstract_link(const Simplex& simplex) const {
+ return Abstract_link(*this, simplex);
+ }
+
+ /**
+ * Constructs the link of 'simplex' with points coordinates.
+ */
+ Abstract_link abstract_link(Edge_handle edge) const {
+ return Abstract_link(*this, edge);
+ }
+
+ private:
+ void add_points_to_link(Geometric_link& link) const {
+ for (Vertex_handle v : link.vertex_range()) {
+ Root_vertex_handle v_root(link.get_id(v));
+ link.point(v) = (*this).point(v_root);
+ }
+ }
+};
+
+
+template<typename SkeletonBlockerGeometricComplex, typename SimpleHandleOutputIterator, typename PointIterator>
+SkeletonBlockerGeometricComplex make_complex_from_top_faces(
+ SimpleHandleOutputIterator simplex_begin,
+ SimpleHandleOutputIterator simplex_end,
+ PointIterator points_begin,
+ PointIterator points_end,
+ bool is_flag_complex = false) {
+ typedef SkeletonBlockerGeometricComplex SBGC;
+ SkeletonBlockerGeometricComplex complex;
+ unsigned current = 0;
+ complex =
+ make_complex_from_top_faces<SBGC>(simplex_begin, simplex_end, is_flag_complex);
+ for (auto point = points_begin; point != points_end; ++point)
+ // complex.point(Vertex_handle(current++)) = Point(point->begin(),point->end());
+ complex.point(typename SBGC::Vertex_handle(current++)) = typename SBGC::Point(*point);
+ return complex;
+}
+
+} // namespace skeleton_blocker
+
+namespace skbl = skeleton_blocker;
+
+} // namespace Gudhi
+
+#endif // SKELETON_BLOCKER_GEOMETRIC_COMPLEX_H_
diff --git a/include/gudhi/Skeleton_blocker_link_complex.h b/include/gudhi/Skeleton_blocker_link_complex.h
new file mode 100644
index 00000000..1bd66289
--- /dev/null
+++ b/include/gudhi/Skeleton_blocker_link_complex.h
@@ -0,0 +1,299 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef SKELETON_BLOCKER_LINK_COMPLEX_H_
+#define SKELETON_BLOCKER_LINK_COMPLEX_H_
+
+#include <gudhi/Skeleton_blocker_complex.h>
+#include <gudhi/Debug_utils.h>
+
+namespace Gudhi {
+
+namespace skeleton_blocker {
+
+template<class ComplexType> class Skeleton_blocker_sub_complex;
+
+/**
+ * \brief Class representing the link of a simplicial complex encoded by a skeleton/blockers pair.
+ * It inherits from Skeleton_blocker_sub_complex because such complex is a sub complex of a
+ * root complex.
+ * \ingroup skbl
+ */
+template<typename ComplexType>
+class Skeleton_blocker_link_complex : public Skeleton_blocker_sub_complex<
+ ComplexType> {
+ template<typename T> friend class Skeleton_blocker_link_superior;
+ typedef typename ComplexType::Edge_handle Edge_handle;
+
+ typedef typename ComplexType::boost_vertex_handle boost_vertex_handle;
+
+ private:
+ bool only_superior_vertices_;
+
+ public:
+ typedef typename ComplexType::Vertex_handle Vertex_handle;
+ typedef typename ComplexType::Root_vertex_handle Root_vertex_handle;
+
+ typedef typename ComplexType::Simplex Simplex;
+ typedef typename ComplexType::Root_simplex_handle Root_simplex_handle;
+
+ typedef typename ComplexType::Blocker_handle Blocker_handle;
+
+ typedef typename ComplexType::Root_simplex_handle::Simplex_vertex_const_iterator Root_simplex_handle_iterator;
+
+ explicit Skeleton_blocker_link_complex(bool only_superior_vertices = false)
+ : only_superior_vertices_(only_superior_vertices) {
+ }
+
+ /**
+ * If the parameter only_superior_vertices is true,
+ * only vertices greater than the one of alpha are added.
+ * Only vertices are computed if only_vertices is true.
+ */
+ Skeleton_blocker_link_complex(const ComplexType & parent_complex,
+ const Simplex& alpha_parent_adress,
+ bool only_superior_vertices = false,
+ bool only_vertices = false)
+ : only_superior_vertices_(only_superior_vertices) {
+ if (!alpha_parent_adress.empty())
+ build_link(parent_complex, alpha_parent_adress, only_vertices);
+ }
+
+ /**
+ * If the parameter only_superior_vertices is true,
+ * only vertices greater than the one of the vertex are added.
+ */
+ Skeleton_blocker_link_complex(const ComplexType & parent_complex,
+ Vertex_handle a_parent_adress,
+ bool only_superior_vertices = false)
+ : only_superior_vertices_(only_superior_vertices) {
+ Simplex alpha_simplex(a_parent_adress);
+ build_link(parent_complex, alpha_simplex);
+ }
+
+ /**
+ * If the parameter only_superior_vertices is true,
+ * only vertices greater than the one of the edge are added.
+ */
+ Skeleton_blocker_link_complex(const ComplexType & parent_complex,
+ Edge_handle edge, bool only_superior_vertices =
+ false)
+ : only_superior_vertices_(only_superior_vertices) {
+ Simplex alpha_simplex(parent_complex.first_vertex(edge),
+ parent_complex.second_vertex(edge));
+ build_link(parent_complex, alpha_simplex);
+ }
+
+ protected:
+ /**
+ * @brief compute vertices of the link.
+ * If the boolean only_superior_vertices is true, then only the vertices
+ * are greater than vertices of alpha_parent_adress are added.
+ */
+ void compute_link_vertices(const ComplexType & parent_complex,
+ const Simplex& alpha_parent_adress,
+ bool only_superior_vertices,
+ bool is_alpha_blocker = false) {
+ if (alpha_parent_adress.dimension() == 0) {
+ // for a vertex we know exactly the number of vertices of the link (and the size of the corresponding vector)
+ // thus we call a specific function that will reserve a vector with appropriate size
+ this->compute_link_vertices(parent_complex,
+ alpha_parent_adress.first_vertex(),
+ only_superior_vertices_);
+ } else {
+ // we compute the intersection of neighbors of alpha and store it in link_vertices
+ Simplex link_vertices_parent;
+ parent_complex.add_neighbours(alpha_parent_adress, link_vertices_parent,
+ only_superior_vertices);
+ // For all vertex 'v' in this intersection, we go through all its adjacent blockers.
+ // If one blocker minus 'v' is included in alpha then the vertex is not in the link complex.
+ for (auto v_parent : link_vertices_parent) {
+ bool new_vertex = true;
+ for (auto beta : parent_complex.const_blocker_range(v_parent)) {
+ if (!is_alpha_blocker || *beta != alpha_parent_adress) {
+ new_vertex = !(alpha_parent_adress.contains_difference(*beta,
+ v_parent));
+ if (!new_vertex)
+ break;
+ }
+ }
+ if (new_vertex)
+ this->add_vertex(parent_complex.get_id(v_parent));
+ }
+ }
+ }
+
+ /**
+ * @brief compute vertices of the link.
+ * If the boolean only_superior_vertices is true, then only the vertices
+ * are greater than vertices of alpha_parent_adress are added.
+ */
+ void compute_link_vertices(const ComplexType & parent_complex,
+ Vertex_handle alpha_parent_adress,
+ bool only_superior_vertices) {
+ // for a vertex we know exactly the number of vertices of the link (and the size of the corresponding vector
+ this->skeleton.m_vertices.reserve(
+ parent_complex.degree(alpha_parent_adress));
+
+ // For all vertex 'v' in this intersection, we go through all its adjacent blockers.
+ // If one blocker minus 'v' is included in alpha then the vertex is not in the link complex.
+ for (auto v_parent : parent_complex.vertex_range(alpha_parent_adress)) {
+ if (!only_superior_vertices
+ || v_parent.vertex > alpha_parent_adress.vertex)
+ this->add_vertex(parent_complex.get_id(v_parent));
+ }
+ }
+
+ void compute_link_edges(const ComplexType & parent_complex,
+ const Simplex& alpha_parent_adress,
+ bool is_alpha_blocker = false) {
+ if (this->num_vertices() <= 1)
+ return;
+
+ for (auto x_link = this->vertex_range().begin();
+ x_link != this->vertex_range().end(); ++x_link) {
+ for (auto y_link = x_link; ++y_link != this->vertex_range().end();) {
+ Vertex_handle x_parent = *parent_complex.get_address(
+ this->get_id(*x_link));
+ Vertex_handle y_parent = *parent_complex.get_address(
+ this->get_id(*y_link));
+ if (parent_complex.contains_edge(x_parent, y_parent)) {
+ // we check that there is no blocker subset of alpha passing trough x and y
+ bool new_edge = true;
+ for (auto blocker_parent : parent_complex.const_blocker_range(
+ x_parent)) {
+ if (!is_alpha_blocker || *blocker_parent != alpha_parent_adress) {
+ if (blocker_parent->contains(y_parent)) {
+ new_edge = !(alpha_parent_adress.contains_difference(
+ *blocker_parent, x_parent, y_parent));
+ if (!new_edge)
+ break;
+ }
+ }
+ }
+ if (new_edge)
+ this->add_edge_without_blockers(*x_link, *y_link);
+ }
+ }
+ }
+ }
+
+ /**
+ * @brief : Given an address in the current complex, it returns the
+ * corresponding address in 'other_complex'.
+ * It assumes that other_complex have a vertex 'this.get_id(address)'
+ */
+ boost::optional<Vertex_handle> give_equivalent_vertex(
+ const ComplexType & other_complex, Vertex_handle address) const {
+ Root_vertex_handle id((*this)[address].get_id());
+ return other_complex.get_address(id);
+ }
+
+ /*
+ * compute the blockers of the link if is_alpha_blocker is false.
+ * Otherwise, alpha is a blocker, and the link is computed in the complex where
+ * the blocker is anticollapsed.
+ */
+ void compute_link_blockers(const ComplexType & parent_complex,
+ const Simplex& alpha_parent,
+ bool is_alpha_blocker = false) {
+ for (auto x_link : this->vertex_range()) {
+ Vertex_handle x_parent = *this->give_equivalent_vertex(parent_complex,
+ x_link);
+
+ for (auto blocker_parent : parent_complex.const_blocker_range(x_parent)) {
+ if (!is_alpha_blocker || *blocker_parent != alpha_parent) {
+ Simplex sigma_parent(*blocker_parent);
+
+ sigma_parent.difference(alpha_parent);
+
+ if (sigma_parent.dimension() >= 2
+ && sigma_parent.first_vertex() == x_parent) {
+ Root_simplex_handle sigma_id(parent_complex.get_id(sigma_parent));
+ auto sigma_link = this->get_simplex_address(sigma_id);
+ // ie if the vertices of sigma are vertices of the link
+ if (sigma_link) {
+ bool is_new_blocker = true;
+ for (auto a : alpha_parent) {
+ for (auto eta_parent : parent_complex.const_blocker_range(a)) {
+ if (!is_alpha_blocker || *eta_parent != alpha_parent) {
+ Simplex eta_minus_alpha(*eta_parent);
+ eta_minus_alpha.difference(alpha_parent);
+ if (eta_minus_alpha != sigma_parent
+ && sigma_parent.contains_difference(*eta_parent,
+ alpha_parent)) {
+ is_new_blocker = false;
+ break;
+ }
+ }
+ }
+ if (!is_new_blocker)
+ break;
+ }
+ if (is_new_blocker)
+ this->add_blocker(new Simplex(*sigma_link));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public:
+ /**
+ * @brief compute vertices, edges and blockers of the link.
+ * @details If the boolean only_superior_vertices is true, then the link is computed only
+ * with vertices that are greater than vertices of alpha_parent_adress.
+ */
+ void build_link(const ComplexType & parent_complex,
+ const Simplex& alpha_parent_adress,
+ bool is_alpha_blocker = false,
+ bool only_vertices = false) {
+ assert(is_alpha_blocker || parent_complex.contains(alpha_parent_adress));
+ compute_link_vertices(parent_complex, alpha_parent_adress, only_superior_vertices_);
+ if(!only_vertices) {
+ compute_link_edges(parent_complex, alpha_parent_adress, is_alpha_blocker);
+ compute_link_blockers(parent_complex, alpha_parent_adress, is_alpha_blocker);
+ }
+ }
+
+ /**
+ * @brief build the link of a blocker which is the link
+ * of the blocker's simplex if this simplex had been
+ * removed from the blockers of the complex.
+ */
+ friend void build_link_of_blocker(const ComplexType & parent_complex,
+ Simplex& blocker,
+ Skeleton_blocker_link_complex & result) {
+ assert(blocker.dimension() >= 2);
+ assert(parent_complex.contains_blocker(blocker));
+ result.clear();
+ result.build_link(parent_complex, blocker, true);
+ }
+};
+
+} // namespace skeleton_blocker
+
+namespace skbl = skeleton_blocker;
+
+} // namespace Gudhi
+
+#endif // SKELETON_BLOCKER_LINK_COMPLEX_H_
diff --git a/include/gudhi/Skeleton_blocker_simplifiable_complex.h b/include/gudhi/Skeleton_blocker_simplifiable_complex.h
new file mode 100644
index 00000000..171efd4b
--- /dev/null
+++ b/include/gudhi/Skeleton_blocker_simplifiable_complex.h
@@ -0,0 +1,468 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef SKELETON_BLOCKER_SIMPLIFIABLE_COMPLEX_H_
+#define SKELETON_BLOCKER_SIMPLIFIABLE_COMPLEX_H_
+
+#include <gudhi/Skeleton_blocker/Skeleton_blocker_sub_complex.h>
+
+#include <list>
+#include <vector>
+#include <set>
+
+namespace Gudhi {
+
+namespace skeleton_blocker {
+
+/**
+ * Returns true iff the blocker 'sigma' is popable.
+ * To define popable, let us call 'L' the complex that
+ * consists in the current complex without the blocker 'sigma'.
+ * A blocker 'sigma' is then "popable" if the link of 'sigma'
+ * in L is reducible.
+ *
+ */
+template<typename SkeletonBlockerDS>
+bool Skeleton_blocker_complex<SkeletonBlockerDS>::is_popable_blocker(Blocker_handle sigma) const {
+ assert(this->contains_blocker(*sigma));
+ Skeleton_blocker_link_complex<Skeleton_blocker_complex> link_blocker_sigma;
+ build_link_of_blocker(*this, *sigma, link_blocker_sigma);
+ return link_blocker_sigma.is_contractible() == CONTRACTIBLE;
+}
+
+/**
+ * Removes all the popable blockers of the complex and delete them.
+ * @returns the number of popable blockers deleted
+ */
+template<typename SkeletonBlockerDS>
+void Skeleton_blocker_complex<SkeletonBlockerDS>::remove_popable_blockers() {
+ std::list<Vertex_handle> vertex_to_check;
+ for (auto v : this->vertex_range())
+ vertex_to_check.push_front(v);
+
+ while (!vertex_to_check.empty()) {
+ Vertex_handle v = vertex_to_check.front();
+ vertex_to_check.pop_front();
+
+ bool blocker_popable_found = true;
+ while (blocker_popable_found) {
+ blocker_popable_found = false;
+ for (auto block : this->blocker_range(v)) {
+ if (this->is_popable_blocker(block)) {
+ for (Vertex_handle nv : *block)
+ if (nv != v) vertex_to_check.push_back(nv);
+ this->delete_blocker(block);
+ blocker_popable_found = true;
+ break;
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Removes all the popable blockers of the complex passing through v and delete them.
+ */
+template<typename SkeletonBlockerDS>
+void Skeleton_blocker_complex<SkeletonBlockerDS>::remove_popable_blockers(Vertex_handle v) {
+ bool blocker_popable_found = true;
+ while (blocker_popable_found) {
+ blocker_popable_found = false;
+ for (auto block : this->blocker_range(v)) {
+ if (is_popable_blocker(block)) {
+ this->delete_blocker(block);
+ blocker_popable_found = true;
+ }
+ }
+ }
+}
+
+/**
+ * @brief Removes all the popable blockers of the complex passing through v and delete them.
+ * Also remove popable blockers in the neighborhood if they became popable.
+ *
+ */
+template<typename SkeletonBlockerDS>
+void Skeleton_blocker_complex<SkeletonBlockerDS>::remove_all_popable_blockers(Vertex_handle v) {
+ std::list<Vertex_handle> vertex_to_check;
+ vertex_to_check.push_front(v);
+
+ while (!vertex_to_check.empty()) {
+ Vertex_handle v = vertex_to_check.front();
+ vertex_to_check.pop_front();
+
+ bool blocker_popable_found = true;
+ while (blocker_popable_found) {
+ blocker_popable_found = false;
+ for (auto block : this->blocker_range(v)) {
+ if (this->is_popable_blocker(block)) {
+ for (Vertex_handle nv : *block)
+ if (nv != v) vertex_to_check.push_back(nv);
+ this->delete_blocker(block);
+ blocker_popable_found = true;
+ break;
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Remove the star of the vertice 'v'
+ */
+template<typename SkeletonBlockerDS>
+void Skeleton_blocker_complex<SkeletonBlockerDS>::remove_star(Vertex_handle v) {
+ // we remove the blockers that are not consistent anymore
+ update_blockers_after_remove_star_of_vertex_or_edge(Simplex(v));
+ while (this->degree(v) > 0) {
+ Vertex_handle w(* (adjacent_vertices(v.vertex, this->skeleton).first));
+ this->remove_edge(v, w);
+ }
+ this->remove_vertex(v);
+}
+
+/**
+ * after removing the star of a simplex, blockers sigma that contains this simplex must be removed.
+ * Furthermore, all simplices tau of the form sigma \setminus simplex_to_be_removed must be added
+ * whenever the dimension of tau is at least 2.
+ */
+template<typename SkeletonBlockerDS>
+void Skeleton_blocker_complex<SkeletonBlockerDS>::update_blockers_after_remove_star_of_vertex_or_edge(
+ const Simplex& simplex_to_be_removed) {
+ std::list <Blocker_handle> blockers_to_update;
+ if (simplex_to_be_removed.empty()) return;
+
+ auto v0 = simplex_to_be_removed.first_vertex();
+ for (auto blocker : this->blocker_range(v0)) {
+ if (blocker->contains(simplex_to_be_removed))
+ blockers_to_update.push_back(blocker);
+ }
+
+ for (auto blocker_to_update : blockers_to_update) {
+ Simplex sub_blocker_to_be_added;
+ bool sub_blocker_need_to_be_added =
+ (blocker_to_update->dimension() - simplex_to_be_removed.dimension()) >= 2;
+ if (sub_blocker_need_to_be_added) {
+ sub_blocker_to_be_added = *blocker_to_update;
+ sub_blocker_to_be_added.difference(simplex_to_be_removed);
+ }
+ this->delete_blocker(blocker_to_update);
+ if (sub_blocker_need_to_be_added)
+ this->add_blocker(sub_blocker_to_be_added);
+ }
+}
+
+/**
+ * Remove the star of the edge connecting vertices a and b.
+ * @returns the number of blocker that have been removed
+ */
+template<typename SkeletonBlockerDS>
+void Skeleton_blocker_complex<SkeletonBlockerDS>::remove_star(Vertex_handle a, Vertex_handle b) {
+ update_blockers_after_remove_star_of_vertex_or_edge(Simplex(a, b));
+ // we remove the edge
+ this->remove_edge(a, b);
+}
+
+/**
+ * Remove the star of the edge 'e'.
+ */
+template<typename SkeletonBlockerDS>
+void Skeleton_blocker_complex<SkeletonBlockerDS>::remove_star(Edge_handle e) {
+ return remove_star(this->first_vertex(e), this->second_vertex(e));
+}
+
+/**
+ * Remove the star of the simplex 'sigma' which needs to belong to the complex
+ */
+template<typename SkeletonBlockerDS>
+void Skeleton_blocker_complex<SkeletonBlockerDS>::remove_star(const Simplex& sigma) {
+ assert(this->contains(sigma));
+ if (sigma.dimension() == 0) {
+ remove_star(sigma.first_vertex());
+ } else if (sigma.dimension() == 1) {
+ remove_star(sigma.first_vertex(), sigma.last_vertex());
+ } else {
+ remove_blocker_containing_simplex(sigma);
+ this->add_blocker(sigma);
+ }
+}
+
+template<typename SkeletonBlockerDS>
+void Skeleton_blocker_complex<SkeletonBlockerDS>::add_simplex(const Simplex& sigma) {
+ // to add a simplex s, all blockers included in s are first removed
+ // and then all simplex in the coboundary of s are added as blockers
+ assert(!this->contains(sigma));
+ assert(sigma.dimension() > 1);
+ if (!contains_vertices(sigma)) {
+ std::cerr << "add_simplex: Some vertices were not present in the complex, adding them" << std::endl;
+ size_t num_vertices_to_add = sigma.last_vertex() - this->num_vertices() + 1;
+ for (size_t i = 0; i < num_vertices_to_add; ++i)
+ this->add_vertex();
+ }
+ assert(contains_vertices(sigma));
+ if (!contains_edges(sigma))
+ add_edge(sigma);
+ remove_blocker_include_in_simplex(sigma);
+ add_blockers_after_simplex_insertion(sigma);
+}
+
+
+
+template<typename SkeletonBlockerDS>
+void Skeleton_blocker_complex<SkeletonBlockerDS>::add_blockers_after_simplex_insertion(Simplex sigma) {
+ if (sigma.dimension() < 1) return;
+
+ for (auto s : coboundary_range(sigma)) {
+ this->add_blocker(s);
+ }
+}
+
+/**
+ * remove all blockers that contains sigma
+ */
+template<typename SkeletonBlockerDS>
+void Skeleton_blocker_complex<SkeletonBlockerDS>::remove_blocker_containing_simplex(const Simplex& sigma) {
+ std::vector <Blocker_handle> blockers_to_remove;
+ for (auto blocker : this->blocker_range(sigma.first_vertex())) {
+ if (blocker->contains(sigma))
+ blockers_to_remove.push_back(blocker);
+ }
+ for (auto blocker_to_update : blockers_to_remove)
+ this->delete_blocker(blocker_to_update);
+}
+
+/**
+ * remove all blockers that contains sigma
+ */
+template<typename SkeletonBlockerDS>
+void Skeleton_blocker_complex<SkeletonBlockerDS>::remove_blocker_include_in_simplex(const Simplex& sigma) {
+ // TODO(DS): write efficiently by using only superior blockers
+ // eg for all s, check blockers whose vertices are all greater than s
+ std::set <Blocker_handle> blockers_to_remove;
+ for (auto s : sigma) {
+ for (auto blocker : this->blocker_range(s)) {
+ if (sigma.contains(*blocker))
+ blockers_to_remove.insert(blocker);
+ }
+ }
+ for (auto blocker_to_update : blockers_to_remove) {
+ auto s = *blocker_to_update;
+ this->delete_blocker(blocker_to_update);
+ // now if there is a vertex v in the link of s
+ // and v is not included in sigma then v.s is a blocker
+ // (all faces of v.s are there since v belongs to the link of s)
+ for (const auto& b : coboundary_range(s))
+ if (!sigma.contains(b))
+ this->add_blocker(b);
+ }
+}
+
+/**
+ * Compute simplices beta such that a.beta is an order 0 blocker
+ * that may be used to construct a new blocker after contracting ab.
+ * It requires that the link condition is satisfied.
+ */
+template<typename SkeletonBlockerDS>
+void Skeleton_blocker_complex<SkeletonBlockerDS>::tip_blockers(Vertex_handle a, Vertex_handle b,
+ std::vector<Simplex> & buffer) const {
+ for (auto const & blocker : this->const_blocker_range(a)) {
+ Simplex beta = (*blocker);
+ beta.remove_vertex(a);
+ buffer.push_back(beta);
+ }
+
+ Simplex n;
+ this->add_neighbours(b, n);
+ this->remove_neighbours(a, n);
+ n.remove_vertex(a);
+
+
+ for (Vertex_handle y : n) {
+ Simplex beta;
+ beta.add_vertex(y);
+ buffer.push_back(beta);
+ }
+}
+
+/**
+ * @brief "Replace" the edge 'bx' by the edge 'ax'.
+ * Assume that the edge 'bx' was present whereas 'ax' was not.
+ * Precisely, it does not replace edges, but remove 'bx' and then add 'ax'.
+ * The visitor 'on_swaped_edge' is called just after edge 'ax' had been added
+ * and just before edge 'bx' had been removed. That way, it can
+ * eventually access to information of 'ax'.
+ */
+template<typename SkeletonBlockerDS>
+void
+Skeleton_blocker_complex<SkeletonBlockerDS>::swap_edge(Vertex_handle a, Vertex_handle b, Vertex_handle x) {
+ this->add_edge_without_blockers(a, x);
+ if (this->visitor) this->visitor->on_swaped_edge(a, b, x);
+ this->remove_edge(b, x);
+}
+
+template<typename SkeletonBlockerDS>
+void
+Skeleton_blocker_complex<SkeletonBlockerDS>::delete_blockers_around_vertex(Vertex_handle v) {
+ std::list <Blocker_handle> blockers_to_delete;
+ for (auto blocker : this->blocker_range(v)) {
+ blockers_to_delete.push_back(blocker);
+ }
+ while (!blockers_to_delete.empty()) {
+ this->remove_blocker(blockers_to_delete.back());
+ blockers_to_delete.pop_back();
+ }
+}
+
+/**
+ * @brief removes all blockers passing through the edge 'ab'
+ */
+template<typename SkeletonBlockerDS>
+void
+Skeleton_blocker_complex<SkeletonBlockerDS>::delete_blockers_around_edge(Vertex_handle a, Vertex_handle b) {
+ std::list<Blocker_handle> blocker_to_delete;
+ for (auto blocker : this->blocker_range(a))
+ if (blocker->contains(b)) blocker_to_delete.push_back(blocker);
+ while (!blocker_to_delete.empty()) {
+ this->delete_blocker(blocker_to_delete.back());
+ blocker_to_delete.pop_back();
+ }
+}
+
+/**
+ * Contracts the edge connecting vertices a and b.
+ * @remark If the link condition Link(ab) = Link(a) inter Link(b) is not satisfied,
+ * it removes first all blockers passing through 'ab'
+ */
+template<typename SkeletonBlockerDS>
+void
+Skeleton_blocker_complex<SkeletonBlockerDS>::contract_edge(Vertex_handle a, Vertex_handle b) {
+ assert(this->contains_vertex(a));
+ assert(this->contains_vertex(b));
+
+ if (this->contains_edge(a, b))
+ this->add_edge_without_blockers(a, b);
+
+ // if some blockers passes through 'ab', we need to remove them.
+ if (!link_condition(a, b))
+ delete_blockers_around_edge(a, b);
+
+ std::set<Simplex> blockers_to_add;
+
+ get_blockers_to_be_added_after_contraction(a, b, blockers_to_add);
+
+ delete_blockers_around_vertices(a, b);
+
+ update_edges_after_contraction(a, b);
+
+ this->remove_vertex(b);
+
+ notify_changed_edges(a);
+
+ for (auto block : blockers_to_add)
+ this->add_blocker(block);
+
+ assert(this->contains_vertex(a));
+ assert(!this->contains_vertex(b));
+}
+
+template<typename SkeletonBlockerDS>
+void Skeleton_blocker_complex<SkeletonBlockerDS>::get_blockers_to_be_added_after_contraction(Vertex_handle a,
+ Vertex_handle b, std::set<Simplex>& blockers_to_add) {
+ blockers_to_add.clear();
+
+ typedef Skeleton_blocker_link_complex<Skeleton_blocker_complex<SkeletonBlockerDS> > LinkComplexType;
+
+ LinkComplexType link_a(*this, a);
+ LinkComplexType link_b(*this, b);
+
+ std::vector<Simplex> vector_alpha, vector_beta;
+
+ tip_blockers(a, b, vector_alpha);
+ tip_blockers(b, a, vector_beta);
+
+ for (auto alpha = vector_alpha.begin(); alpha != vector_alpha.end(); ++alpha) {
+ for (auto beta = vector_beta.begin(); beta != vector_beta.end(); ++beta) {
+ Simplex sigma = *alpha;
+ sigma.union_vertices(*beta);
+ Root_simplex_handle sigma_id = this->get_id(sigma);
+ if (this->contains(sigma) &&
+ proper_faces_in_union<Skeleton_blocker_complex < SkeletonBlockerDS >> (sigma_id, link_a, link_b)) {
+ // Blocker_handle blocker = new Simplex(sigma);
+ sigma.add_vertex(a);
+ blockers_to_add.insert(sigma);
+ }
+ }
+ }
+}
+
+/**
+ * delete all blockers that passes through a or b
+ */
+template<typename SkeletonBlockerDS>
+void
+Skeleton_blocker_complex<SkeletonBlockerDS>::delete_blockers_around_vertices(Vertex_handle a, Vertex_handle b) {
+ std::vector<Blocker_handle> blocker_to_delete;
+ for (auto bl : this->blocker_range(a))
+ blocker_to_delete.push_back(bl);
+ for (auto bl : this->blocker_range(b))
+ blocker_to_delete.push_back(bl);
+ while (!blocker_to_delete.empty()) {
+ this->delete_blocker(blocker_to_delete.back());
+ blocker_to_delete.pop_back();
+ }
+}
+
+template<typename SkeletonBlockerDS>
+void
+Skeleton_blocker_complex<SkeletonBlockerDS>::update_edges_after_contraction(Vertex_handle a, Vertex_handle b) {
+ // We update the set of edges
+ this->remove_edge(a, b);
+
+ // For all edges {b,x} incident to b,
+ // we remove {b,x} and add {a,x} if not already there.
+ while (this->degree(b) > 0) {
+ Vertex_handle x(*(adjacent_vertices(b.vertex, this->skeleton).first));
+ if (!this->contains_edge(a, x))
+ // we 'replace' the edge 'bx' by the edge 'ax'
+ this->swap_edge(a, b, x);
+ else
+ this->remove_edge(b, x);
+ }
+}
+
+template<typename SkeletonBlockerDS>
+void
+Skeleton_blocker_complex<SkeletonBlockerDS>::notify_changed_edges(Vertex_handle a) {
+ // We notify the visitor that all edges incident to 'a' had changed
+ boost_adjacency_iterator v, v_end;
+
+ for (tie(v, v_end) = adjacent_vertices(a.vertex, this->skeleton); v != v_end; ++v)
+ if (this->visitor) this->visitor->on_changed_edge(a, Vertex_handle(*v));
+}
+
+
+} // namespace skeleton_blocker
+
+namespace skbl = skeleton_blocker;
+
+} // namespace Gudhi
+
+#endif // SKELETON_BLOCKER_SIMPLIFIABLE_COMPLEX_H_
diff --git a/include/gudhi/Test.h b/include/gudhi/Test.h
new file mode 100644
index 00000000..6024c822
--- /dev/null
+++ b/include/gudhi/Test.h
@@ -0,0 +1,105 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): David Salinas
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Mediterranee (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef TEST_H_
+#define TEST_H_
+
+#include <list>
+#include <string>
+#include <vector>
+#include <sstream>
+#include <iostream>
+
+
+#define TEST(a) std::cout << "TEST: " << (a) << std::endl
+#define TESTMSG(a, b) std::cout << "TEST: " << a << b << std::endl
+#define TESTVALUE(a) std::cout << "TEST: " << #a << ": " << a << std::endl
+
+/**
+ * Class to perform test
+ */
+
+class Test {
+ private:
+ std::string name;
+ bool (*test)();
+
+ std::string separation() const {
+ return "+++++++++++++++++++++++++++++++++++++++++++++++++\n";
+ }
+
+ std::string print_between_plus(std::string& s) const {
+ std::stringstream res;
+ res << "+++++++++++++++++" << s << "+++++++++++++++++\n";
+ return res.str();
+ }
+
+ public:
+ Test(std::string name_, bool (*test_)()) {
+ name = name_;
+ test = test_;
+ }
+
+ bool run() {
+ std::cout << print_between_plus(name);
+ return test();
+ }
+
+ std::string getName() {
+ return name;
+ }
+};
+
+class Tests {
+ private:
+ std::list<Test> tests;
+
+ public:
+ void add(std::string name_, bool (*test_)()) {
+ Test test(name_, test_);
+ tests.push_back(test);
+ }
+
+ bool run() {
+ bool tests_succesful(true);
+ std::vector<bool> res;
+ for (Test test : tests) {
+ res.push_back(test.run());
+ }
+ std::cout << "\n\n results of tests : " << std::endl;
+ int i = 0;
+ for (Test t : tests) {
+ std::cout << "Test " << i << " \"" << t.getName() << "\" --> ";
+ if (res[i++]) {
+ std::cout << "OK" << std::endl;
+ } else {
+ std::cout << "Fail" << std::endl;
+ tests_succesful = false;
+ break;
+ }
+ }
+ return tests_succesful;
+ }
+};
+
+#endif // TEST_H_
diff --git a/include/gudhi/Witness_complex.h b/include/gudhi/Witness_complex.h
new file mode 100644
index 00000000..489cdf11
--- /dev/null
+++ b/include/gudhi/Witness_complex.h
@@ -0,0 +1,265 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Siargey Kachanovich
+ *
+ * Copyright (C) 2015 INRIA Sophia Antipolis-Méditerranée (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef WITNESS_COMPLEX_H_
+#define WITNESS_COMPLEX_H_
+
+// Needed for the adjacency graph in bad link search
+#include <boost/graph/graph_traits.hpp>
+#include <boost/graph/adjacency_list.hpp>
+#include <boost/graph/connected_components.hpp>
+
+#include <boost/range/size.hpp>
+
+#include <gudhi/distance_functions.h>
+
+#include <algorithm>
+#include <utility>
+#include <vector>
+#include <list>
+#include <set>
+#include <queue>
+#include <limits>
+#include <ctime>
+#include <iostream>
+
+namespace Gudhi {
+
+namespace witness_complex {
+
+// /*
+// * \private
+// \class Witness_complex
+// \brief Constructs the witness complex for the given set of witnesses and landmarks.
+// \ingroup witness_complex
+// */
+template< class SimplicialComplex>
+class Witness_complex {
+ private:
+ struct Active_witness {
+ int witness_id;
+ int landmark_id;
+
+ Active_witness(int witness_id_, int landmark_id_)
+ : witness_id(witness_id_),
+ landmark_id(landmark_id_) { }
+ };
+
+ private:
+ typedef typename SimplicialComplex::Simplex_handle Simplex_handle;
+ typedef typename SimplicialComplex::Vertex_handle Vertex_handle;
+
+ typedef std::vector< double > Point_t;
+ typedef std::vector< Point_t > Point_Vector;
+
+ typedef std::vector< Vertex_handle > typeVectorVertex;
+ typedef std::pair< typeVectorVertex, Filtration_value> typeSimplex;
+ typedef std::pair< Simplex_handle, bool > typePairSimplexBool;
+
+ typedef int Witness_id;
+ typedef int Landmark_id;
+ typedef std::list< Vertex_handle > ActiveWitnessList;
+
+ private:
+ int nbL_; // Number of landmarks
+ SimplicialComplex& sc_; // Simplicial complex
+
+ public:
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /* @name Constructor
+ */
+
+ //@{
+
+ // Witness_range<Closest_landmark_range<Vertex_handle>>
+
+ /*
+ * \brief Iterative construction of the witness complex.
+ * \details The witness complex is written in sc_ basing on a matrix knn of
+ * nearest neighbours of the form {witnesses}x{landmarks}.
+ *
+ * The type KNearestNeighbors can be seen as
+ * Witness_range<Closest_landmark_range<Vertex_handle>>, where
+ * Witness_range and Closest_landmark_range are random access ranges.
+ *
+ * Constructor takes into account at most (dim+1)
+ * first landmarks from each landmark range to construct simplices.
+ *
+ * Landmarks are supposed to be in [0,nbL_-1]
+ */
+ template< typename KNearestNeighbors >
+ Witness_complex(KNearestNeighbors const & knn,
+ int nbL,
+ int dim,
+ SimplicialComplex & sc) : nbL_(nbL), sc_(sc) {
+ // Construction of the active witness list
+ int nbW = boost::size(knn);
+ typeVectorVertex vv;
+ int counter = 0;
+ /* The list of still useful witnesses
+ * it will diminuish in the course of iterations
+ */
+ ActiveWitnessList active_w; // = new ActiveWitnessList();
+ for (Vertex_handle i = 0; i != nbL_; ++i) {
+ // initial fill of 0-dimensional simplices
+ // by doing it we don't assume that landmarks are necessarily witnesses themselves anymore
+ counter++;
+ vv = {i};
+ sc_.insert_simplex(vv);
+ // TODO(SK) Error if not inserted : normally no need here though
+ }
+ int k = 1; /* current dimension in iterative construction */
+ for (int i = 0; i != nbW; ++i)
+ active_w.push_back(i);
+ while (!active_w.empty() && k < dim) {
+ typename ActiveWitnessList::iterator it = active_w.begin();
+ while (it != active_w.end()) {
+ typeVectorVertex simplex_vector;
+ /* THE INSERTION: Checking if all the subfaces are in the simplex tree*/
+ bool ok = all_faces_in(knn, *it, k);
+ if (ok) {
+ for (int i = 0; i != k + 1; ++i)
+ simplex_vector.push_back(knn[*it][i]);
+ sc_.insert_simplex(simplex_vector);
+ // TODO(SK) Error if not inserted : normally no need here though
+ ++it;
+ } else {
+ active_w.erase(it++); // First increase the iterator and then erase the previous element
+ }
+ }
+ k++;
+ }
+ }
+
+ //@}
+
+ private:
+ /* \brief Check if the facets of the k-dimensional simplex witnessed
+ * by witness witness_id are already in the complex.
+ * inserted_vertex is the handle of the (k+1)-th vertex witnessed by witness_id
+ */
+ template <typename KNearestNeighbors>
+ bool all_faces_in(KNearestNeighbors const &knn, int witness_id, int k) {
+ std::vector< Vertex_handle > facet;
+ // CHECK ALL THE FACETS
+ for (int i = 0; i != k + 1; ++i) {
+ facet = {};
+ for (int j = 0; j != k + 1; ++j) {
+ if (j != i) {
+ facet.push_back(knn[witness_id][j]);
+ }
+ } // endfor
+ if (sc_.find(facet) == sc_.null_simplex())
+ return false;
+ } // endfor
+ return true;
+ }
+
+ template <typename T>
+ static void print_vector(const std::vector<T>& v) {
+ std::cout << "[";
+ if (!v.empty()) {
+ std::cout << *(v.begin());
+ for (auto it = v.begin() + 1; it != v.end(); ++it) {
+ std::cout << ",";
+ std::cout << *it;
+ }
+ }
+ std::cout << "]";
+ }
+
+ public:
+ // /*
+ // * \brief Verification if every simplex in the complex is witnessed by witnesses in knn.
+ // * \param print_output =true will print the witnesses for each simplex
+ // * \remark Added for debugging purposes.
+ // */
+ template< class KNearestNeighbors >
+ bool is_witness_complex(KNearestNeighbors const & knn, bool print_output) {
+ for (Simplex_handle sh : sc_.complex_simplex_range()) {
+ bool is_witnessed = false;
+ typeVectorVertex simplex;
+ int nbV = 0; // number of verticed in the simplex
+ for (Vertex_handle v : sc_.simplex_vertex_range(sh))
+ simplex.push_back(v);
+ nbV = simplex.size();
+ for (typeVectorVertex w : knn) {
+ bool has_vertices = true;
+ for (Vertex_handle v : simplex)
+ if (std::find(w.begin(), w.begin() + nbV, v) == w.begin() + nbV) {
+ has_vertices = false;
+ }
+ if (has_vertices) {
+ is_witnessed = true;
+ if (print_output) {
+ std::cout << "The simplex ";
+ print_vector(simplex);
+ std::cout << " is witnessed by the witness ";
+ print_vector(w);
+ std::cout << std::endl;
+ }
+ break;
+ }
+ }
+ if (!is_witnessed) {
+ if (print_output) {
+ std::cout << "The following simplex is not witnessed ";
+ print_vector(simplex);
+ std::cout << std::endl;
+ }
+ assert(is_witnessed);
+ return false;
+ }
+ }
+ return true;
+ }
+};
+
+ /**
+ * \ingroup witness_complex
+ * \brief Iterative construction of the witness complex.
+ * \details The witness complex is written in simplicial complex sc_
+ * basing on a matrix knn of
+ * nearest neighbours of the form {witnesses}x{landmarks}.
+ *
+ * The type KNearestNeighbors can be seen as
+ * Witness_range<Closest_landmark_range<Vertex_handle>>, where
+ * Witness_range and Closest_landmark_range are random access ranges.
+ *
+ * Procedure takes into account at most (dim+1)
+ * first landmarks from each landmark range to construct simplices.
+ *
+ * Landmarks are supposed to be in [0,nbL_-1]
+ */
+ template <class KNearestNeighbors, class SimplicialComplexForWitness>
+ void witness_complex(KNearestNeighbors const & knn,
+ int nbL,
+ int dim,
+ SimplicialComplexForWitness & sc) {
+ Witness_complex<SimplicialComplexForWitness>(knn, nbL, dim, sc);
+ }
+
+} // namespace witness_complex
+
+} // namespace Gudhi
+
+#endif // WITNESS_COMPLEX_H_
diff --git a/include/gudhi/allocator.h b/include/gudhi/allocator.h
new file mode 100644
index 00000000..4ede14e4
--- /dev/null
+++ b/include/gudhi/allocator.h
@@ -0,0 +1,55 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Marc Glisse
+ *
+ * Copyright (C) 2015 INRIA Saclay - Ile de France
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ALLOCATOR_H_
+#define ALLOCATOR_H_
+
+#include <memory>
+#include <utility>
+
+namespace Gudhi {
+
+/** \private
+ * An allocator that can be used to build an uninitialized vector.
+ */
+template <class T, class Base = std::allocator<T>>
+struct no_init_allocator : Base {
+ typedef std::allocator_traits<Base> Base_traits;
+ template <class U> struct rebind {
+ typedef no_init_allocator<U, typename Base_traits::template rebind_alloc<U>> other;
+ };
+
+ // Inherit constructors.
+ using Base::Base;
+
+ // Do nothing: that's the whole point!
+ template<class P>
+ void construct(P*) noexcept {}
+
+ template<class P, class...U> void construct(P*p, U&&...u) {
+ Base_traits::construct(*(Base*)this, p, std::forward<U>(u)...);
+ }
+};
+
+} // namespace Gudhi
+
+#endif // ALLOCATOR_H_
diff --git a/include/gudhi/distance_functions.h b/include/gudhi/distance_functions.h
new file mode 100644
index 00000000..cd518581
--- /dev/null
+++ b/include/gudhi/distance_functions.h
@@ -0,0 +1,43 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Clément Maria
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Méditerranée (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DISTANCE_FUNCTIONS_H_
+#define DISTANCE_FUNCTIONS_H_
+
+#include <cmath> // for std::sqrt
+
+/* Compute the Euclidean distance between two Points given
+ * by a range of coordinates. The points are assumed to have
+ * the same dimension. */
+template< typename Point >
+double euclidean_distance(Point &p1, Point &p2) {
+ double dist = 0.;
+ auto it1 = p1.begin();
+ auto it2 = p2.begin();
+ for (; it1 != p1.end(); ++it1, ++it2) {
+ double tmp = *it1 - *it2;
+ dist += tmp*tmp;
+ }
+ return std::sqrt(dist);
+}
+
+#endif // DISTANCE_FUNCTIONS_H_
diff --git a/include/gudhi/graph_simplicial_complex.h b/include/gudhi/graph_simplicial_complex.h
new file mode 100644
index 00000000..042ef516
--- /dev/null
+++ b/include/gudhi/graph_simplicial_complex.h
@@ -0,0 +1,99 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Clément Maria
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Méditerranée (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRAPH_SIMPLICIAL_COMPLEX_H_
+#define GRAPH_SIMPLICIAL_COMPLEX_H_
+
+#include <boost/graph/adjacency_list.hpp>
+
+#include <utility> // for pair<>
+#include <vector>
+#include <map>
+
+/* Edge tag for Boost PropertyGraph. */
+struct edge_filtration_t {
+ typedef boost::edge_property_tag kind;
+};
+
+/* Vertex tag for Boost PropertyGraph. */
+struct vertex_filtration_t {
+ typedef boost::vertex_property_tag kind;
+};
+
+typedef int Vertex_handle;
+typedef double Filtration_value;
+typedef boost::adjacency_list < boost::vecS, boost::vecS, boost::undirectedS
+, boost::property < vertex_filtration_t, Filtration_value >
+, boost::property < edge_filtration_t, Filtration_value >
+> Graph_t;
+typedef std::pair< Vertex_handle, Vertex_handle > Edge_t;
+
+/** \brief Output the proximity graph of the points.
+ *
+ * If points contains n elements, the proximity graph is the graph
+ * with n vertices, and an edge [u,v] iff the distance function between
+ * points u and v is smaller than threshold.
+ *
+ * The type PointCloud furnishes .begin() and .end() methods, that return
+ * iterators with value_type Point.
+ */
+template< typename PointCloud
+, typename Point >
+Graph_t compute_proximity_graph(PointCloud &points
+ , Filtration_value threshold
+ , Filtration_value distance(Point p1, Point p2)) {
+ std::vector< Edge_t > edges;
+ std::vector< Filtration_value > edges_fil;
+ std::map< Vertex_handle, Filtration_value > vertices;
+
+ Vertex_handle idx_u, idx_v;
+ Filtration_value fil;
+ idx_u = 0;
+ for (auto it_u = points.begin(); it_u != points.end(); ++it_u) {
+ idx_v = idx_u + 1;
+ for (auto it_v = it_u + 1; it_v != points.end(); ++it_v, ++idx_v) {
+ fil = distance(*it_u, *it_v);
+ if (fil <= threshold) {
+ edges.emplace_back(idx_u, idx_v);
+ edges_fil.push_back(fil);
+ }
+ }
+ ++idx_u;
+ }
+
+ Graph_t skel_graph(edges.begin()
+ , edges.end()
+ , edges_fil.begin()
+ , idx_u); // number of points labeled from 0 to idx_u-1
+
+ auto vertex_prop = boost::get(vertex_filtration_t(), skel_graph);
+
+ boost::graph_traits<Graph_t>::vertex_iterator vi, vi_end;
+ for (std::tie(vi, vi_end) = boost::vertices(skel_graph);
+ vi != vi_end; ++vi) {
+ boost::put(vertex_prop, *vi, 0.);
+ }
+
+ return skel_graph;
+}
+
+#endif // GRAPH_SIMPLICIAL_COMPLEX_H_
diff --git a/include/gudhi/reader_utils.h b/include/gudhi/reader_utils.h
new file mode 100644
index 00000000..899f9df6
--- /dev/null
+++ b/include/gudhi/reader_utils.h
@@ -0,0 +1,198 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Clément Maria
+ *
+ * Copyright (C) 2014 INRIA Sophia Antipolis-Méditerranée (France)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef READER_UTILS_H_
+#define READER_UTILS_H_
+
+#include <gudhi/graph_simplicial_complex.h>
+
+#include <boost/graph/adjacency_list.hpp>
+
+#include <iostream>
+#include <fstream>
+#include <map>
+#include <limits> // for numeric_limits<>
+#include <string>
+#include <vector>
+
+/**
+ * \brief Read a set of points to turn it
+ * into a vector< vector<double> > by filling points
+ *
+ * File format: 1 point per line
+ * X11 X12 ... X1d
+ * X21 X22 ... X2d
+ * etc
+ */
+inline void read_points(std::string file_name, std::vector< std::vector< double > > & points) {
+ std::ifstream in_file(file_name.c_str(), std::ios::in);
+ if (!in_file.is_open()) {
+ std::cerr << "Unable to open file " << file_name << std::endl;
+ return;
+ }
+
+ std::string line;
+ double x;
+ while (getline(in_file, line)) {
+ std::vector< double > point;
+ std::istringstream iss(line);
+ while (iss >> x) {
+ point.push_back(x);
+ }
+ // Check for empty lines
+ if (!point.empty())
+ points.push_back(point);
+ }
+ in_file.close();
+}
+
+/**
+ * \brief Read a graph from a file.
+ *
+ * File format: 1 simplex per line
+ * Dim1 X11 X12 ... X1d Fil1
+ * Dim2 X21 X22 ... X2d Fil2
+ * etc
+ *
+ * The vertices must be labeled from 0 to n-1.
+ * Every simplex must appear exactly once.
+ * Simplices of dimension more than 1 are ignored.
+ */
+inline Graph_t read_graph(std::string file_name) {
+ std::ifstream in_(file_name.c_str(), std::ios::in);
+ if (!in_.is_open()) {
+ std::cerr << "Unable to open file " << file_name << std::endl;
+ }
+
+ std::vector< Edge_t > edges;
+ std::vector< Filtration_value > edges_fil;
+ std::map< Vertex_handle, Filtration_value > vertices;
+
+ std::string line;
+ int dim;
+ Vertex_handle u, v, max_h = -1;
+ Filtration_value fil;
+ while (getline(in_, line)) {
+ std::istringstream iss(line);
+ while (iss >> dim) {
+ switch (dim) {
+ case 0:
+ {
+ iss >> u;
+ iss >> fil;
+ vertices[u] = fil;
+ if (max_h < u) {
+ max_h = u;
+ }
+ break;
+ }
+ case 1:
+ {
+ iss >> u;
+ iss >> v;
+ iss >> fil;
+ edges.push_back(Edge_t(u, v));
+ edges_fil.push_back(fil);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+ }
+ in_.close();
+
+ if ((size_t) (max_h + 1) != vertices.size()) {
+ std::cerr << "Error: vertices must be labeled from 0 to n-1 \n";
+ }
+
+ Graph_t skel_graph(edges.begin(), edges.end(), edges_fil.begin(), vertices.size());
+ auto vertex_prop = boost::get(vertex_filtration_t(), skel_graph);
+
+ boost::graph_traits<Graph_t>::vertex_iterator vi, vi_end;
+ auto v_it = vertices.begin();
+ for (std::tie(vi, vi_end) = boost::vertices(skel_graph); vi != vi_end; ++vi, ++v_it) {
+ boost::put(vertex_prop, *vi, v_it->second);
+ }
+
+ return skel_graph;
+}
+
+/**
+ * \brief Read a face from a file.
+ *
+ * File format: 1 simplex per line
+ * Dim1 X11 X12 ... X1d Fil1
+ * Dim2 X21 X22 ... X2d Fil2
+ * etc
+ *
+ * The vertices must be labeled from 0 to n-1.
+ * Every simplex must appear exactly once.
+ * Simplices of dimension more than 1 are ignored.
+ */
+template< typename Vertex_handle, typename Filtration_value >
+bool read_simplex(std::istream & in_, std::vector< Vertex_handle > & simplex, Filtration_value & fil) {
+ int dim = 0;
+ if (!(in_ >> dim)) return false;
+ Vertex_handle v;
+ for (int i = 0; i < dim + 1; ++i) {
+ in_ >> v;
+ simplex.push_back(v);
+ }
+ in_ >> fil;
+ in_.ignore((std::numeric_limits<std::streamsize>::max)(), '\n'); // ignore until the carriage return
+ return true;
+}
+
+/**
+ * \brief Read a hasse simplex from a file.
+ *
+ * File format: 1 simplex per line
+ * Dim1 k11 k12 ... k1Dim1 Fil1
+ * Dim2 k21 k22 ... k2Dim2 Fil2
+ * etc
+ *
+ * The key of a simplex is its position in the filtration order
+ * and also the number of its row in the file.
+ * Dimi ki1 ki2 ... kiDimi Fili means that the ith simplex in the
+ * filtration has dimension Dimi, filtration value fil1 and simplices with
+ * key ki1 ... kiDimi in its boundary.*/
+template< typename Simplex_key, typename Filtration_value >
+bool read_hasse_simplex(std::istream & in_, std::vector< Simplex_key > & boundary, Filtration_value & fil) {
+ int dim;
+ if (!(in_ >> dim)) return false;
+ if (dim == 0) {
+ in_ >> fil;
+ return true;
+ }
+ Simplex_key key;
+ for (int i = 0; i < dim + 1; ++i) {
+ in_ >> key;
+ boundary.push_back(key);
+ }
+ in_ >> fil;
+ return true;
+}
+
+#endif // READER_UTILS_H_