// Copyright 2008 Gunter Winkler <guwi17@gmx.de>
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef _HPP_TESTHELPER_
#define _HPP_TESTHELPER_

#include <utility>
#include <iostream>
#include <boost/numeric/ublas/vector_expression.hpp>
#include <boost/numeric/ublas/matrix_expression.hpp>

static unsigned _success_counter = 0;
static unsigned _fail_counter    = 0;

static inline
void assertTrue(const char* message, bool condition) {
#ifndef NOMESSAGES
  std::cout << message;
#endif
  if ( condition ) {
    ++ _success_counter;
    std::cout << "1\n"; // success
  } else {
    ++ _fail_counter;
    std::cout << "0\n"; // failed
  }
}

template < class T >
void assertEquals(const char* message, T expected, T actual) {
#ifndef NOMESSAGES
  std::cout << message;
#endif
  if ( expected == actual ) {
    ++ _success_counter;
    std::cout << "1\n"; // success
  } else {
    #ifndef NOMESSAGES
      std::cout << " expected " << expected << " actual " << actual << " ";
    #endif
    ++ _fail_counter;
    std::cout << "0\n"; // failed
  }
}

inline static
std::pair<unsigned, unsigned> getResults() {
  return std::make_pair(_success_counter, _fail_counter);
}

template < class M1, class M2 >
bool compare( const boost::numeric::ublas::matrix_expression<M1> & m1,
              const boost::numeric::ublas::matrix_expression<M2> & m2 ) {
  if ((m1().size1() != m2().size1()) ||
      (m1().size2() != m2().size2())) {
    return false;
  }

  size_t size1 = m1().size1();
  size_t size2 = m1().size2();
  for (size_t i=0; i < size1; ++i) {
    for (size_t j=0; j < size2; ++j) {
      if ( m1()(i,j) != m2()(i,j) ) return false;
    }
  }
  return true;
}

template < class M1, class M2 >
bool compare( const boost::numeric::ublas::vector_expression<M1> & m1,
              const boost::numeric::ublas::vector_expression<M2> & m2 ) {
  if (m1().size() != m2().size()) {
    return false;
  }

  size_t size = m1().size();
  for (size_t i=0; i < size; ++i) {
    if ( m1()(i) != m2()(i) ) return false;
  }
  return true;
}

// Compare if two matrices or vectors are equals based on distance.

template <class AE>
typename AE::value_type mean_square(const boost::numeric::ublas::matrix_expression<AE> &me) {
    typename AE::value_type s(0);
    typename AE::size_type i, j;
    for (i=0; i!= me().size1(); i++) {
        for (j=0; j!= me().size2(); j++) {
            s += boost::numeric::ublas::scalar_traits<typename AE::value_type>::type_abs(me()(i,j));
        }
    }
    return s / (me().size1() * me().size2());
}

template <class AE>
typename AE::value_type mean_square(const boost::numeric::ublas::vector_expression<AE> &ve) {
    // We could have use norm2 here, but ublas' ABS does not support unsigned types.
    typename AE::value_type s(0);
    typename AE::size_type i;
    for (i=0; i!= ve().size(); i++) {
        s += boost::numeric::ublas::scalar_traits<typename AE::value_type>::type_abs(ve()(i));
    }
    return s / ve().size();
}

template < class M1, class M2 >
bool compare_to( const boost::numeric::ublas::matrix_expression<M1> & m1,
               const boost::numeric::ublas::matrix_expression<M2> & m2,
               double tolerance = 0.0 ) {
    if ((m1().size1() != m2().size1()) ||
        (m1().size2() != m2().size2())) {
        return false;
    }

    return mean_square(m2() - m1()) <= tolerance;
}

template < class M1, class M2 >
bool compare_to( const boost::numeric::ublas::vector_expression<M1> & m1,
               const boost::numeric::ublas::vector_expression<M2> & m2,
               double tolerance = 0.0 ) {
    if (m1().size() != m2().size()) {
        return false;
    }

    return mean_square(m2() - m1()) <= tolerance;
}

#endif


/**
 * \file dcs/disjoint_sets.hpp
 *
 * \brief Implementation of the disjoint-sets (union-find) data structure.
 *
 * \author Marco Guazzone (marco.guazzone@gmail.com)
 *
 * <hr/>
 *
 * Copyright 2009 Marco Guazzone (marco.guazzone@gmail.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef DCS_DISJOINT_SETS_HPP
#define DCS_DISJOINT_SETS_HPP

#include <boost/function.hpp>
#include <cstddef>
#include <map>
#include <vector>

namespace dcs {

namespace detail { namespace /*<unnamed>*/ {

template <typename T,typename ValueT>
struct identity_hasher
{
	ValueT operator()(T const& t) { return t; }
}; // identity

template <class ParentPA, class Vertex>
Vertex find_representative_with_path_halving(ParentPA p, Vertex v)
{
  Vertex parent = p[v];
  Vertex grandparent = p[parent];
  while (parent != grandparent) {
    p[v] = grandparent;
    v =  grandparent;
    parent = p[v];
    grandparent = p[parent];
  }
  return parent;
}

template <class ParentPA, class Vertex>
Vertex find_representative_with_full_compression(ParentPA parent, Vertex v)
{
  Vertex old = v;
  Vertex ancestor = parent[v];
  while (ancestor != v) {
    v = ancestor;
    ancestor = parent[v];
  }
  v = parent[old];
  while (ancestor != v) {
    parent[old] = ancestor;
    old = v;
    v = parent[old];
  }
  return ancestor;
}

}} // Namespace detail::<unnamed>

struct find_with_path_halving
{
	template <class ParentPA, class Vertex>
	Vertex operator()(ParentPA p, Vertex v) const
	{
		return detail::find_representative_with_path_halving(p, v);
	}
};

struct find_with_full_path_compression
{
	template <class ParentPA, class Vertex>
	Vertex operator()(ParentPA p, Vertex v) const
	{
		return detail::find_representative_with_full_compression(p, v);
	}
};

template <typename ElementT, typename FinderT = find_with_full_path_compression>
class disjoint_sets
{
	public: typedef ElementT element_type;
	public: typedef ::std::size_t size_type;
	public: typedef size_type (*hash_function_type)(element_type const&);
	public: typedef FinderT finder_type;

	public: disjoint_sets()
	: hash_(detail::identity_hasher<element_type,size_type>())
	{
	}

	public: void make_set(element_type const& e)
	{
		size_type sid(id_map_.size());
		id_map_[hash_(e)] = sid;
		inv_id_map_[sid] = e;
		ranks_.push_back(0);
		parents_.push_back(sid);

	}

	public: size_type find_set(element_type const& e) const
	{
		return finder_(parents_, id_map_.at(hash_(e)));
	}

	public: void link_sets(element_type const& e1, element_type const& e2)
	{
		size_type sid1 = find_set(e1);
		size_type sid2 = find_set(e2);
		if (sid1 == sid2)
		{
			return;
		}

		// e1 and e1 are not already in same set. Merge them.
		if (ranks_[sid1] > ranks_[sid2])
		{
			parents_[sid2] = sid1;
		}
		else
		{
			parents_[sid1] = sid2;
			if (ranks_[sid1] == ranks_[sid2])
			{
				ranks_[sid2] += 1;
			}
		}
	}

	public: void union_sets(element_type const& e1, element_type const& e2)
	{
		link_sets(find_set(e1), find_set(e2));
	}

	private: ::std::vector<size_type> ranks_;
	private: ::std::vector<size_type> parents_;
	private: ::std::map<size_type,size_type> id_map_;
	private: ::std::map<size_type,element_type> inv_id_map_;
	private: ::boost::function<size_type(element_type const&)> hash_;
	private: finder_type finder_;
}; // disjoint_sets

} // Namespace dcs

#endif // DCS_DISJOINT_SETS_HPP


//==============================================================================
//         Copyright 2003 - 2012   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2012   LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#define NT2_UNIT_MODULE "nt2 arithmetic toolbox - iround/scalar Mode"

//////////////////////////////////////////////////////////////////////////////
// unit test behavior of arithmetic components in scalar mode
//////////////////////////////////////////////////////////////////////////////
/// created by jt the 01/12/2010
///
#include <nt2/arithmetic/include/functions/iround.hpp>
#include <boost/simd/sdk/simd/io.hpp>
#include <nt2/sdk/meta/as_integer.hpp>
#include <nt2/sdk/unit/tests.hpp>
#include <nt2/sdk/unit/module.hpp>
#include <nt2/sdk/unit/tests/type_expr.hpp>

#include <nt2/constant/constant.hpp>
#include <nt2/include/constants/one.hpp>
#include <nt2/include/constants/inf.hpp>
#include <nt2/include/constants/zero.hpp>
#include <nt2/include/constants/minf.hpp>
#include <nt2/include/constants/mone.hpp>
#include <nt2/include/constants/nan.hpp>

NT2_TEST_CASE_TPL ( iround_real__1_0,  NT2_REAL_TYPES)
{

  using nt2::iround;
  using nt2::tag::iround_;
  typedef typename nt2::meta::as_integer<T>::type iT;
  typedef typename nt2::meta::call<iround_(T)>::type r_t;
  typedef iT wished_r_t;

  // return type conformity test
  NT2_TEST_TYPE_IS( r_t, wished_r_t );

  // specific values tests
  NT2_TEST_ULP_EQUAL(iround(T(1.4)), 1, 0);
  NT2_TEST_ULP_EQUAL(iround(T(1.5)), 2, 0);
  NT2_TEST_ULP_EQUAL(iround(T(1.6)), 2, 0);
  NT2_TEST_ULP_EQUAL(iround(T(2.5)), 3, 0);
  NT2_TEST_ULP_EQUAL(iround(nt2::Half<T>()), nt2::One<r_t>(), 0);
  NT2_TEST_ULP_EQUAL(iround(nt2::Inf<T>()), nt2::Inf<r_t>(), 0);
  NT2_TEST_ULP_EQUAL(iround(nt2::Mhalf<T>()), nt2::Mone<r_t>(), 0);
  NT2_TEST_ULP_EQUAL(iround(nt2::Minf<T>()), nt2::Minf<r_t>(), 0);
  NT2_TEST_ULP_EQUAL(iround(nt2::Mone<T>()), nt2::Mone<r_t>(), 0);
  NT2_TEST_ULP_EQUAL(iround(nt2::Nan<T>()), nt2::Zero<r_t>(), 0);
  NT2_TEST_ULP_EQUAL(iround(nt2::One<T>()), nt2::One<r_t>(), 0);
  NT2_TEST_ULP_EQUAL(iround(nt2::Zero<T>()), nt2::Zero<r_t>(), 0);
} // end of test for floating_

NT2_TEST_CASE_TPL ( iround_unsigned_int__1_0,  NT2_UNSIGNED_TYPES)
{

  using nt2::iround;
  using nt2::tag::iround_;
  typedef typename nt2::meta::as_integer<T>::type iT;
  typedef typename nt2::meta::call<iround_(T)>::type r_t;
  typedef iT wished_r_t;

  // return type conformity test
  NT2_TEST_TYPE_IS( r_t, wished_r_t );

  // specific values tests
  NT2_TEST_ULP_EQUAL(iround(nt2::One<T>()), nt2::One<r_t>(), 0);
  NT2_TEST_ULP_EQUAL(iround(nt2::Zero<T>()), nt2::Zero<r_t>(), 0);
} // end of test for unsigned_int_

NT2_TEST_CASE_TPL ( iround_signed_int__1_0,  NT2_INTEGRAL_SIGNED_TYPES)
{

  using nt2::iround;
  using nt2::tag::iround_;
  typedef typename nt2::meta::as_integer<T>::type iT;
  typedef typename nt2::meta::call<iround_(T)>::type r_t;
  typedef iT wished_r_t;

  // return type conformity test
  NT2_TEST_TYPE_IS( r_t, wished_r_t );

  // specific values tests
  NT2_TEST_ULP_EQUAL(iround(nt2::Mone<T>()), nt2::Mone<r_t>(), 0);
  NT2_TEST_ULP_EQUAL(iround(nt2::One<T>()), nt2::One<r_t>(), 0);
  NT2_TEST_ULP_EQUAL(iround(nt2::Zero<T>()), nt2::Zero<T>(), 0);
} // end of test for signed_int_


//  (C) Copyright Gennadiy Rozental 2011-2015.
//  Distributed under the Boost Software License, Version 1.0.
//  (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)

//  See http://www.boost.org/libs/test for the library home page.

//[example_code
#define BOOST_TEST_MODULE example
#include <boost/test/included/unit_test.hpp>

bool is_even( int i )
{
  return i%2 == 0;
}

BOOST_AUTO_TEST_CASE( test_is_even )
{
  BOOST_CHECK_PREDICATE( is_even, (14) );

  int i = 17;
  BOOST_CHECK_PREDICATE( is_even, (i) );
}
//]


// (C) Copyright 2007-2009 Andrew Sutton
//
// Use, modification and distribution are subject to the
// Boost Software License, Version 1.0 (See accompanying file
// LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_GRAPH_CYCLE_HPP
#define BOOST_GRAPH_CYCLE_HPP

#include <vector>

#include <boost/config.hpp>
#include <boost/graph/graph_concepts.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/properties.hpp>

#include <boost/concept/detail/concept_def.hpp>
namespace boost {
    namespace concepts {
        BOOST_concept(CycleVisitor,(Visitor)(Path)(Graph))
        {
            BOOST_CONCEPT_USAGE(CycleVisitor)
            {
                vis.cycle(p, g);
            }
        private:
            Visitor vis;
            Graph g;
            Path p;
        };
    } /* namespace concepts */
using concepts::CycleVisitorConcept;
} /* namespace boost */
#include <boost/concept/detail/concept_undef.hpp>

namespace boost
{

// The implementation of this algorithm is a reproduction of the Teirnan
// approach for directed graphs: bibtex follows
//
//     @article{362819,
//         author = {James C. Tiernan},
//         title = {An efficient search algorithm to find the elementary circuits of a graph},
//         journal = {Commun. ACM},
//         volume = {13},
//         number = {12},
//         year = {1970},
//         issn = {0001-0782},
//         pages = {722--726},
//         doi = {http://doi.acm.org/10.1145/362814.362819},
//             publisher = {ACM Press},
//             address = {New York, NY, USA},
//         }
//
// It should be pointed out that the author does not provide a complete analysis for
// either time or space. This is in part, due to the fact that it's a fairly input
// sensitive problem related to the density and construction of the graph, not just
// its size.
//
// I've also taken some liberties with the interpretation of the algorithm - I've
// basically modernized it to use real data structures (no more arrays and matrices).
// Oh... and there's explicit control structures - not just gotos.
//
// The problem is definitely NP-complete, an an unbounded implementation of this
// will probably run for quite a while on a large graph. The conclusions
// of this paper also reference a Paton algorithm for undirected graphs as being
// much more efficient (apparently based on spanning trees). Although not implemented,
// it can be found here:
//
//     @article{363232,
//         author = {Keith Paton},
//         title = {An algorithm for finding a fundamental set of cycles of a graph},
//         journal = {Commun. ACM},
//         volume = {12},
//         number = {9},
//         year = {1969},
//         issn = {0001-0782},
//         pages = {514--518},
//         doi = {http://doi.acm.org/10.1145/363219.363232},
//             publisher = {ACM Press},
//             address = {New York, NY, USA},
//         }

/**
 * The default cycle visitor providse an empty visit function for cycle
 * visitors.
 */
struct cycle_visitor
{
    template <typename Path, typename Graph>
    inline void cycle(const Path& p, const Graph& g)
    { }
};

/**
 * The min_max_cycle_visitor simultaneously records the minimum and maximum
 * cycles in a graph.
 */
struct min_max_cycle_visitor
{
    min_max_cycle_visitor(std::size_t& min_, std::size_t& max_)
        : minimum(min_), maximum(max_)
    { }

    template <typename Path, typename Graph>
    inline void cycle(const Path& p, const Graph& g)
    {
        BOOST_USING_STD_MIN();
        BOOST_USING_STD_MAX();
        std::size_t len = p.size();
        minimum = min BOOST_PREVENT_MACRO_SUBSTITUTION (minimum, len);
        maximum = max BOOST_PREVENT_MACRO_SUBSTITUTION (maximum, len);
    }
    std::size_t& minimum;
    std::size_t& maximum;
};

inline min_max_cycle_visitor
find_min_max_cycle(std::size_t& min_, std::size_t& max_)
{ return min_max_cycle_visitor(min_, max_); }

namespace detail
{
    template <typename Graph, typename Path>
    inline bool
    is_vertex_in_path(const Graph&,
                        typename graph_traits<Graph>::vertex_descriptor v,
                        const Path& p)
    {
        return (std::find(p.begin(), p.end(), v) != p.end());
    }

    template <typename Graph, typename ClosedMatrix>
    inline bool
    is_path_closed(const Graph& g,
                    typename graph_traits<Graph>::vertex_descriptor u,
                    typename graph_traits<Graph>::vertex_descriptor v,
                    const ClosedMatrix& closed)
    {
        // the path from u to v is closed if v can be found in the list
        // of closed vertices associated with u.
        typedef typename ClosedMatrix::const_reference Row;
        Row r = closed[get(vertex_index, g, u)];
        if(find(r.begin(), r.end(), v) != r.end()) {
            return true;
        }
        return false;
    }

    template <typename Graph, typename Path, typename ClosedMatrix>
    inline bool
    can_extend_path(const Graph& g,
                    typename graph_traits<Graph>::edge_descriptor e,
                    const Path& p,
                    const ClosedMatrix& m)
    {
        function_requires< IncidenceGraphConcept<Graph> >();
        function_requires< VertexIndexGraphConcept<Graph> >();
        typedef typename graph_traits<Graph>::vertex_descriptor Vertex;

        // get the vertices in question
        Vertex
            u = source(e, g),
            v = target(e, g);

        // conditions for allowing a traversal along this edge are:
        // 1. the index of v must be greater than that at which the
        //    the path is rooted (p.front()).
        // 2. the vertex v cannot already be in the path
        // 3. the vertex v cannot be closed to the vertex u

        bool indices = get(vertex_index, g, p.front()) < get(vertex_index, g, v);
        bool path = !is_vertex_in_path(g, v, p);
        bool closed = !is_path_closed(g, u, v, m);
        return indices && path && closed;
    }

    template <typename Graph, typename Path>
    inline bool
    can_wrap_path(const Graph& g, const Path& p)
    {
        function_requires< IncidenceGraphConcept<Graph> >();
        typedef typename graph_traits<Graph>::vertex_descriptor Vertex;
        typedef typename graph_traits<Graph>::out_edge_iterator OutIterator;

        // iterate over the out-edges of the back, looking for the
        // front of the path. also, we can't travel along the same
        // edge that we did on the way here, but we don't quite have the
        // stringent requirements that we do in can_extend_path().
        Vertex
            u = p.back(),
            v = p.front();
        OutIterator i, end;
        for(boost::tie(i, end) = out_edges(u, g); i != end; ++i) {
            if((target(*i, g) == v)) {
                return true;
            }
        }
        return false;
    }

    template <typename Graph,
        typename Path,
        typename ClosedMatrix>
    inline typename graph_traits<Graph>::vertex_descriptor
    extend_path(const Graph& g,
                Path& p,
                ClosedMatrix& closed)
    {
        function_requires< IncidenceGraphConcept<Graph> >();
        typedef typename graph_traits<Graph>::vertex_descriptor Vertex;
        typedef typename graph_traits<Graph>::edge_descriptor Edge;
        typedef typename graph_traits<Graph>::out_edge_iterator OutIterator;

        // get the current vertex
        Vertex u = p.back();
        Vertex ret = graph_traits<Graph>::null_vertex();

        // AdjacencyIterator i, end;
        OutIterator i, end;
        for(boost::tie(i, end) = out_edges(u, g); i != end; ++i) {
            Vertex v = target(*i, g);

            // if we can actually extend along this edge,
            // then that's what we want to do
            if(can_extend_path(g, *i, p, closed)) {
                p.push_back(v);         // add the vertex to the path
                ret = v;
                break;
            }
        }
        return ret;
    }

    template <typename Graph, typename Path, typename ClosedMatrix>
    inline bool
    exhaust_paths(const Graph& g, Path& p, ClosedMatrix& closed)
    {
        function_requires< GraphConcept<Graph> >();
        typedef typename graph_traits<Graph>::vertex_descriptor Vertex;

        // if there's more than one vertex in the path, this closes
        // of some possible routes and returns true. otherwise, if there's
        // only one vertex left, the vertex has been used up
        if(p.size() > 1) {
            // get the last and second to last vertices, popping the last
            // vertex off the path
            Vertex last, prev;
            last = p.back();
            p.pop_back();
            prev = p.back();

            // reset the closure for the last vertex of the path and
            // indicate that the last vertex in p is now closed to
            // the next-to-last vertex in p
            closed[get(vertex_index, g, last)].clear();
            closed[get(vertex_index, g, prev)].push_back(last);
            return true;
        }
        else {
            return false;
        }
    }

    template <typename Graph, typename Visitor>
    inline void
    all_cycles_from_vertex(const Graph& g,
                            typename graph_traits<Graph>::vertex_descriptor v,
                            Visitor vis,
                            std::size_t minlen,
                            std::size_t maxlen)
    {
        function_requires< VertexListGraphConcept<Graph> >();
        typedef typename graph_traits<Graph>::vertex_descriptor Vertex;
        typedef std::vector<Vertex> Path;
        function_requires< CycleVisitorConcept<Visitor,Path,Graph> >();
        typedef std::vector<Vertex> VertexList;
        typedef std::vector<VertexList> ClosedMatrix;

        Path p;
        ClosedMatrix closed(num_vertices(g), VertexList());
        Vertex null = graph_traits<Graph>::null_vertex();

        // each path investigation starts at the ith vertex
        p.push_back(v);

        while(1) {
            // extend the path until we've reached the end or the
            // maxlen-sized cycle
            Vertex j = null;
            while(((j = detail::extend_path(g, p, closed)) != null)
                    && (p.size() < maxlen))
                ; // empty loop

            // if we're done extending the path and there's an edge
            // connecting the back to the front, then we should have
            // a cycle.
            if(detail::can_wrap_path(g, p) && p.size() >= minlen) {
                vis.cycle(p, g);
            }

            if(!detail::exhaust_paths(g, p, closed)) {
                break;
            }
        }
    }

    // Select the minimum allowable length of a cycle based on the directedness
    // of the graph - 2 for directed, 3 for undirected.
    template <typename D> struct min_cycles { enum { value = 2 }; };
    template <> struct min_cycles<undirected_tag> { enum { value = 3 }; };
} /* namespace detail */

template <typename Graph, typename Visitor>
inline void
tiernan_all_cycles(const Graph& g,
                    Visitor vis,
                    std::size_t minlen,
                    std::size_t maxlen)
{
    function_requires< VertexListGraphConcept<Graph> >();
    typedef typename graph_traits<Graph>::vertex_iterator VertexIterator;

    VertexIterator i, end;
    for(boost::tie(i, end) = vertices(g); i != end; ++i) {
        detail::all_cycles_from_vertex(g, *i, vis, minlen, maxlen);
    }
}

template <typename Graph, typename Visitor>
inline void
tiernan_all_cycles(const Graph& g, Visitor vis, std::size_t maxlen)
{
    typedef typename graph_traits<Graph>::directed_category Dir;
    tiernan_all_cycles(g, vis, detail::min_cycles<Dir>::value, maxlen);
}

template <typename Graph, typename Visitor>
inline void
tiernan_all_cycles(const Graph& g, Visitor vis)
{
    typedef typename graph_traits<Graph>::directed_category Dir;
    tiernan_all_cycles(g, vis, detail::min_cycles<Dir>::value,
                       (std::numeric_limits<std::size_t>::max)());
}

template <typename Graph>
inline std::pair<std::size_t, std::size_t>
tiernan_girth_and_circumference(const Graph& g)
{
    std::size_t
        min_ = (std::numeric_limits<std::size_t>::max)(),
        max_ = 0;
    tiernan_all_cycles(g, find_min_max_cycle(min_, max_));

    // if this is the case, the graph is acyclic...
    if(max_ == 0) max_ = min_;

    return std::make_pair(min_, max_);
}

template <typename Graph>
inline std::size_t
tiernan_girth(const Graph& g)
{ return tiernan_girth_and_circumference(g).first; }

template <typename Graph>
inline std::size_t
tiernan_circumference(const Graph& g)
{ return tiernan_girth_and_circumference(g).second; }

} /* namespace boost */

#endif


#define BOOST_TEST_MODULE "test_read_clementi_dihedral_potential"

#ifdef BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
#else
#include <boost/test/included/unit_test.hpp>
#endif

#include <mjolnir/input/read_local_potential.hpp>
#include <tuple>

using test_types = std::tuple<double, float>;

constexpr inline float  tolerance_value(float)  noexcept {return 1e-4;}
constexpr inline double tolerance_value(double) noexcept {return 1e-8;}

template<typename Real>
decltype(boost::test_tools::tolerance(std::declval<Real>()))
tolerance() {return boost::test_tools::tolerance(tolerance_value(Real()));}

BOOST_AUTO_TEST_CASE_TEMPLATE(read_clementi_dihedral_noenv, T, test_types)
{
    mjolnir::LoggerManager::set_default_logger("test_read_clementi_dihedral.log");

    using real_type = T;
    {
        using namespace toml::literals;
        const toml::value env;
        const toml::value v = u8R"(
            indices = [1, 2, 3, 4]
            k1 = 3.14
            k3 = 0.577
            v0 = 2.71
        )"_toml;

        const auto g = mjolnir::read_clementi_dihedral_potential<real_type>(v, env);
        BOOST_TEST(g.k1() == real_type(3.14),  tolerance<real_type>());
        BOOST_TEST(g.k3() == real_type(0.577), tolerance<real_type>());
        BOOST_TEST(g.v0() == real_type(2.71),  tolerance<real_type>());
    }
}

BOOST_AUTO_TEST_CASE_TEMPLATE(read_clementi_dihedral_env, T, test_types)
{
    mjolnir::LoggerManager::set_default_logger("test_read_clementi_dihedral.log");

    using real_type = T;
    {
        using namespace toml::literals;
        const auto env = u8R"(
            indices = [1, 2, 3, 4]
            k1 = 3.14
            k3 = 0.577
            v0 = 2.71
        )"_toml;
        const auto v = u8R"(
            indices = "indices"
            k1 = "k1"
            k3 = "k3"
            v0 = "v0"
        )"_toml;

        const auto g = mjolnir::read_clementi_dihedral_potential<real_type>(v, env);
        BOOST_TEST(g.k1() == real_type(3.14),  tolerance<real_type>());
        BOOST_TEST(g.k3() == real_type(0.577), tolerance<real_type>());
        BOOST_TEST(g.v0() == real_type(2.71),  tolerance<real_type>());
    }
}

// ---------------------------------------------------------------------------
// read_local_potential

BOOST_AUTO_TEST_CASE_TEMPLATE(read_local_potential_clementi_dihedral_noenv, T, test_types)
{
    mjolnir::LoggerManager::set_default_logger("test_read_clementi_dihedral.log");

    using real_type = T;
    {
        using namespace toml::literals;
        const toml::value v = u8R"(
            parameters = [
                {indices = [1, 2, 3, 4], k1 = 3.14, k3 = 0.577, v0 = 2.71}
            ]
        )"_toml;

        const auto g = mjolnir::read_local_potential<4,
              mjolnir::ClementiDihedralPotential<real_type>>(v);

        const std::array<std::size_t, 4> ref_idx{{1, 2, 3, 4}};

        BOOST_TEST(g.size() == 1u);
        BOOST_TEST(g.at(0).first == ref_idx);
        BOOST_TEST(g.at(0).second.k1() == real_type(3.14),  tolerance<real_type>());
        BOOST_TEST(g.at(0).second.k3() == real_type(0.577), tolerance<real_type>());
        BOOST_TEST(g.at(0).second.v0() == real_type(2.71),  tolerance<real_type>());
    }
}

BOOST_AUTO_TEST_CASE_TEMPLATE(read_local_potential_clementi_dihedral_env, T, test_types)
{
    mjolnir::LoggerManager::set_default_logger("test_read_clementi_dihedral.log");

    using real_type = T;
    {
        using namespace toml::literals;
        const toml::value v = u8R"(
            env.pi    = 3.14
            env.gamma = 0.577
            env.e     = 2.71
            parameters = [
                {indices = [1, 2, 3, 4], k1 = "pi", k3 = "gamma", v0 = "e"}
            ]
        )"_toml;

        const auto g = mjolnir::read_local_potential<4,
              mjolnir::ClementiDihedralPotential<real_type>>(v);

        const std::array<std::size_t, 4> ref_idx{{1, 2, 3, 4}};

        BOOST_TEST(g.size() == 1u);
        BOOST_TEST(g.at(0).first == ref_idx);
        BOOST_TEST(g.at(0).second.k1() == real_type(3.14),  tolerance<real_type>());
        BOOST_TEST(g.at(0).second.k3() == real_type(0.577), tolerance<real_type>());
        BOOST_TEST(g.at(0).second.v0() == real_type(2.71),  tolerance<real_type>());
    }
}


#include "leap.h"
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_CASE(a_known_leap_year)
{
    BOOST_REQUIRE(leap::is_leap_year(1996));
}

BOOST_AUTO_TEST_CASE(any_old_year)
{
    BOOST_REQUIRE(!leap::is_leap_year(1997));
}

BOOST_AUTO_TEST_CASE(turn_of_the_20th_century)
{
    BOOST_REQUIRE(!leap::is_leap_year(1900));
}

BOOST_AUTO_TEST_CASE(turn_of_the_21st_century)
{
    BOOST_REQUIRE(leap::is_leap_year(2000));
}

BOOST_AUTO_TEST_CASE(turn_of_the_25th_century)
{
    BOOST_REQUIRE(leap::is_leap_year(2400));
}
#if defined(EXERCISM_RUN_ALL_TESTS)
#endif


/*    This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT.
 *    See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details.
 *    Author(s):       Vincent Rouvreau
 *
 *    Copyright (C) 2014 Inria
 *
 *    Modification(s):
 *      - YYYY/MM Author: Description of the modification
 */

#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
#include <utility>  // std::pair, std::make_pair
#include <cmath>  // float comparison
#include <limits>
#include <functional>  // greater
#include <tuple>  // std::tie
#include <iterator>  // for std::distance
#include <cstddef>  // for std::size_t

#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE "simplex_tree"
#include <boost/test/unit_test.hpp>
#include <boost/mpl/list.hpp>

//  ^
// /!\ Nothing else from Simplex_tree shall be included to test includes are well defined.
#include "gudhi/Simplex_tree.h"

using namespace Gudhi;

typedef boost::mpl::list<Simplex_tree<>, Simplex_tree<Simplex_tree_options_fast_persistence>> list_of_tested_variants;

template<class typeST>
void test_empty_simplex_tree(typeST& tst) {
  typedef typename typeST::Vertex_handle Vertex_handle;
  const Vertex_handle DEFAULT_VERTEX_VALUE = Vertex_handle(- 1);
  BOOST_CHECK(tst.null_vertex() == DEFAULT_VERTEX_VALUE);
  BOOST_CHECK(tst.num_vertices() == (size_t) 0);
  BOOST_CHECK(tst.num_simplices() == (size_t) 0);
  typename typeST::Siblings* STRoot = tst.root();
  BOOST_CHECK(STRoot != nullptr);
  BOOST_CHECK(STRoot->oncles() == nullptr);
  BOOST_CHECK(STRoot->parent() == DEFAULT_VERTEX_VALUE);
  BOOST_CHECK(tst.dimension() == -1);
}

template<class typeST>
void test_iterators_on_empty_simplex_tree(typeST& tst) {
  std::clog << "Iterator on vertices: " << std::endl;
  for (auto vertex : tst.complex_vertex_range()) {
    std::clog << "vertice:" << vertex << std::endl;
    BOOST_CHECK(false); // shall be empty
  }
  std::clog << "Iterator on simplices: " << std::endl;
  for (auto simplex : tst.complex_simplex_range()) {
    BOOST_CHECK(simplex != simplex); // shall be empty - to remove warning of non-used simplex
  }

  std::clog
      << "Iterator on Simplices in the filtration, with [filtration value]:"
      << std::endl;
  for (auto f_simplex : tst.filtration_simplex_range()) {
    BOOST_CHECK(false); // shall be empty
    std::clog << "test_iterators_on_empty_simplex_tree - filtration="
        << tst.filtration(f_simplex) << std::endl;
  }
}

BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_when_empty, typeST, list_of_tested_variants) {
  typedef std::pair<typename typeST::Simplex_handle, bool> typePairSimplexBool;
  typedef std::vector<typename typeST::Vertex_handle> typeVectorVertex;

  std::clog << "********************************************************************" << std::endl;
  std::clog << "TEST OF DEFAULT CONSTRUCTOR" << std::endl;
  typeST st;

  test_empty_simplex_tree(st);

  test_iterators_on_empty_simplex_tree(st);
  // TEST OF EMPTY INSERTION
  std::clog << "TEST OF EMPTY INSERTION" << std::endl;
  typeVectorVertex simplexVectorEmpty;
  BOOST_CHECK(simplexVectorEmpty.empty() == true);
  typePairSimplexBool returnEmptyValue = st.insert_simplex(simplexVectorEmpty, 0.0);
  BOOST_CHECK(returnEmptyValue.first == typename typeST::Simplex_handle(nullptr));
  BOOST_CHECK(returnEmptyValue.second == true);

  test_empty_simplex_tree(st);

  test_iterators_on_empty_simplex_tree(st);
}

bool AreAlmostTheSame(float a, float b) {
  return std::fabs(a - b) < std::numeric_limits<float>::epsilon();
}

BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_from_file, typeST, list_of_tested_variants) {
  // TEST OF INSERTION
  std::clog << "********************************************************************" << std::endl;
  std::clog << "TEST OF SIMPLEX TREE FROM A FILE" << std::endl;
  typeST st;

  std::string inputFile("simplex_tree_for_unit_test.txt");
  std::ifstream simplex_tree_stream(inputFile.c_str());
  simplex_tree_stream >> st;

  // Display the Simplex_tree
  std::clog << "The complex contains " << st.num_simplices() << " simplices" << std::endl;
  std::clog << "   - dimension " << st.dimension() << std::endl;

  // Check
  BOOST_CHECK(st.num_simplices() == 143353);
  BOOST_CHECK(st.dimension() == 3);

  int previous_size = 0;
  for (auto f_simplex : st.filtration_simplex_range()) {
    // Size of simplex
    int size = 0;
    for (auto vertex : st.simplex_vertex_range(f_simplex)) {
      // Remove warning
      (void) vertex;
      size++;
    }
    BOOST_CHECK(AreAlmostTheSame(st.filtration(f_simplex), (0.1 * size))); // Specific test: filtration = 0.1 * simplex_size
    BOOST_CHECK(previous_size <= size); // Check list is sorted (because of sorted filtrations in simplex_tree.txt)
    previous_size = size;
  }
  simplex_tree_stream.close();
}

template<class typeST, class typeSimplex>
void test_simplex_tree_contains(typeST& simplexTree, typeSimplex& simplex, int pos) {
  auto f_simplex = simplexTree.filtration_simplex_range().begin() + pos;

  std::clog << "test_simplex_tree_contains - filtration=" << simplexTree.filtration(*f_simplex) << "||" << simplex.second << std::endl;
  BOOST_CHECK(AreAlmostTheSame(simplexTree.filtration(*f_simplex), simplex.second));

  int simplexIndex = simplex.first.size() - 1;
  std::sort(simplex.first.begin(), simplex.first.end()); // if the simplex wasn't sorted, the next test could fail
  for (auto vertex : simplexTree.simplex_vertex_range(*f_simplex)) {
    std::clog << "test_simplex_tree_contains - vertex=" << vertex << "||" << simplex.first.at(simplexIndex) << std::endl;
    BOOST_CHECK(vertex == simplex.first.at(simplexIndex));
    BOOST_CHECK(simplexIndex >= 0);
    simplexIndex--;
  }
}

template<class typeST, class typePairSimplexBool>
void test_simplex_tree_insert_returns_true(const typePairSimplexBool& returnValue) {
  BOOST_CHECK(returnValue.second == true);
  // Simplex_handle = boost::container::flat_map< typeST::Vertex_handle, Node >::iterator
  typename typeST::Simplex_handle shReturned = returnValue.first;
  BOOST_CHECK(shReturned != typename typeST::Simplex_handle(nullptr));
}

// Global variables
int dim_max = -1;

template<class typeST, class Filtration_value>
void set_and_test_simplex_tree_dim_fil(typeST& simplexTree, int vectorSize, const Filtration_value& fil) {
  if (vectorSize > dim_max + 1) {
    dim_max = vectorSize - 1;
    simplexTree.set_dimension(dim_max);
    std::clog << "   set_and_test_simplex_tree_dim_fil - dim_max=" << dim_max
        << std::endl;
  }

  BOOST_CHECK(simplexTree.dimension() == dim_max);

  // Another way to count simplices:
  size_t num_simp = 0;
  for (auto f_simplex : simplexTree.complex_simplex_range()) {
    // Remove warning
    (void) f_simplex;
    num_simp++;
  }

  BOOST_CHECK(simplexTree.num_simplices() == num_simp);
}

BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_insertion, typeST, list_of_tested_variants) {
  typedef typename typeST::Filtration_value Filtration_value;
  typedef std::pair<typename typeST::Simplex_handle, bool> typePairSimplexBool;
  typedef std::vector<typename typeST::Vertex_handle> typeVectorVertex;
  typedef std::pair<typeVectorVertex, Filtration_value> typeSimplex;
  const Filtration_value FIRST_FILTRATION_VALUE = 0.1;
  const Filtration_value SECOND_FILTRATION_VALUE = 0.2;
  const Filtration_value THIRD_FILTRATION_VALUE = 0.3;
  const Filtration_value FOURTH_FILTRATION_VALUE = 0.4;
  // reset since we run the test several times
  dim_max = -1;

  // TEST OF INSERTION
  std::clog << "********************************************************************" << std::endl;
  std::clog << "TEST OF INSERTION" << std::endl;
  typeST st;

  // ++ FIRST
  std::clog << "   - INSERT 0" << std::endl;
  typeVectorVertex firstSimplexVector{0};
  BOOST_CHECK(firstSimplexVector.size() == 1);
  typeSimplex firstSimplex = std::make_pair(firstSimplexVector, Filtration_value(FIRST_FILTRATION_VALUE));
  typePairSimplexBool returnValue = st.insert_simplex(firstSimplex.first, firstSimplex.second);

  test_simplex_tree_insert_returns_true<typeST>(returnValue);
  set_and_test_simplex_tree_dim_fil(st, firstSimplexVector.size(), firstSimplex.second);
  BOOST_CHECK(st.num_vertices() == (size_t) 1);

  // ++ SECOND
  std::clog << "   - INSERT 1" << std::endl;
  typeVectorVertex secondSimplexVector{1};
  BOOST_CHECK(secondSimplexVector.size() == 1);
  typeSimplex secondSimplex = std::make_pair(secondSimplexVector, Filtration_value(FIRST_FILTRATION_VALUE));
  returnValue = st.insert_simplex(secondSimplex.first, secondSimplex.second);

  test_simplex_tree_insert_returns_true<typeST>(returnValue);
  set_and_test_simplex_tree_dim_fil(st, secondSimplexVector.size(), secondSimplex.second);
  BOOST_CHECK(st.num_vertices() == (size_t) 2);

  // ++ THIRD
  std::clog << "   - INSERT (0,1)" << std::endl;
  typeVectorVertex thirdSimplexVector{0, 1};
  BOOST_CHECK(thirdSimplexVector.size() == 2);
  typeSimplex thirdSimplex = std::make_pair(thirdSimplexVector, Filtration_value(SECOND_FILTRATION_VALUE));
  returnValue = st.insert_simplex(thirdSimplex.first, thirdSimplex.second);

  test_simplex_tree_insert_returns_true<typeST>(returnValue);
  set_and_test_simplex_tree_dim_fil(st, thirdSimplexVector.size(), thirdSimplex.second);
  BOOST_CHECK(st.num_vertices() == (size_t) 2); // Not incremented !!

  // ++ FOURTH
  std::clog << "   - INSERT 2" << std::endl;
  typeVectorVertex fourthSimplexVector{2};
  BOOST_CHECK(fourthSimplexVector.size() == 1);
  typeSimplex fourthSimplex = std::make_pair(fourthSimplexVector, Filtration_value(FIRST_FILTRATION_VALUE));
  returnValue = st.insert_simplex(fourthSimplex.first, fourthSimplex.second);

  test_simplex_tree_insert_returns_true<typeST>(returnValue);
  set_and_test_simplex_tree_dim_fil(st, fourthSimplexVector.size(), fourthSimplex.second);
  BOOST_CHECK(st.num_vertices() == (size_t) 3);

  // ++ FIFTH
  std::clog << "   - INSERT (2,0)" << std::endl;
  typeVectorVertex fifthSimplexVector{2, 0};
  BOOST_CHECK(fifthSimplexVector.size() == 2);
  typeSimplex fifthSimplex = std::make_pair(fifthSimplexVector, Filtration_value(SECOND_FILTRATION_VALUE));
  returnValue = st.insert_simplex(fifthSimplex.first, fifthSimplex.second);

  test_simplex_tree_insert_returns_true<typeST>(returnValue);
  set_and_test_simplex_tree_dim_fil(st, fifthSimplexVector.size(), fifthSimplex.second);
  BOOST_CHECK(st.num_vertices() == (size_t) 3); // Not incremented !!

  // ++ SIXTH
  std::clog << "   - INSERT (2,1)" << std::endl;
  typeVectorVertex sixthSimplexVector{2, 1};
  BOOST_CHECK(sixthSimplexVector.size() == 2);
  typeSimplex sixthSimplex = std::make_pair(sixthSimplexVector, Filtration_value(SECOND_FILTRATION_VALUE));
  returnValue = st.insert_simplex(sixthSimplex.first, sixthSimplex.second);

  test_simplex_tree_insert_returns_true<typeST>(returnValue);
  set_and_test_simplex_tree_dim_fil(st, sixthSimplexVector.size(), sixthSimplex.second);
  BOOST_CHECK(st.num_vertices() == (size_t) 3); // Not incremented !!

  // ++ SEVENTH
  std::clog << "   - INSERT (2,1,0)" << std::endl;
  typeVectorVertex seventhSimplexVector{2, 1, 0};
  BOOST_CHECK(seventhSimplexVector.size() == 3);
  typeSimplex seventhSimplex = std::make_pair(seventhSimplexVector, Filtration_value(THIRD_FILTRATION_VALUE));
  returnValue = st.insert_simplex(seventhSimplex.first, seventhSimplex.second);

  test_simplex_tree_insert_returns_true<typeST>(returnValue);
  set_and_test_simplex_tree_dim_fil(st, seventhSimplexVector.size(), seventhSimplex.second);
  BOOST_CHECK(st.num_vertices() == (size_t) 3); // Not incremented !!

  // ++ EIGHTH
  std::clog << "   - INSERT 3" << std::endl;
  typeVectorVertex eighthSimplexVector{3};
  BOOST_CHECK(eighthSimplexVector.size() == 1);
  typeSimplex eighthSimplex = std::make_pair(eighthSimplexVector, Filtration_value(FIRST_FILTRATION_VALUE));
  returnValue = st.insert_simplex(eighthSimplex.first, eighthSimplex.second);

  test_simplex_tree_insert_returns_true<typeST>(returnValue);
  set_and_test_simplex_tree_dim_fil(st, eighthSimplexVector.size(), eighthSimplex.second);
  BOOST_CHECK(st.num_vertices() == (size_t) 4);

  // ++ NINETH
  std::clog << "   - INSERT (3,0)" << std::endl;
  typeVectorVertex ninethSimplexVector{3, 0};
  BOOST_CHECK(ninethSimplexVector.size() == 2);
  typeSimplex ninethSimplex = std::make_pair(ninethSimplexVector, Filtration_value(SECOND_FILTRATION_VALUE));
  returnValue = st.insert_simplex(ninethSimplex.first, ninethSimplex.second);

  test_simplex_tree_insert_returns_true<typeST>(returnValue);
  set_and_test_simplex_tree_dim_fil(st, ninethSimplexVector.size(), ninethSimplex.second);
  BOOST_CHECK(st.num_vertices() == (size_t) 4); // Not incremented !!

  // ++ TENTH
  std::clog << "   - INSERT 0 (already inserted)" << std::endl;
  typeVectorVertex tenthSimplexVector{0};
  BOOST_CHECK(tenthSimplexVector.size() == 1);
  // With a different filtration value
  typeSimplex tenthSimplex = std::make_pair(tenthSimplexVector, Filtration_value(FOURTH_FILTRATION_VALUE));
  returnValue = st.insert_simplex(tenthSimplex.first, tenthSimplex.second);

  BOOST_CHECK(returnValue.second == false);
  // Simplex_handle = boost::container::flat_map< typeST::Vertex_handle, Node >::iterator
  typename typeST::Simplex_handle shReturned = returnValue.first;
  BOOST_CHECK(shReturned == typename typeST::Simplex_handle(nullptr));
  std::clog << "st.num_vertices()=" << st.num_vertices() << std::endl;
  BOOST_CHECK(st.num_vertices() == (size_t) 4); // Not incremented !!
  BOOST_CHECK(st.dimension() == dim_max);

  // ++ ELEVENTH
  std::clog << "   - INSERT (2,1,0) (already inserted)" << std::endl;
  typeVectorVertex eleventhSimplexVector{2, 1, 0};
  BOOST_CHECK(eleventhSimplexVector.size() == 3);
  typeSimplex eleventhSimplex = std::make_pair(eleventhSimplexVector, Filtration_value(FOURTH_FILTRATION_VALUE));
  returnValue = st.insert_simplex(eleventhSimplex.first, eleventhSimplex.second);

  BOOST_CHECK(returnValue.second == false);
  // Simplex_handle = boost::container::flat_map< typeST::Vertex_handle, Node >::iterator
  shReturned = returnValue.first;
  BOOST_CHECK(shReturned == typename typeST::Simplex_handle(nullptr));
  BOOST_CHECK(st.num_vertices() == (size_t) 4); // Not incremented !!
  BOOST_CHECK(st.dimension() == dim_max);

  /* Inserted simplex:        */
  /*    1                     */
  /*    o                     */
  /*   /X\                    */
  /*  o---o---o               */
  /*  2   0   3               */

  //   [0.1] 0
  //   [0.1] 1
  //   [0.1] 2
  //   [0.1] 3
  //   [0.2] 1 0
  //   [0.2] 2 0
  //   [0.2] 2 1
  //   [0.2] 3 0
  //   [0.3] 2 1 0
  //  !! Be careful, simplex are sorted by filtration value on insertion !!
  std::clog << "simplex_tree_insertion - first - 0" << std::endl;
  test_simplex_tree_contains(st, firstSimplex, 0); // (0) -> 0
  std::clog << "simplex_tree_insertion - second - 1" << std::endl;
  test_simplex_tree_contains(st, secondSimplex, 1); // (1) -> 1
  std::clog << "simplex_tree_insertion - third - 4" << std::endl;
  test_simplex_tree_contains(st, thirdSimplex, 4); // (0,1) -> 4
  std::clog << "simplex_tree_insertion - fourth - 2" << std::endl;
  test_simplex_tree_contains(st, fourthSimplex, 2); // (2) -> 2
  std::clog << "simplex_tree_insertion - fifth - 5" << std::endl;
  test_simplex_tree_contains(st, fifthSimplex, 5); // (2,0) -> 5
  std::clog << "simplex_tree_insertion - sixth - 6" << std::endl;
  test_simplex_tree_contains(st, sixthSimplex, 6); //(2,1) -> 6
  std::clog << "simplex_tree_insertion - seventh - 8" << std::endl;
  test_simplex_tree_contains(st, seventhSimplex, 8); // (2,1,0) -> 8
  std::clog << "simplex_tree_insertion - eighth - 3" << std::endl;
  test_simplex_tree_contains(st, eighthSimplex, 3); // (3) -> 3
  std::clog << "simplex_tree_insertion - nineth - 7" << std::endl;
  test_simplex_tree_contains(st, ninethSimplex, 7); // (3,0) -> 7

  // Display the Simplex_tree - Can not be done in the middle of 2 inserts
  std::clog << "The complex contains " << st.num_simplices() << " simplices" << std::endl;
  std::clog << "   - dimension " << st.dimension() << std::endl;
  std::clog << std::endl << std::endl << "Iterator on Simplices in the filtration, with [filtration value]:" << std::endl;
  for (auto f_simplex : st.filtration_simplex_range()) {
    std::clog << "   " << "[" << st.filtration(f_simplex) << "] ";
    for (auto vertex : st.simplex_vertex_range(f_simplex)) {
      std::clog << (int) vertex << " ";
    }
    std::clog << std::endl;
  }

}

BOOST_AUTO_TEST_CASE_TEMPLATE(NSimplexAndSubfaces_tree_insertion, typeST, list_of_tested_variants) {
  typedef std::pair<typename typeST::Simplex_handle, bool> typePairSimplexBool;
  typedef std::vector<typename typeST::Vertex_handle> typeVectorVertex;
  typedef std::pair<typeVectorVertex, typename typeST::Filtration_value> typeSimplex;
  std::clog << "********************************************************************" << std::endl;
  std::clog << "TEST OF RECURSIVE INSERTION" << std::endl;
  typeST st;
  typePairSimplexBool returnValue;
  int position = 0;

  // ++ FIRST
  std::clog << "   - INSERT (2,1,0)" << std::endl;
  typeVectorVertex SimplexVector1{2, 1, 0};
  BOOST_CHECK(SimplexVector1.size() == 3);
  returnValue = st.insert_simplex_and_subfaces(SimplexVector1);

  BOOST_CHECK(st.num_vertices() == (size_t) 3); // +3 (2, 1 and 0 are not existing)

  // Check it is well inserted
  BOOST_CHECK(true == returnValue.second);
  position = 0;
  std::sort(SimplexVector1.begin(), SimplexVector1.end(), std::greater<typename typeST::Vertex_handle>());
  for (auto vertex : st.simplex_vertex_range(returnValue.first)) {
    // Check returned Simplex_handle
    std::clog << "vertex = " << vertex << " | vector[" << position << "] = " << SimplexVector1[position] << std::endl;
    BOOST_CHECK(vertex == SimplexVector1[position]);
    position++;
  }

  // ++ SECOND
  std::clog << "   - INSERT 3" << std::endl;
  typeVectorVertex SimplexVector2{3};
  BOOST_CHECK(SimplexVector2.size() == 1);
  returnValue = st.insert_simplex_and_subfaces(SimplexVector2);

  BOOST_CHECK(st.num_vertices() == (size_t) 4); // +1 (3 is not existing)

  // Check it is well inserted
  BOOST_CHECK(true == returnValue.second);
  position = 0;
  std::sort(SimplexVector2.begin(), SimplexVector2.end(), std::greater<typename typeST::Vertex_handle>());
  for (auto vertex : st.simplex_vertex_range(returnValue.first)) {
    // Check returned Simplex_handle
    std::clog << "vertex = " << vertex << " | vector[" << position << "] = " << SimplexVector2[position] << std::endl;
    BOOST_CHECK(vertex == SimplexVector2[position]);
    position++;
  }

  // ++ THIRD
  std::clog << "   - INSERT (0,3)" << std::endl;
  typeVectorVertex SimplexVector3{3, 0};
  BOOST_CHECK(SimplexVector3.size() == 2);
  returnValue = st.insert_simplex_and_subfaces(SimplexVector3);

  BOOST_CHECK(st.num_vertices() == (size_t) 4); // Not incremented (all are existing)

  // Check it is well inserted
  BOOST_CHECK(true == returnValue.second);
  position = 0;
  std::sort(SimplexVector3.begin(), SimplexVector3.end(), std::greater<typename typeST::Vertex_handle>());
  for (auto vertex : st.simplex_vertex_range(returnValue.first)) {
    // Check returned Simplex_handle
    std::clog << "vertex = " << vertex << " | vector[" << position << "] = " << SimplexVector3[position] << std::endl;
    BOOST_CHECK(vertex == SimplexVector3[position]);
    position++;
  }

  // ++ FOURTH
  std::clog << "   - INSERT (1,0) (already inserted)" << std::endl;
  typeVectorVertex SimplexVector4{1, 0};
  BOOST_CHECK(SimplexVector4.size() == 2);
  returnValue = st.insert_simplex_and_subfaces(SimplexVector4);

  BOOST_CHECK(st.num_vertices() == (size_t) 4); // Not incremented (all are existing)

  // Check it was not inserted (already there from {2,1,0} insertion)
  BOOST_CHECK(false == returnValue.second);

  // ++ FIFTH
  std::clog << "   - INSERT (3,4,5)" << std::endl;
  typeVectorVertex SimplexVector5{3, 4, 5};
  BOOST_CHECK(SimplexVector5.size() == 3);
  returnValue = st.insert_simplex_and_subfaces(SimplexVector5);

  BOOST_CHECK(st.num_vertices() == (size_t) 6);

  // Check it is well inserted
  BOOST_CHECK(true == returnValue.second);
  position = 0;
  std::sort(SimplexVector5.begin(), SimplexVector5.end(), std::greater<typename typeST::Vertex_handle>());
  for (auto vertex : st.simplex_vertex_range(returnValue.first)) {
    // Check returned Simplex_handle
    std::clog << "vertex = " << vertex << " | vector[" << position << "] = " << SimplexVector5[position] << std::endl;
    BOOST_CHECK(vertex == SimplexVector5[position]);
    position++;
  }

  // ++ SIXTH
  std::clog << "   - INSERT (0,1,6,7)" << std::endl;
  typeVectorVertex SimplexVector6{0, 1, 6, 7};
  BOOST_CHECK(SimplexVector6.size() == 4);
  returnValue = st.insert_simplex_and_subfaces(SimplexVector6);

  BOOST_CHECK(st.num_vertices() == (size_t) 8); // +2 (6 and 7 are not existing - 0 and 1 are already existing)

  // Check it is well inserted
  BOOST_CHECK(true == returnValue.second);
  position = 0;
  std::sort(SimplexVector6.begin(), SimplexVector6.end(), std::greater<typename typeST::Vertex_handle>());
  for (auto vertex : st.simplex_vertex_range(returnValue.first)) {
    // Check returned Simplex_handle
    std::clog << "vertex = " << vertex << " | vector[" << position << "] = " << SimplexVector6[position] << std::endl;
    BOOST_CHECK(vertex == SimplexVector6[position]);
    position++;
  }

  /* Inserted simplex:        */
  /*    1   6                 */
  /*    o---o                 */
  /*   /X\7/                  */
  /*  o---o---o---o           */
  /*  2   0   3\X/4           */
  /*            o             */
  /*            5             */
  /*                          */
  /* In other words:          */
  /*   A facet [2,1,0]        */
  /*   An edge [0,3]          */
  /*   A facet [3,4,5]        */
  /*   A cell  [0,1,6,7]      */

  typeSimplex simplexPair1 = std::make_pair(SimplexVector1, 0.0);
  typeSimplex simplexPair2 = std::make_pair(SimplexVector2, 0.0);
  typeSimplex simplexPair3 = std::make_pair(SimplexVector3, 0.0);
  typeSimplex simplexPair4 = std::make_pair(SimplexVector4, 0.0);
  typeSimplex simplexPair5 = std::make_pair(SimplexVector5, 0.0);
  typeSimplex simplexPair6 = std::make_pair(SimplexVector6, 0.0);
  test_simplex_tree_contains(st, simplexPair1, 6); // (2,1,0) is in position 6
  test_simplex_tree_contains(st, simplexPair2, 7); // (3) is in position 7
  test_simplex_tree_contains(st, simplexPair3, 8); // (3,0) is in position 8
  test_simplex_tree_contains(st, simplexPair4, 2); // (1,0) is in position 2
  test_simplex_tree_contains(st, simplexPair5, 14); // (3,4,5) is in position 14
  test_simplex_tree_contains(st, simplexPair6, 26); // (7,6,1,0) is in position 26

  // ------------------------------------------------------------------------------------------------------------------
  // Find in the simplex_tree
  // ------------------------------------------------------------------------------------------------------------------
  typeVectorVertex simpleSimplexVector{1};
  typename typeST::Simplex_handle simplexFound = st.find(simpleSimplexVector);
  std::clog << "**************IS THE SIMPLEX {1} IN THE SIMPLEX TREE ?\n";
  if (simplexFound != st.null_simplex())
    std::clog << "***+ YES IT IS!\n";
  else
    std::clog << "***- NO IT ISN'T\n";
  // Check it is found
  BOOST_CHECK(simplexFound != st.null_simplex());

  typeVectorVertex unknownSimplexVector{15};
  simplexFound = st.find(unknownSimplexVector);
  std::clog << "**************IS THE SIMPLEX {15} IN THE SIMPLEX TREE ?\n";
  if (simplexFound != st.null_simplex())
    std::clog << "***+ YES IT IS!\n";
  else
    std::clog << "***- NO IT ISN'T\n";
  // Check it is NOT found
  BOOST_CHECK(simplexFound == st.null_simplex());

  simplexFound = st.find(SimplexVector6);
  std::clog << "**************IS THE SIMPLEX {0,1,6,7} IN THE SIMPLEX TREE ?\n";
  if (simplexFound != st.null_simplex())
    std::clog << "***+ YES IT IS!\n";
  else
    std::clog << "***- NO IT ISN'T\n";
  // Check it is found
  BOOST_CHECK(simplexFound != st.null_simplex());

  typeVectorVertex otherSimplexVector{1, 15};
  simplexFound = st.find(otherSimplexVector);
  std::clog << "**************IS THE SIMPLEX {15,1} IN THE SIMPLEX TREE ?\n";
  if (simplexFound != st.null_simplex())
    std::clog << "***+ YES IT IS!\n";
  else
    std::clog << "***- NO IT ISN'T\n";
  // Check it is NOT found
  BOOST_CHECK(simplexFound == st.null_simplex());

  typeVectorVertex invSimplexVector{1, 2, 0};
  simplexFound = st.find(invSimplexVector);
  std::clog << "**************IS THE SIMPLEX {1,2,0} IN THE SIMPLEX TREE ?\n";
  if (simplexFound != st.null_simplex())
    std::clog << "***+ YES IT IS!\n";
  else
    std::clog << "***- NO IT ISN'T\n";
  // Check it is found
  BOOST_CHECK(simplexFound != st.null_simplex());

  // Display the Simplex_tree - Can not be done in the middle of 2 inserts
  std::clog << "The complex contains " << st.num_simplices() << " simplices" << std::endl;
  std::clog << "   - dimension " << st.dimension() << std::endl;
  std::clog << std::endl << std::endl << "Iterator on Simplices in the filtration, with [filtration value]:" << std::endl;
  for (auto f_simplex : st.filtration_simplex_range()) {
    std::clog << "   " << "[" << st.filtration(f_simplex) << "] ";
    for (auto vertex : st.simplex_vertex_range(f_simplex)) {
      std::clog << (int) vertex << " ";
    }
    std::clog << std::endl;
  }
}

template<class typeST, class Vertex_handle>
void test_cofaces(typeST& st, const std::vector<Vertex_handle>& expected, int dim, const std::vector<typename typeST::Simplex_handle>& res) {
  typename typeST::Cofaces_simplex_range cofaces;
  if (dim == 0)
    cofaces = st.star_simplex_range(st.find(expected));
  else
    cofaces = st.cofaces_simplex_range(st.find(expected), dim);
  for (auto simplex = cofaces.begin(); simplex != cofaces.end(); ++simplex) {
    typename typeST::Simplex_vertex_range rg = st.simplex_vertex_range(*simplex);
    for (auto vertex = rg.begin(); vertex != rg.end(); ++vertex) {
      std::clog << "(" << *vertex << ")";
    }
    std::clog << std::endl;
    BOOST_CHECK(std::find(res.begin(), res.end(), *simplex) != res.end());
  }
}

BOOST_AUTO_TEST_CASE_TEMPLATE(coface_on_simplex_tree, typeST, list_of_tested_variants) {
  typedef std::vector<typename typeST::Vertex_handle> typeVectorVertex;
  std::clog << "********************************************************************" << std::endl;
  std::clog << "TEST COFACE ALGORITHM" << std::endl;
  typeST st;

  typeVectorVertex SimplexVector{2, 1, 0};
  st.insert_simplex_and_subfaces(SimplexVector);

  SimplexVector = {3, 0};
  st.insert_simplex_and_subfaces(SimplexVector);

  SimplexVector = {3, 4, 5};
  st.insert_simplex_and_subfaces(SimplexVector);

  SimplexVector = {0, 1, 6, 7};
  st.insert_simplex_and_subfaces(SimplexVector);

  /* Inserted simplex:        */
  /*    1   6                 */
  /*    o---o                 */
  /*   /X\7/                  */
  /*  o---o---o---o           */
  /*  2   0   3\X/4           */
  /*            o             */
  /*            5             */

  std::vector<typename typeST::Vertex_handle> simplex_result;
  std::vector<typename typeST::Simplex_handle> result;
  std::clog << "First test - Star of (3):" << std::endl;

  simplex_result = {3};
  result.push_back(st.find(simplex_result));

  simplex_result = {3, 0};
  result.push_back(st.find(simplex_result));

  simplex_result = {4, 3};
  result.push_back(st.find(simplex_result));

  simplex_result = {5, 4, 3};
  result.push_back(st.find(simplex_result));

  simplex_result = {5, 3};
  result.push_back(st.find(simplex_result));
  simplex_result.clear();

  std::vector<typename typeST::Vertex_handle> vertex = {3};
  test_cofaces(st, vertex, 0, result);
  vertex.clear();
  result.clear();

  vertex.push_back(1);
  vertex.push_back(7);
  std::clog << "Second test - Star of (1,7): " << std::endl;

  simplex_result = {7, 1};
  result.push_back(st.find(simplex_result));

  simplex_result = {7, 6, 1, 0};
  result.push_back(st.find(simplex_result));

  simplex_result = {7, 1, 0};
  result.push_back(st.find(simplex_result));

  simplex_result = {7, 6, 1};
  result.push_back(st.find(simplex_result));

  test_cofaces(st, vertex, 0, result);
  result.clear();

  std::clog << "Third test - 2-dimension Cofaces of simplex(1,7) : " << std::endl;

  simplex_result = {7, 1, 0};
  result.push_back(st.find(simplex_result));

  simplex_result = {7, 6, 1};
  result.push_back(st.find(simplex_result));

  test_cofaces(st, vertex, 1, result);
  result.clear();

  std::clog << "Cofaces with a codimension too high (codimension + vetices > tree.dimension) :" << std::endl;
  test_cofaces(st, vertex, 5, result);

  //std::clog << "Cofaces with an empty codimension" << std::endl;
  //test_cofaces(st, vertex, -1, result);
  //    std::clog << "Cofaces in an empty simplex tree" << std::endl;
  //   typeST empty_tree;
  //    test_cofaces(empty_tree, vertex, 1, result);
  //std::clog << "Cofaces of an empty simplex" << std::endl;
  //vertex.clear();
  // test_cofaces(st, vertex, 1, result);

}

BOOST_AUTO_TEST_CASE_TEMPLATE(copy_move_on_simplex_tree, typeST, list_of_tested_variants) {
  typedef std::vector<typename typeST::Vertex_handle> typeVectorVertex;
  std::clog << "********************************************************************" << std::endl;
  std::clog << "TEST COPY MOVE CONSTRUCTORS" << std::endl;
  typeST st;

  typeVectorVertex SimplexVector{2, 1, 0};
  st.insert_simplex_and_subfaces(SimplexVector);

  SimplexVector = {3, 0};
  st.insert_simplex_and_subfaces(SimplexVector);

  SimplexVector = {3, 4, 5};
  st.insert_simplex_and_subfaces(SimplexVector);

  SimplexVector = {0, 1, 6, 7};
  st.insert_simplex_and_subfaces(SimplexVector);

  /* Inserted simplex:        */
  /*    1   6                 */
  /*    o---o                 */
  /*   /X\7/                  */
  /*  o---o---o---o           */
  /*  2   0   3\X/4           */
  /*            o             */
  /*            5             */

  std::clog << "Printing st - address = " << &st << std::endl;

  // Copy constructor
  typeST st_copy = st;
  std::clog << "Printing a copy of st - address = " << &st_copy << std::endl;

  // Check the data are the same
  BOOST_CHECK(st == st_copy);
  // Check there is a new simplex tree reference
  BOOST_CHECK(&st != &st_copy);

  // Move constructor
  typeST st_move = std::move(st);
  std::clog << "Printing a move of st - address = " << &st_move << std::endl;

  // Check the data are the same
  BOOST_CHECK(st_move == st_copy);
  // Check there is a new simplex tree reference
  BOOST_CHECK(&st_move != &st_copy);
  BOOST_CHECK(&st_move != &st);

  typeST st_empty;
  // Check st has been emptied by the move
  BOOST_CHECK(st == st_empty);
  BOOST_CHECK(st.dimension() == -1);
  BOOST_CHECK(st.num_simplices() == 0);
  BOOST_CHECK(st.num_vertices() == (size_t)0);

  std::clog << "Printing st once again- address = " << &st << std::endl;
}

template<class typeST>
void test_simplex_is_vertex(typeST& st, typename typeST::Simplex_handle sh, typename typeST::Vertex_handle v) {
  BOOST_CHECK(st.dimension(sh) == 0);
  auto&& r = st.simplex_vertex_range(sh);
  auto i = std::begin(r);
  BOOST_CHECK(*i == v);
  BOOST_CHECK(++i == std::end(r));
}

BOOST_AUTO_TEST_CASE(non_contiguous) {
  typedef Simplex_tree<> typeST;
  typedef typeST::Simplex_handle Simplex_handle;
  std::clog << "********************************************************************" << std::endl;
  std::clog << "TEST NON-CONTIGUOUS VERTICES" << std::endl;
  typeST st;
  typeST::Vertex_handle e[] = {3,-7};
  std::clog << "Insert" << std::endl;
  st.insert_simplex_and_subfaces(e);
  BOOST_CHECK(st.num_vertices() == 2);
  BOOST_CHECK(st.num_simplices() == 3);
  std::clog << "Find" << std::endl;
  Simplex_handle sh = st.find(e);
  BOOST_CHECK(sh != st.null_simplex());
  std::clog << "Endpoints" << std::endl;
  auto p = st.endpoints(sh);
  test_simplex_is_vertex(st, p.first, 3);
  test_simplex_is_vertex(st, p.second, -7);
  std::clog << "Boundary" << std::endl;
  auto&& b = st.boundary_simplex_range(sh);
  auto i = std::begin(b);
  test_simplex_is_vertex(st, *i, -7);
  test_simplex_is_vertex(st, *++i, 3);
  BOOST_CHECK(++i == std::end(b));
}

typedef boost::mpl::list<boost::adjacency_list<boost::setS, boost::vecS, boost::directedS,
                                               boost::property<vertex_filtration_t, double>,
                                               boost::property<edge_filtration_t, double>>,
                         boost::adjacency_list<boost::setS, boost::vecS, boost::undirectedS,
                                               boost::property<vertex_filtration_t, double>,
                                               boost::property<edge_filtration_t, double>>,
                         boost::adjacency_list<boost::setS, boost::vecS, boost::bidirectionalS,
                                               boost::property<vertex_filtration_t, double>,
                                               boost::property<edge_filtration_t, double>>,
                         boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS,
                                               boost::property<vertex_filtration_t, double>,
                                               boost::property<edge_filtration_t, double>>,
                         boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS,
                                               boost::property<vertex_filtration_t, double>,
                                               boost::property<edge_filtration_t, double>>,
                         boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS,
                                               boost::property<vertex_filtration_t, double>,
                                               boost::property<edge_filtration_t, double>>> list_of_graph_variants;

BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_insert_graph, Graph, list_of_graph_variants) {
  std::clog << "********************************************************************" << std::endl;
  std::clog << "INSERT GRAPH" << std::endl;

  Graph g(3);
  // filtration value 0 everywhere
  put(Gudhi::vertex_filtration_t(), g, 0, 0);
  put(Gudhi::vertex_filtration_t(), g, 1, 0);
  put(Gudhi::vertex_filtration_t(), g, 2, 0);
  // vertices don't always occur in sorted order
  add_edge(0, 1, 1.1, g);
  add_edge(2, 0, 2.2, g);
  add_edge(2, 1, 3.3, g);

  Simplex_tree<> st1;
  st1.insert_graph(g);
  BOOST_CHECK(st1.num_simplices() == 6);

  // edges can have multiplicity in the graph unless we replace the first vecS with (hash_)setS
  add_edge(1, 0, 1.1, g);
  add_edge(1, 2, 3.3, g);
  add_edge(0, 2, 2.2, g);
  add_edge(0, 1, 1.1, g);
  add_edge(2, 1, 3.3, g);
  add_edge(2, 0, 2.2, g);
  Simplex_tree<> st2;
  st2.insert_graph(g);
  BOOST_CHECK(st2.num_simplices() == 6);

  std::clog << "st1 is" << std::endl;
  std::clog << st1 << std::endl;

  std::clog << "st2 is" << std::endl;
  std::clog << st2 << std::endl;

  BOOST_CHECK(st1 == st2);
}

BOOST_AUTO_TEST_CASE_TEMPLATE(insert_duplicated_vertices, typeST, list_of_tested_variants) {
  std::clog << "********************************************************************" << std::endl;
  std::clog << "TEST INSERT DUPLICATED VERTICES" << std::endl;
  typeST st;

  typename typeST::Simplex_handle sh;
  bool success = false;
  std::tie(sh, success) = st.insert_simplex_and_subfaces({1});
  BOOST_CHECK(success);
  BOOST_CHECK(sh != st.null_simplex());
  std::clog << "st.dimension(sh)= " << st.dimension(sh) << std::endl;
  BOOST_CHECK(st.dimension(sh) == 0);
  std::tie(sh, success) = st.insert_simplex_and_subfaces({2, 2});
  BOOST_CHECK(success);
  BOOST_CHECK(sh != st.null_simplex());
  std::clog << "st.dimension(sh)= " << st.dimension(sh) << std::endl;
  BOOST_CHECK(st.dimension(sh) == 0);
  std::tie(sh, success) = st.insert_simplex_and_subfaces({3, 3, 3});
  BOOST_CHECK(success);
  BOOST_CHECK(sh != st.null_simplex());
  std::clog << "st.dimension(sh)= " << st.dimension(sh) << std::endl;
  BOOST_CHECK(st.dimension(sh) == 0);
  std::tie(sh, success) = st.insert_simplex_and_subfaces({4, 4, 4, 4});
  BOOST_CHECK(success);
  BOOST_CHECK(sh != st.null_simplex());
  std::clog << "st.dimension(sh)= " << st.dimension(sh) << std::endl;
  BOOST_CHECK(st.dimension(sh) == 0);

  std::clog << "dimension =" << st.dimension() << " - num_vertices = " << st.num_vertices()
            << " - num_simplices = " << st.num_simplices() << std::endl;
  BOOST_CHECK(st.dimension() == 0);
  BOOST_CHECK(st.num_simplices() == st.num_vertices());

  std::tie(sh, success) = st.insert_simplex_and_subfaces({2, 1, 1, 2});
  BOOST_CHECK(success);
  BOOST_CHECK(sh != st.null_simplex());
  std::clog << "st.dimension(sh)= " << st.dimension(sh) << std::endl;
  BOOST_CHECK(st.dimension(sh) == 1);

  std::clog << "dimension =" << st.dimension() << " - num_vertices = " << st.num_vertices()
            << " - num_simplices = " << st.num_simplices() << std::endl;
  BOOST_CHECK(st.dimension() == 1);
  BOOST_CHECK(st.num_simplices() == st.num_vertices() + 1);

  // Already inserted
  std::tie(sh, success) = st.insert_simplex_and_subfaces({1, 2, 2, 1});
  BOOST_CHECK(!success);
  BOOST_CHECK(sh == st.null_simplex());

  std::clog << "dimension =" << st.dimension() << " - num_vertices = " << st.num_vertices()
            << " - num_simplices = " << st.num_simplices() << std::endl;
  BOOST_CHECK(st.dimension() == 1);
  BOOST_CHECK(st.num_simplices() == st.num_vertices() + 1);
}

BOOST_AUTO_TEST_CASE_TEMPLATE(generators, typeST, list_of_tested_variants) {
  std::cout << "********************************************************************" << std::endl;
  std::cout << "TEST FIND GENERATORS" << std::endl;
  {
    typeST st;
    st.insert_simplex_and_subfaces({0,1,2,3,4,5,6},0);
    st.assign_filtration(st.find({0,2,4}), 10);
    st.assign_filtration(st.find({1,5}), 20);
    st.assign_filtration(st.find({1,2,4}), 30);
    st.assign_filtration(st.find({3}), 5);
    st.make_filtration_non_decreasing();
    BOOST_CHECK(st.filtration(st.find({1,2}))==0);
    BOOST_CHECK(st.filtration(st.find({0,1,2,3,4}))==30);
    BOOST_CHECK(st.minimal_simplex_with_same_filtration(st.find({0,1,2,3,4,5}))==st.find({1,2,4}));
    BOOST_CHECK(st.minimal_simplex_with_same_filtration(st.find({0,2,3}))==st.find({3}));
    auto s=st.minimal_simplex_with_same_filtration(st.find({0,2,6}));
    BOOST_CHECK(s==st.find({0})||s==st.find({2})||s==st.find({6}));
    BOOST_CHECK(st.vertex_with_same_filtration(st.find({2}))==2);
    BOOST_CHECK(st.vertex_with_same_filtration(st.find({1,5}))==st.null_vertex());
    BOOST_CHECK(st.vertex_with_same_filtration(st.find({5,6}))>=5);
  }
  {
    typeST st;
    st.insert_simplex_and_subfaces({0,1}, 8);
    st.insert_simplex_and_subfaces({0,2}, 10);
    st.insert_simplex_and_subfaces({3,4}, 6);
    st.insert_simplex_and_subfaces({1,2}, 5);
    st.insert_simplex_and_subfaces({1,5}, 4);
    st.insert_simplex_and_subfaces({0,5}, 3);
    st.insert_simplex_and_subfaces({2,5}, 2);
    st.insert_simplex_and_subfaces({1,3}, 9);
    st.expansion(50);
    BOOST_CHECK(st.edge_with_same_filtration(st.find({0,1,2,5}))==st.find({0,2}));
    BOOST_CHECK(st.edge_with_same_filtration(st.find({1,5}))==st.find({1,5}));
  }
}

BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_reset_filtration, typeST, list_of_tested_variants) {
  std::clog << "********************************************************************" << std::endl;
  std::clog << "TEST RESET FILTRATION" << std::endl;
  typeST st;

  st.insert_simplex_and_subfaces({2, 1, 0}, 3.);
  st.insert_simplex_and_subfaces({3, 0}, 2.);
  st.insert_simplex_and_subfaces({3, 4, 5}, 3.);
  st.insert_simplex_and_subfaces({0, 1, 6, 7}, 4.);

  /* Inserted simplex:        */
  /*    1   6                 */
  /*    o---o                 */
  /*   /X\7/                  */
  /*  o---o---o---o           */
  /*  2   0   3\X/4           */
  /*            o             */
  /*            5             */

  for (auto f_simplex : st.skeleton_simplex_range(3)) {
    std::clog << "vertex = (";
    for (auto vertex : st.simplex_vertex_range(f_simplex)) {
      std::clog << vertex << ",";
    }
    std::clog << ") - filtration = " << st.filtration(f_simplex);
    std::clog << " - dimension = " << st.dimension(f_simplex) << std::endl;
    // Guaranteed by construction
    BOOST_CHECK(st.filtration(f_simplex) >= 2.);
  }

  // dimension until 5 even if simplex tree is of dimension 3 to test the limits
  for(int dimension = 5; dimension >= 0; dimension --) {
    std::clog << "### reset_filtration - dimension = " << dimension << "\n";
    st.reset_filtration(0., dimension);
    for (auto f_simplex : st.skeleton_simplex_range(3)) {
      std::clog << "vertex = (";
      for (auto vertex : st.simplex_vertex_range(f_simplex)) {
        std::clog << vertex << ",";
      }
      std::clog << ") - filtration = " << st.filtration(f_simplex);
      std::clog << " - dimension = " << st.dimension(f_simplex) << std::endl;
      if (st.dimension(f_simplex) < dimension)
        BOOST_CHECK(st.filtration(f_simplex) >= 2.);
      else
        BOOST_CHECK(st.filtration(f_simplex) == 0.);
    }
  }

}

BOOST_AUTO_TEST_CASE_TEMPLATE(simplex_tree_boundaries_and_opposite_vertex_iterator, typeST, list_of_tested_variants) {
  std::clog << "********************************************************************" << std::endl;
  std::clog << "TEST OF BOUNDARIES AND OPPOSITE VERTEX ITERATORS" << std::endl;
  typeST st;

  st.insert_simplex_and_subfaces({2, 1, 0}, 3.);
  st.insert_simplex_and_subfaces({3, 0}, 2.);
  st.insert_simplex_and_subfaces({3, 4, 5}, 3.);
  st.insert_simplex_and_subfaces({0, 1, 6, 7}, 4.);

  /* Inserted simplex:        */
  /*    1   6                 */
  /*    o---o                 */
  /*   /X\7/                  */
  /*  o---o---o---o           */
  /*  2   0   3\X/4           */
  /*            o             */
  /*            5             */
  using Simplex = std::vector<typename typeST::Vertex_handle>;
  // simplices must be kept sorted by vertex number for std::vector to use operator== - cf. last BOOST_CHECK
  std::vector<Simplex> simplices = {{0, 1, 2}, {0, 3}, {0, 1, 6, 7}, {3, 4, 5}, {3, 5}, {2}};
  for (auto simplex : simplices) {
    Simplex opposite_vertices;
    for(auto boundary_and_opposite_vertex : st.boundary_opposite_vertex_simplex_range(st.find(simplex))) {
      Simplex output;
      for (auto vertex : st.simplex_vertex_range(boundary_and_opposite_vertex.first)) {
        std::clog << vertex << " ";
        output.emplace_back(vertex);
      }
      std::clog << " - opposite vertex = " << boundary_and_opposite_vertex.second << std::endl;
      // Check that boundary simplex + opposite vertex = simplex given as input
      output.emplace_back(boundary_and_opposite_vertex.second);
      std::sort(output.begin(), output.end());
      BOOST_CHECK(simplex == output);
      opposite_vertices.emplace_back(boundary_and_opposite_vertex.second);
    }
    // Check that the list of all opposite vertices = simplex given as input
    // no opposite vertices if simplex given as input is of dimension 1
    std::sort(opposite_vertices.begin(), opposite_vertices.end());
    if (simplex.size() > 1)
      BOOST_CHECK(simplex == opposite_vertices);
    else
      BOOST_CHECK(opposite_vertices.size() == 0);
  }
}


/*    This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT.
 *    See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details.
 *    Author(s):       Vincent Rouvreau
 *
 *    Copyright (C) 2014 Inria
 *
 *    Modification(s):
 *      - YYYY/MM Author: Description of the modification
 */

#include <iostream>

#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE "simplex_tree_remove"
#include <boost/test/unit_test.hpp>

//  ^
// /!\ Nothing else from Simplex_tree shall be included to test includes are well defined.
#include "gudhi/Simplex_tree.h"

using namespace Gudhi;

struct MyOptions : Simplex_tree_options_full_featured {
  // Not doing persistence, so we don't need those
  static const bool store_key = false;
  static const bool store_filtration = false;
  // I have few vertices
  typedef short Vertex_handle;
};

using Mini_stree = Simplex_tree<MyOptions>;
using Stree = Simplex_tree<>;

BOOST_AUTO_TEST_CASE(remove_maximal_simplex) {
  std::clog << "********************************************************************" << std::endl;
  std::clog << "REMOVE MAXIMAL SIMPLEX" << std::endl;

  Mini_stree st;

  st.insert_simplex_and_subfaces({0, 1, 6, 7});
  st.insert_simplex_and_subfaces({3, 4, 5});

  // Constructs a copy at this state for further test purpose
  Mini_stree st_pruned = st;

  st.insert_simplex_and_subfaces({3, 0});
  st.insert_simplex_and_subfaces({2, 1, 0});

  // Constructs a copy at this state for further test purpose
  Mini_stree st_complete = st;
  // st_complete and st:
  //    1   6
  //    o---o
  //   /X\7/
  //  o---o---o---o
  //  2   0   3\X/4
  //            o
  //            5
  // st_pruned:
  //    1   6
  //    o---o
  //     \7/
  //      o   o---o
  //      0   3\X/4
  //            o
  //            5

#ifdef GUDHI_DEBUG
  std::clog << "Check exception throw in debug mode" << std::endl;
  // throw excpt because sh has children
  BOOST_CHECK_THROW (st.remove_maximal_simplex(st.find({0, 1, 6})), std::invalid_argument);
  BOOST_CHECK_THROW (st.remove_maximal_simplex(st.find({3})), std::invalid_argument);
  BOOST_CHECK(st == st_complete);
#endif
  std::clog << "st.remove_maximal_simplex({0, 2})" << std::endl;
  st.remove_maximal_simplex(st.find({0, 2}));
  std::clog << "st.remove_maximal_simplex({0, 1, 2})" << std::endl;
  st.remove_maximal_simplex(st.find({0, 1, 2}));
  std::clog << "st.remove_maximal_simplex({1, 2})" << std::endl;
  st.remove_maximal_simplex(st.find({1, 2}));
  std::clog << "st.remove_maximal_simplex({2})" << std::endl;
  st.remove_maximal_simplex(st.find({2}));
  std::clog << "st.remove_maximal_simplex({3})" << std::endl;
  st.remove_maximal_simplex(st.find({0, 3}));

  BOOST_CHECK(st == st_pruned);
  // Remove all, but as the simplex tree is not storing filtration, there is no modification
  st.prune_above_filtration(0.0);
  BOOST_CHECK(st == st_pruned);

  Mini_stree st_wo_seven;

  st_wo_seven.insert_simplex_and_subfaces({0, 1, 6});
  st_wo_seven.insert_simplex_and_subfaces({3, 4, 5});
  // st_wo_seven:
  //    1   6
  //    o---o
  //     \X/
  //      o   o---o
  //      0   3\X/4
  //            o
  //            5

  // Remove all 7 to test the both remove_maximal_simplex cases (when _members is empty or not)
  std::clog << "st.remove_maximal_simplex({0, 1, 6, 7})" << std::endl;
  st.remove_maximal_simplex(st.find({0, 1, 6, 7}));
  std::clog << "st.remove_maximal_simplex({0, 1, 7})" << std::endl;
  st.remove_maximal_simplex(st.find({0, 1, 7}));
  std::clog << "st.remove_maximal_simplex({0, 6, 7})" << std::endl;
  st.remove_maximal_simplex(st.find({0, 6, 7}));
  std::clog << "st.remove_maximal_simplex({0, 7})" << std::endl;
  st.remove_maximal_simplex(st.find({0, 7}));
  std::clog << "st.remove_maximal_simplex({1, 6, 7})" << std::endl;
  st.remove_maximal_simplex(st.find({1, 6, 7}));
  std::clog << "st.remove_maximal_simplex({1, 7})" << std::endl;
  st.remove_maximal_simplex(st.find({1, 7}));
  std::clog << "st.remove_maximal_simplex({6, 7})" << std::endl;
  st.remove_maximal_simplex(st.find({6, 7}));
  std::clog << "st.remove_maximal_simplex({7})" << std::endl;
  st.remove_maximal_simplex(st.find({7}));

  std::clog << "st.upper_bound_dimension()=" << st.upper_bound_dimension() << std::endl;
  BOOST_CHECK(st.upper_bound_dimension() == 3);

  // Check dimension calls lower_upper_bound_dimension to recompute dimension
  BOOST_CHECK(st.dimension() == 2);
  BOOST_CHECK(st.upper_bound_dimension() == 2);

  std::clog << "st.upper_bound_dimension()=" << st.upper_bound_dimension()
            << " | st_wo_seven.upper_bound_dimension()=" << st_wo_seven.upper_bound_dimension() << std::endl;
  std::clog << "st.dimension()=" << st.dimension() << " | st_wo_seven.dimension()=" << st_wo_seven.dimension() << std::endl;
  BOOST_CHECK(st == st_wo_seven);
}

BOOST_AUTO_TEST_CASE(auto_dimension_set) {
  std::clog << "********************************************************************" << std::endl;
  std::clog << "DIMENSION ON REMOVE MAXIMAL SIMPLEX" << std::endl;

  Mini_stree st;

  st.insert_simplex_and_subfaces({0, 1, 2});
  st.insert_simplex_and_subfaces({0, 1, 3});
  st.insert_simplex_and_subfaces({1, 2, 3, 4});
  st.insert_simplex_and_subfaces({1, 2, 3, 5});
  st.insert_simplex_and_subfaces({6, 7, 8, 9});
  st.insert_simplex_and_subfaces({6, 7, 8, 10});

  BOOST_CHECK(st.upper_bound_dimension() == 3);
  BOOST_CHECK(st.dimension() == 3);

  std::clog << "st.remove_maximal_simplex({6, 7, 8, 10})" << std::endl;
  st.remove_maximal_simplex(st.find({6, 7, 8, 10}));
  std::clog << "st.upper_bound_dimension()=" << st.upper_bound_dimension() << std::endl;
  BOOST_CHECK(st.upper_bound_dimension() == 3);
  BOOST_CHECK(st.dimension() == 3);

  std::clog << "st.remove_maximal_simplex({6, 7, 8, 9})" << std::endl;
  st.remove_maximal_simplex(st.find({6, 7, 8, 9}));
  std::clog << "st.upper_bound_dimension()=" << st.upper_bound_dimension() << std::endl;
  BOOST_CHECK(st.upper_bound_dimension() == 3);
  BOOST_CHECK(st.dimension() == 3);

  std::clog << "st.remove_maximal_simplex({1, 2, 3, 4})" << std::endl;
  st.remove_maximal_simplex(st.find({1, 2, 3, 4}));
  std::clog << "st.upper_bound_dimension()=" << st.upper_bound_dimension() << std::endl;
  BOOST_CHECK(st.upper_bound_dimension() == 3);
  BOOST_CHECK(st.dimension() == 3);

  std::clog << "st.remove_maximal_simplex({1, 2, 3, 5})" << std::endl;
  st.remove_maximal_simplex(st.find({1, 2, 3, 5}));
  std::clog << "st.upper_bound_dimension()=" << st.upper_bound_dimension() << std::endl;
  BOOST_CHECK(st.upper_bound_dimension() == 3);
  BOOST_CHECK(st.dimension() == 2);
  std::clog << "st.dimension()=" << st.dimension() << std::endl;

  std::clog << "st.insert_simplex_and_subfaces({1, 2, 3, 5})" << std::endl;
  st.insert_simplex_and_subfaces({1, 2, 3, 5});
  std::clog << "st.upper_bound_dimension()=" << st.upper_bound_dimension() << std::endl;
  BOOST_CHECK(st.upper_bound_dimension() == 3);
  BOOST_CHECK(st.dimension() == 3);

  std::clog << "st.insert_simplex_and_subfaces({1, 2, 3, 4})" << std::endl;
  st.insert_simplex_and_subfaces({1, 2, 3, 4});
  std::clog << "st.upper_bound_dimension()=" << st.upper_bound_dimension() << std::endl;
  BOOST_CHECK(st.upper_bound_dimension() == 3);
  BOOST_CHECK(st.dimension() == 3);

  std::clog << "st.remove_maximal_simplex({1, 2, 3, 5})" << std::endl;
  st.remove_maximal_simplex(st.find({1, 2, 3, 5}));
  std::clog << "st.upper_bound_dimension()=" << st.upper_bound_dimension() << std::endl;
  BOOST_CHECK(st.upper_bound_dimension() == 3);
  BOOST_CHECK(st.dimension() == 3);

  std::clog << "st.remove_maximal_simplex({1, 2, 3, 4})" << std::endl;
  st.remove_maximal_simplex(st.find({1, 2, 3, 4}));
  std::clog << "st.upper_bound_dimension()=" << st.upper_bound_dimension() << std::endl;
  BOOST_CHECK(st.upper_bound_dimension() == 3);
  BOOST_CHECK(st.dimension() == 2);
  std::clog << "st.dimension()=" << st.dimension() << std::endl;

  std::clog << "st.insert_simplex_and_subfaces({0, 1, 3, 4})" << std::endl;
  st.insert_simplex_and_subfaces({0, 1, 3, 4});
  std::clog << "st.upper_bound_dimension()=" << st.upper_bound_dimension() << std::endl;
  BOOST_CHECK(st.upper_bound_dimension() == 3);
  BOOST_CHECK(st.dimension() == 3);

  std::clog << "st.remove_maximal_simplex({0, 1, 3, 4})" << std::endl;
  st.remove_maximal_simplex(st.find({0, 1, 3, 4}));
  std::clog << "st.upper_bound_dimension()=" << st.upper_bound_dimension() << std::endl;
  BOOST_CHECK(st.upper_bound_dimension() == 3);
  BOOST_CHECK(st.dimension() == 2);
  std::clog << "st.dimension()=" << st.dimension() << std::endl;

  std::clog << "st.insert_simplex_and_subfaces({1, 2, 3, 5})" << std::endl;
  st.insert_simplex_and_subfaces({1, 2, 3, 5});
  std::clog << "st.upper_bound_dimension()=" << st.upper_bound_dimension() << std::endl;
  BOOST_CHECK(st.upper_bound_dimension() == 3);
  BOOST_CHECK(st.dimension() == 3);

  std::clog << "st.insert_simplex_and_subfaces({1, 2, 3, 4})" << std::endl;
  st.insert_simplex_and_subfaces({1, 2, 3, 4});
  std::clog << "st.upper_bound_dimension()=" << st.upper_bound_dimension() << std::endl;
  BOOST_CHECK(st.upper_bound_dimension() == 3);
  BOOST_CHECK(st.dimension() == 3);

  // Check you can override the dimension
  // This is a limit test case - shall not happen
  st.set_dimension(1);
  std::clog << "st.upper_bound_dimension()=" << st.upper_bound_dimension() << std::endl;
  BOOST_CHECK(st.upper_bound_dimension() == 1);
  // check dimension() and lower_upper_bound_dimension() is not giving the right answer because dimension is too low
  BOOST_CHECK(st.dimension() == 1);

  // Check you can override the dimension
  // This is a limit test case - shall not happen
  st.set_dimension(6);
  std::clog << "st.upper_bound_dimension()=" << st.upper_bound_dimension() << std::endl;
  BOOST_CHECK(st.upper_bound_dimension() == 6);
  // check dimension() do not launch lower_upper_bound_dimension()
  BOOST_CHECK(st.dimension() == 6);

  // Reset with the correct value
  st.set_dimension(3);
  std::clog << "st.upper_bound_dimension()=" << st.upper_bound_dimension() << std::endl;
  BOOST_CHECK(st.upper_bound_dimension() == 3);
  BOOST_CHECK(st.dimension() == 3);

  std::clog << "st.insert_simplex_and_subfaces({0, 1, 2, 3, 4, 5, 6})" << std::endl;
  st.insert_simplex_and_subfaces({0, 1, 2, 3, 4, 5, 6});
  std::clog << "st.upper_bound_dimension()=" << st.upper_bound_dimension() << std::endl;
  BOOST_CHECK(st.upper_bound_dimension() == 6);
  BOOST_CHECK(st.dimension() == 6);

  std::clog << "st.remove_maximal_simplex({0, 1, 2, 3, 4, 5, 6})" << std::endl;
  st.remove_maximal_simplex(st.find({0, 1, 2, 3, 4, 5, 6}));
  std::clog << "st.upper_bound_dimension()=" << st.upper_bound_dimension() << std::endl;
  BOOST_CHECK(st.upper_bound_dimension() == 6);
  BOOST_CHECK(st.dimension() == 5);

}

BOOST_AUTO_TEST_CASE(prune_above_filtration) {
  std::clog << "********************************************************************" << std::endl;
  std::clog << "PRUNE ABOVE FILTRATION" << std::endl;

  Stree st;

  st.insert_simplex_and_subfaces({0, 1, 6, 7}, 1.0);
  st.insert_simplex_and_subfaces({3, 4, 5}, 2.0);

  // Constructs a copy at this state for further test purpose
  Stree st_pruned = st;
  st_pruned.initialize_filtration();  // reset

  st.insert_simplex_and_subfaces({3, 0}, 3.0);
  st.insert_simplex_and_subfaces({2, 1, 0}, 4.0);

  // Constructs a copy at this state for further test purpose
  Stree st_complete = st;
  // st_complete and st:
  //    1   6
  //    o---o
  //   /X\7/
  //  o---o---o---o
  //  2   0   3\X/4
  //            o
  //            5
  // st_pruned:
  //    1   6
  //    o---o
  //     \7/
  //      o   o---o
  //      0   3\X/4
  //            o
  //            5

  bool simplex_is_changed = false;
  // Check the no action cases
  // greater than initial filtration value
  simplex_is_changed = st.prune_above_filtration(10.0);
  if (simplex_is_changed)
    st.initialize_filtration();
  BOOST_CHECK(st == st_complete);
  BOOST_CHECK(!simplex_is_changed);
  // equal to initial filtration value
  simplex_is_changed = st.prune_above_filtration(6.0);
  if (simplex_is_changed)
    st.initialize_filtration();
  BOOST_CHECK(st == st_complete);
  BOOST_CHECK(!simplex_is_changed);
  // lower than initial filtration value, but still greater than the maximum filtration value
  simplex_is_changed = st.prune_above_filtration(5.0);
  if (simplex_is_changed)
    st.initialize_filtration();
  BOOST_CHECK(st == st_complete);
  BOOST_CHECK(!simplex_is_changed);

  // Display the Simplex_tree
  std::clog << "The complex contains " << st.num_simplices() << " simplices";
  std::clog << " - dimension " << st.dimension() << std::endl;
  std::clog << "Iterator on Simplices in the filtration, with [filtration value]:" << std::endl;
  for (auto f_simplex : st.filtration_simplex_range()) {
    std::clog << "   " << "[" << st.filtration(f_simplex) << "] ";
    for (auto vertex : st.simplex_vertex_range(f_simplex)) {
      std::clog << (int) vertex << " ";
    }
    std::clog << std::endl;
  }

  // Check the pruned cases
  simplex_is_changed = st.prune_above_filtration(2.5);
  if (simplex_is_changed)
    st.initialize_filtration();
  BOOST_CHECK(st == st_pruned);
  BOOST_CHECK(simplex_is_changed);

  // Display the Simplex_tree
  std::clog << "The complex pruned at 2.5 contains " << st.num_simplices() << " simplices";
  std::clog << " - dimension " << st.dimension() << std::endl;

  simplex_is_changed = st.prune_above_filtration(2.0);
  if (simplex_is_changed)
    st.initialize_filtration();

  std::clog << "The complex pruned at 2.0 contains " << st.num_simplices() << " simplices";
  std::clog << " - dimension " << st.dimension() << std::endl;

  BOOST_CHECK(st == st_pruned);
  BOOST_CHECK(!simplex_is_changed);

  Stree st_empty;
  simplex_is_changed = st.prune_above_filtration(0.0);
  BOOST_CHECK(simplex_is_changed == true);
  if (simplex_is_changed)
    st.initialize_filtration();

  // Display the Simplex_tree
  std::clog << "The complex pruned at 0.0 contains " << st.num_simplices() << " simplices";
  std::clog << " - upper_bound_dimension " << st.upper_bound_dimension() << std::endl;
  BOOST_CHECK(st.upper_bound_dimension() == 3);

  BOOST_CHECK(st.dimension() == -1);
  std::clog << "upper_bound_dimension=" << st.upper_bound_dimension() << std::endl;
  BOOST_CHECK(st.upper_bound_dimension() == -1);

  BOOST_CHECK(st == st_empty);
  BOOST_CHECK(simplex_is_changed);

  // Test case to the limit
  simplex_is_changed = st.prune_above_filtration(-1.0);
  if (simplex_is_changed)
    st.initialize_filtration();
  BOOST_CHECK(st == st_empty);
  BOOST_CHECK(!simplex_is_changed);
}

BOOST_AUTO_TEST_CASE(mini_prune_above_filtration) {
  std::clog << "********************************************************************" << std::endl;
  std::clog << "MINI PRUNE ABOVE FILTRATION" << std::endl;

  Mini_stree st;

  st.insert_simplex_and_subfaces({0, 1, 6, 7});
  st.insert_simplex_and_subfaces({3, 4, 5});
  st.insert_simplex_and_subfaces({3, 0});
  st.insert_simplex_and_subfaces({2, 1, 0});

  // st:
  //    1   6
  //    o---o
  //   /X\7/
  //  o---o---o---o
  //  2   0   3\X/4
  //            o
  //            5

  st.initialize_filtration();

  // Display the Simplex_tree
  std::clog << "The complex contains " << st.num_simplices() << " simplices" << std::endl;
  BOOST_CHECK(st.num_simplices() == 27);

  // Test case to the limit - With these options, there is no filtration, which means filtration is 0
  bool simplex_is_changed = st.prune_above_filtration(1.0);
  if (simplex_is_changed)
    st.initialize_filtration();
  // Display the Simplex_tree
  std::clog << "The complex pruned at 1.0 contains " << st.num_simplices() << " simplices" << std::endl;
  BOOST_CHECK(!simplex_is_changed);
  BOOST_CHECK(st.num_simplices() == 27);

  simplex_is_changed = st.prune_above_filtration(0.0);
  if (simplex_is_changed)
    st.initialize_filtration();
  // Display the Simplex_tree
  std::clog << "The complex pruned at 0.0 contains " << st.num_simplices() << " simplices" << std::endl;
  BOOST_CHECK(!simplex_is_changed);
  BOOST_CHECK(st.num_simplices() == 27);

  // Test case to the limit
  simplex_is_changed = st.prune_above_filtration(-1.0);
  if (simplex_is_changed)
    st.initialize_filtration();
  // Display the Simplex_tree
  std::clog << "The complex pruned at -1.0 contains " << st.num_simplices() << " simplices" << std::endl;
  BOOST_CHECK(simplex_is_changed);
  BOOST_CHECK(st.num_simplices() == 0);

  // Display the Simplex_tree
  std::clog << "The complex contains " << st.num_simplices() << " simplices" << std::endl;

}


//  Copyright Paul A. Bristow 2015.
//  Copyright Christopher Kormanyos 2015.
//  Distributed under the Boost Software License,
//  Version 1.0. (See accompanying file LICENSE_1_0.txt
//  or copy at http://www.boost.org/LICENSE_1_0.txt)

//! \file
//!\brief Test numeric_limits lowest for a list of negatable fixed_point types.

//! \sa http://www.boost.org/doc/libs/1_59_0/libs/test/doc/html/boost_test/tests_organization/test_cases/test_organization_templates.html#ref_BOOST_AUTO_TEST_CASE_TEMPLATE

#define BOOST_TEST_MODULE test_negatable_basic_limits
#define BOOST_LIB_DIAGNOSTIC
//#define ENABLE_LOCAL_TEST_DEBUG_MESSAGES

#include <boost/test/included/unit_test.hpp>
#include <boost/mpl/list.hpp> // for mpl::list
#include <boost/fixed_point/fixed_point.hpp>

#include <limits>
#include <iostream>
#include <type_traits>

using namespace boost::fixed_point;

// List of fixed_point types to use in testing (with rationale).
// All have default rounding and default overflow undefined.
// Split are chosen to be as close as possible to analogous IEEE floating-point layout,
// but can't match exactly because that format has an implicit hidden bit for fraction part,
// and exponent part has a sign bit, so use one less than the exponent bits.
// For a start, cover the edge case of all resolution bits, zero range,
// so can only represent a fraction -1..0 .. 0.999....
typedef boost::fixed_point::negatable<0, -7> fixed_point_type_0m7; // 8-bit Fraction only.
typedef boost::fixed_point::negatable<2, -5> fixed_point_type_2m5; // 8-bit split, -4 to +3.999
// 8-bit types.
typedef boost::fixed_point::negatable<0, -15> fixed_point_type_0m15; // 16-bit Fraction only.
typedef boost::fixed_point::negatable<4, -11> fixed_point_type_4m11; // 16-bit split (as IEEE binary16).
// 32-bit types.
typedef boost::fixed_point::negatable<0, -31> fixed_point_type_0m31; // 32-bit Fraction only.
typedef boost::fixed_point::negatable<7, -24> fixed_point_type_7m24; // 32-bit split (as IEEE binary32 float).
// 64-bit types.
typedef boost::fixed_point::negatable<0, -63> fixed_point_type_0m63; // 64-bit Fraction only.
typedef boost::fixed_point::negatable<10, -53> fixed_point_type_10m53; // 64-bit split (as IEEE binary64 double).

// 80-bit type
typedef boost::fixed_point::negatable<15, -64> fixed_point_type_15m64; // 80-bit split (as IEEE X86 extended long double).
// Can't match exactly here as 64th bit is used to show that value is denormal).

// 128-bit types that will use float128 where available, else Boost.Multiprecision.
typedef boost::fixed_point::negatable<0, -127> fixed_point_type_0m127; // 128-bit Fraction only.
typedef boost::fixed_point::negatable<14, -113> fixed_point_type_14m113; // 128-bit split (as IEEE binary128).

// 256-bit types that must use Boost.Multiprecision.
typedef boost::fixed_point::negatable<0, -255> fixed_point_type_0m255; // 128-bit Fraction only.
typedef boost::fixed_point::negatable<15, -240> fixed_point_type_15m240; // 128-bit split (as IEEE binary128).

typedef boost::mpl::list<
  fixed_point_type_0m7,  fixed_point_type_2m5,  // 8-bit types.
  fixed_point_type_0m15, fixed_point_type_4m11, // 16-bit types.
  fixed_point_type_0m31, fixed_point_type_7m24, // 32-bit types.
  fixed_point_type_0m63, fixed_point_type_10m53, // 64-bit types.
  fixed_point_type_15m64, // 80-bit type.
  fixed_point_type_0m127, fixed_point_type_14m113, // 128-bit types.
  fixed_point_type_0m255, fixed_point_type_15m240 // 256-bit types.
  > test_types;

BOOST_AUTO_TEST_CASE_TEMPLATE(lowest_test, T, test_types)
{
  T x = (std::numeric_limits<T>::lowest)();
  T mm = -(std::numeric_limits<T>::max)();

  BOOST_CHECK(x < mm);
  BOOST_CHECK(mm > x);
} // BOOST_AUTO_TEST_CASE_TEMPLATE(lowest_test, T, test_types)

BOOST_AUTO_TEST_CASE_TEMPLATE(min_test, T, test_types)
{
  T z; // Default constructor.
  BOOST_CHECK_EQUAL(z, T(0)); // Check that initial value is zero.

  T m((std::numeric_limits<T>::min)()); // Constructor.
  T x = m; // Use assignment operator.

  std::string s = m.bit_pattern(); // "00...001"
  std::size_t d = s.find('1');
  BOOST_CHECK_EQUAL(d, x.all_bits-1);
} // BOOST_AUTO_TEST_CASE_TEMPLATE(mintest_test, T, test_types)

BOOST_AUTO_TEST_CASE_TEMPLATE(max_test, T, test_types)
{
  T z; // Default constructor.
  BOOST_CHECK_EQUAL(z, T(0)); // Check that initial value is zero.

  T m((std::numeric_limits<T>::max)()); // Constructor.
  T x = m; // Use assignment operator.
  BOOST_CHECK_EQUAL(x, m); // Check assignment operator.
  // This and many other tests should be elsewhere.

  std::string s = m.bit_pattern(); // "00...001"
  std::string e("0");
  e.append(m.all_bits - 1, '1');  // Expected string "011...111.
  BOOST_CHECK_EQUAL(s, e); // Not sign bit.
} // BOOST_AUTO_TEST_CASE_TEMPLATE(maxtest_test, T, test_types)

BOOST_AUTO_TEST_CASE_TEMPLATE(radix_test, T, test_types)
{ // radix always 2 for all fixed_point types.
  BOOST_CHECK_EQUAL(std::numeric_limits<T>::radix, 2);
} //

/*
1>  test_fixed_point_types_limits.cpp
1>  test_fixed_point_types_limits.vcxproj -> J:\Cpp\fixed_point\Debug\test_fixed_point_types_limits.exe
1>
1>  Running 39 test cases...
1>  *** No errors detected

*/