// Copyright (c) 2009-2014 INRIA Sophia-Antipolis (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL$
// $Id$
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
// Author(s)    : Samuel Hornus

#ifndef CGAL_TRIANGULATION_H
#define CGAL_TRIANGULATION_H

#include <CGAL/license/Triangulation.h>

#include <CGAL/disable_warnings.h>

#include <CGAL/internal/Triangulation/utilities.h>
#include <CGAL/Triangulation_data_structure.h>
#include <CGAL/Triangulation_full_cell.h>
#include <CGAL/Triangulation_vertex.h>
#include <CGAL/Iterator_project.h>
#include <CGAL/spatial_sort.h>
#include <CGAL/Dimension.h>
#include <CGAL/iterator.h>
#include <CGAL/Default.h>
#include <CGAL/Random.h>

#include <boost/iterator/filter_iterator.hpp>
#include <CGAL/boost/iterator/transform_iterator.hpp>

namespace CGAL {

// Iterator which iterates over vertex_handle's, but returns a point when
// dereferenced. If the current 
// vertex_handle vh == vh_where_point_should_be_substituted, it returns
// "subtitute_point", otherwise, it returns vh->point()
template<class VertexHandleConstIter>
class Substitute_point_in_vertex_iterator 
{
  typedef typename std::iterator_traits<VertexHandleConstIter>::value_type Vertex_handle;
  typedef typename Vertex_handle::value_type Vertex;
  typedef typename Vertex::Point Point;

public:
  typedef Point const& result_type; // For result_of

  Substitute_point_in_vertex_iterator(
    Vertex_handle vh_where_point_should_be_substituted,
    Point const *subtitute_point)
  : vh_where_point_should_be_substituted_(vh_where_point_should_be_substituted)
  , subtitute_point_(subtitute_point)
  {}

  result_type operator()(Vertex_handle vh) const
  {
    if (vh == vh_where_point_should_be_substituted_) 
      return *subtitute_point_;
    else
      return vh->point();
  }

private:
  Vertex_handle vh_where_point_should_be_substituted_;
  Point const *subtitute_point_;

};


template <  class TriangulationTraits, class TDS_ = Default >
class Triangulation
{
    typedef typename TriangulationTraits::Dimension  Maximal_dimension_;
    typedef typename Default::Get<TDS_, Triangulation_data_structure
                    <   Maximal_dimension_,
                        Triangulation_vertex<TriangulationTraits>,
                        Triangulation_full_cell<TriangulationTraits> >
                        >::type                      TDS;
    typedef Triangulation<TriangulationTraits, TDS_> Self;
    
protected:
    typedef typename TriangulationTraits::Flat_orientation_d Flat_orientation_d;
    typedef typename TriangulationTraits::Construct_flat_orientation_d Construct_flat_orientation_d;
    typedef typename TriangulationTraits::In_flat_orientation_d In_flat_orientation_d;
    
    // Wrapper
    struct Coaffine_orientation_d 
    {
      boost::optional<Flat_orientation_d>* fop;
      Construct_flat_orientation_d cfo;
      In_flat_orientation_d ifo;

      Coaffine_orientation_d(
        boost::optional<Flat_orientation_d>& x, 
        Construct_flat_orientation_d const&y, 
        In_flat_orientation_d const&z)
      : fop(&x), cfo(y), ifo(z) {}
      
      template<class Iter> 
      CGAL::Orientation operator()(Iter a, Iter b) const
      {
        if (*fop)
          return ifo(fop->get(),a,b);
        *fop = cfo(a,b);
        CGAL_assertion(ifo(fop->get(),a,b) == CGAL::POSITIVE);
        return CGAL::POSITIVE;
      }
    };

    void reset_flat_orientation()
    {
      if (current_dimension() == preset_flat_orientation_.first)
      {
        CGAL_assertion(preset_flat_orientation_.second != nullptr);
        flat_orientation_ = *preset_flat_orientation_.second;
      }
      else
        flat_orientation_ = boost::none;
    }

    typedef typename TriangulationTraits::Orientation_d
                                                    Orientation_d;

public:

    typedef TriangulationTraits                     Geom_traits;
    typedef TDS                                     Triangulation_ds;

    typedef typename TDS::Vertex                    Vertex;
    typedef typename TDS::Full_cell                 Full_cell;
    typedef typename TDS::Facet                     Facet;
    typedef typename TDS::Face                      Face;
    typedef typename TDS::Vertex::Point             Point;

    typedef Maximal_dimension_                      Maximal_dimension;

    typedef typename TDS::Vertex_handle             Vertex_handle;
    typedef typename TDS::Vertex_iterator           Vertex_iterator;
    typedef typename TDS::Vertex_const_handle       Vertex_const_handle;
    typedef typename TDS::Vertex_const_iterator     Vertex_const_iterator;

    typedef typename TDS::Full_cell_handle          Full_cell_handle;
    typedef typename TDS::Full_cell_iterator        Full_cell_iterator;
    typedef typename TDS::Full_cell_const_handle    Full_cell_const_handle;
    typedef typename TDS::Full_cell_const_iterator  Full_cell_const_iterator;
    
    typedef typename TDS::Facet_iterator            Facet_iterator;

    typedef typename TDS::size_type                 size_type;
    typedef typename TDS::difference_type           difference_type;

    /// The type of location a new point is found lying on
    enum  Locate_type
    {
          ON_VERTEX = 0 // simplex of dimension 0
        , IN_FACE   = 1 // simplex of dimension in [ 1, |current_dimension()| - 2 ]
        , IN_FACET  = 2 // simplex of dimension |current_dimension()| - 1
        , IN_FULL_CELL  = 3 /// simplex of dimension |current_dimension()|
        , OUTSIDE_CONVEX_HULL = 4
        , OUTSIDE_AFFINE_HULL = 5
    };

    // Finite elements iterators

    class Finiteness_predicate;

    typedef boost::filter_iterator<Finiteness_predicate, Vertex_iterator>
        Finite_vertex_iterator;
    typedef boost::filter_iterator<Finiteness_predicate, Vertex_const_iterator>
        Finite_vertex_const_iterator;
    typedef boost::filter_iterator<Finiteness_predicate, Full_cell_iterator>
        Finite_full_cell_iterator;
    typedef boost::filter_iterator<Finiteness_predicate, Full_cell_const_iterator>
        Finite_full_cell_const_iterator;
    typedef boost::filter_iterator<Finiteness_predicate, Facet_iterator>
        Finite_facet_iterator;

    //Tag to distinguish Delaunay from regular triangulations
    typedef Tag_false                               Weighted_tag;

    // Tag to distinguish periodic triangulations from others
    typedef Tag_false                               Periodic_tag;

protected: // DATA MEMBERS

    Triangulation_ds                            tds_;
    const Geom_traits                           kernel_;
    Vertex_handle                               infinity_;
    mutable std::vector<Oriented_side>          orientations_;
    mutable boost::optional<Flat_orientation_d> flat_orientation_;
    // The user can specify a Flat_orientation_d object to be used for 
    // orienting simplices of a specific dimension 
    // (= preset_flat_orientation_.first)
    // preset_flat_orientation_.first = numeric_limits<int>::max() otherwise)
    std::pair<int, const Flat_orientation_d *>  preset_flat_orientation_;
    // for stochastic walk in the locate() function:
    mutable Random                              rng_;
#ifdef CGAL_TRIANGULATION_STATISTICS
    mutable unsigned long walk_size_;
#endif

protected: // HELPER FUNCTIONS

    typedef CGAL::Iterator_project<
        typename Full_cell::Vertex_handle_const_iterator,
        internal::Triangulation::Point_from_vertex_handle<Vertex_handle, Point>
    > Point_const_iterator;

    Point_const_iterator points_begin(Full_cell_const_handle c) const
        { return Point_const_iterator(c->vertices_begin()); }
    Point_const_iterator points_end(Full_cell_const_handle c) const
        { return Point_const_iterator(c->vertices_end()); }
    Point_const_iterator points_begin(Full_cell_handle c) const
        { return Point_const_iterator(c->vertices_begin()); }
    Point_const_iterator points_end(Full_cell_handle c) const
        { return Point_const_iterator(c->vertices_end()); }

public:

    //       FACETS OPERATIONS

    Full_cell_handle full_cell(const Facet & f) const
    {
        return tds().full_cell(f);
    }

    int index_of_covertex(const Facet & f) const
    {
        return tds().index_of_covertex(f);
    }
    
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - UTILITIES
    
    // A co-dimension 2 sub-simplex. called a Rotor because we can rotate
    // the two "covertices" around the sub-simplex. Useful for traversing the
    // boundary of a hole. NOT DOCUMENTED
    typedef std::tuple<Full_cell_handle, int, int>    Rotor;

    // Commented out because it was causing "internal compiler error" in MSVC
    /*Full_cell_handle full_cell(const Rotor & r) const // NOT DOCUMENTED
    {
        return std::get<0>(r);
    }
    int index_of_covertex(const Rotor & r) const // NOT DOCUMENTED
    {
        return std::get<1>(r);
    }
    int index_of_second_covertex(const Rotor & r) const // NOT DOCUMENTED
    {
        return std::get<2>(r);
    }*/
    Rotor rotate_rotor(Rotor & r) // NOT DOCUMENTED...
    {
        int opposite = std::get<0>(r)->mirror_index(std::get<1>(r));
        Full_cell_handle s = std::get<0>(r)->neighbor(std::get<1>(r));
        int new_second = s->index(std::get<0>(r)->vertex(std::get<2>(r)));
        return Rotor(s, new_second, opposite);
    }
    
    // - - - - - - - - - - - - - - - - - - - - - - - - CREATION / CONSTRUCTORS

    Triangulation(int dim, const Geom_traits &k = Geom_traits())
        : tds_(dim)
        , kernel_(k)
        , infinity_()
        , preset_flat_orientation_((std::numeric_limits<int>::max)(),
                                   (Flat_orientation_d*) nullptr)
        , rng_((long)0)
#ifdef CGAL_TRIANGULATION_STATISTICS
        ,walk_size_(0)
#endif
    {
        clear();
    }

    // With this constructor,
    // the user can specify a Flat_orientation_d object to be used for 
    // orienting simplices of a specific dimension 
    // (= preset_flat_orientation_.first)
    // It it used for by dark triangulations created by DT::remove
    Triangulation(
      int dim,
      const std::pair<int, const Flat_orientation_d *> &preset_flat_orientation, 
      const Geom_traits k = Geom_traits())
        : tds_(dim)
        , kernel_(k)
        , infinity_()
        , preset_flat_orientation_(preset_flat_orientation)
        , rng_((long)0)
#ifdef CGAL_TRIANGULATION_STATISTICS
        ,walk_size_(0)
#endif
    {
        clear();
    }
    
    Triangulation(const Triangulation & t2)
        : tds_(t2.tds_)
        , kernel_(t2.kernel_)
        , infinity_()
        , preset_flat_orientation_((std::numeric_limits<int>::max)(), 
                                   (Flat_orientation_d*) nullptr)
        , rng_(t2.rng_)
#ifdef CGAL_TRIANGULATION_STATISTICS
        ,walk_size_(t2.walk_size_)
#endif
    {
        // We find the vertex at infinity by scanning the vertices of both
        // triangulations. This works because Compact_container guarantees that
        // the vertices in the copy (*this) are stored in the same order as in
        // the original triangulation (t2)
        infinity_ = vertices_begin();
        Vertex_const_iterator inf2 = t2.vertices_begin();
        while( inf2 != t2.infinite_vertex() )
        {
            ++infinity_;
            ++inf2;
        }
        // A full_cell has at most 1 + maximal_dimension() facets:
        orientations_.resize(1 + maximal_dimension());
        // Our coaffine orientation predicates HAS state member variables
        reset_flat_orientation();
    }

    ~Triangulation() {}

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ACCESS FUNCTIONS

    /* These three function are no longer needed since we do not use them anymore
       in the Delaunay_triangulation::remove. *But*, they may reappear in the future
       if we manage to passe the information that flags/TDS_data is available or not
       for marking simplices in Delaunay_triangulation::remove. This would be useful
       to make it a little faster, instead of binary searching if a simplex is marked
       or not... 
    // NOT DOCUMENTED -- 
    bool get_visited(Full_cell_handle s) const
    {
        return tds().get_visited(s);
    }
    // NOT DOCUMENTED -- 
    bool get_visited(Full_cell_const_handle s) const
    {
        return tds().get_visited(s);
    }

    // NOT DOCUMENTED -- 
    void set_visited(Full_cell_handle s, bool b) const
    {
        tds().set_visited(s, b);
    } */

    Coaffine_orientation_d coaffine_orientation_predicate() const
    {
      return Coaffine_orientation_d (
        flat_orientation_, 
        geom_traits().construct_flat_orientation_d_object(), 
        geom_traits().in_flat_orientation_d_object()
      );
    }

    const Triangulation_ds & tds() const
    {
        return tds_;
    }

    Triangulation_ds & tds()
    {
        return tds_;
    }

    const Geom_traits & geom_traits() const
    {
        return kernel_;
    }

    int maximal_dimension() const { return tds().maximal_dimension(); }
    int current_dimension() const { return tds().current_dimension(); }

    bool empty() const
    {
        return current_dimension() == -1;
    }

    size_type number_of_vertices() const
    {
        return tds().number_of_vertices() - 1;
    }

    size_type number_of_full_cells() const
    {
        return tds().number_of_full_cells();
    }

    Vertex_handle infinite_vertex() const
    {
        return infinity_;
    }

    Full_cell_handle infinite_full_cell() const
    {
        CGAL_assertion(infinite_vertex()->full_cell()->has_vertex(infinite_vertex()));
        return infinite_vertex()->full_cell();
    }

// - - - - - - - - - - - - - - - - - - - - - - - - - NON CONSTANT-TIME ACCESS FUNCTIONS

    size_type number_of_finite_full_cells() const
    {
        Full_cell_const_iterator s = full_cells_begin();
        size_type result = number_of_full_cells();
        for( ; s != full_cells_end(); ++s )
        {
            if( is_infinite(s) )
                --result;
        }
        return result;
    }

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - TRAVERSAL

    Vertex_iterator vertices_begin() { return tds().vertices_begin(); }
    Vertex_iterator vertices_end()   { return tds().vertices_end(); }

    Vertex_const_iterator vertices_begin() const { return tds().vertices_begin(); }
    Vertex_const_iterator vertices_end()   const { return tds().vertices_end(); }

    Finite_vertex_iterator finite_vertices_begin()
    { return Finite_vertex_iterator(Finiteness_predicate(*this), vertices_begin(), vertices_end()); }
    Finite_vertex_iterator finite_vertices_end()
    { return Finite_vertex_iterator(Finiteness_predicate(*this), vertices_end(), vertices_end()); }
    Finite_vertex_const_iterator finite_vertices_begin() const
    { return Finite_vertex_const_iterator(Finiteness_predicate(*this), vertices_begin(), vertices_end()); }
    Finite_vertex_const_iterator finite_vertices_end() const
    { return Finite_vertex_const_iterator(Finiteness_predicate(*this), vertices_end(), vertices_end()); }

    Full_cell_iterator full_cells_begin() { return tds().full_cells_begin(); }
    Full_cell_iterator full_cells_end()   { return tds().full_cells_end(); }

    Full_cell_const_iterator full_cells_begin() const { return tds().full_cells_begin(); }
    Full_cell_const_iterator full_cells_end()   const { return tds().full_cells_end(); }

    Finite_full_cell_iterator finite_full_cells_begin()
    { return Finite_full_cell_iterator(Finiteness_predicate(*this), full_cells_begin(), full_cells_end()); }
    Finite_full_cell_iterator finite_full_cells_end()
    { return Finite_full_cell_iterator(Finiteness_predicate(*this), full_cells_end(), full_cells_end()); }
    Finite_full_cell_const_iterator finite_full_cells_begin() const
    { return Finite_full_cell_const_iterator(Finiteness_predicate(*this), full_cells_begin(), full_cells_end()); }
    Finite_full_cell_const_iterator finite_full_cells_end() const
    { return Finite_full_cell_const_iterator(Finiteness_predicate(*this), full_cells_end(), full_cells_end()); }

    Facet_iterator facets_begin() { return tds().facets_begin(); }
    Facet_iterator facets_end() { return tds().facets_end(); }
    Finite_facet_iterator finite_facets_begin()
    { return Finite_facet_iterator(Finiteness_predicate(*this), facets_begin(), facets_end()); }
    Finite_facet_iterator finite_facets_end()
    { return Finite_facet_iterator(Finiteness_predicate(*this), facets_end(), facets_end()); }

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SOME PREDICATE FUNCTORS

    class Finiteness_predicate
    {
        const Self & t_;
    public:
        Finiteness_predicate(const Self & t) : t_(t) {}
        template < class T >
        bool operator()(const T & t) const
        {
            return ! t_.is_infinite(t);
        }
    };

    class Point_equality_predicate
    {
        const Point & o_;
    public:
        Point_equality_predicate(const Point & o) : o_(o) {}
        bool operator()(const Point & o) const { return  (o == o_ );}
    };

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SIMPLE QUERIES
/*
    bool is_vertex(const Point & p, Vertex_handle & v, Full_cell_handle hint = Full_cell_handle()) const
    {
        Locate_type lt;
        Face f(maximal_dimension());
        Facet ft;
        Full_cell_handle s = locate(p, lt, f, ft, hint);
        if( ON_VERTEX == lt )
        {
            v = s->vertex(f.index(0));
            return true;
        }
        return false;
    }

    bool is_vertex(Vertex_const_handle v) const
    {
        return tds().is_vertex(v);
    }

    bool is_full_cell(Full_cell_const_handle s) const
    {
        return tds().is_full_cell(s);
    }
*/

    bool is_infinite(Vertex_const_handle v) const
    {
        CGAL_precondition(Vertex_const_handle() != v);
        return (infinite_vertex() == v);
    }

    bool is_infinite(const Vertex & v) const /* internal use, not documented */
    {
        return (&(*infinite_vertex()) == &v);
    }

    bool is_infinite(Full_cell_const_handle s) const
    {
        CGAL_precondition(Full_cell_const_handle() != s);
        return is_infinite(*s);
    }
    bool is_infinite(const Full_cell & s) const /* internal use, not documented */
    {
        for(int i = 0; i <= current_dimension(); ++i)
            if( is_infinite(s.vertex(i)) )
                return true;
        return false;
    }
    bool is_infinite(const Facet & ft) const
    {
        Full_cell_const_handle s = full_cell(ft);
        CGAL_precondition(s != Full_cell_const_handle());
        if( is_infinite(s) )
            return (s->vertex(index_of_covertex(ft)) != infinite_vertex());
        return false;
    }

    bool is_infinite(const Face & f) const
    {
        Full_cell_const_handle s = f.full_cell();
        CGAL_precondition(s != Full_cell_const_handle());
        if( is_infinite(s) )
        {
            Vertex_handle v;
            for( int i(0); i<= f.face_dimension(); ++i)
                if ( is_infinite( f.vertex(i) )) return true;
        }
        return false;
    }

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ELEMENT GATHERING

    
    template< typename OutputIterator >
    OutputIterator incident_full_cells(const Face & f, OutputIterator out) const
    {
        return tds().incident_full_cells(f, out);
    }
    template< typename OutputIterator >
    OutputIterator incident_full_cells(Vertex_const_handle v, OutputIterator out) const
    {
        return tds().incident_full_cells(v, out);
    }
    template< typename OutputIterator >
    OutputIterator star(const Face & f, OutputIterator out) const
    {
        return tds().star(f, out);
    }

    template< typename OutputIterator >
    OutputIterator incident_faces(Vertex_const_handle v, int d, OutputIterator out) const
    {
        return tds().incident_faces(v, d, out);
    }
    /*
    template< typename OutputIterator, class Comparator >
    OutputIterator incident_upper_faces( Vertex_const_handle v, int d,
                                                OutputIterator out, Comparator cmp = Comparator())
    {
        return tds().incident_upper_faces(v, d, out, cmp);
    }
    template< typename OutputIterator >
    OutputIterator incident_upper_faces( Vertex_const_handle v, int d,
                                                OutputIterator out)
    { // FIXME: uncomment this function, since it uses a comparator specific to
       // *geometric* triangulation (taking infinite vertex into account)
        internal::Triangulation::Compare_vertices_for_upper_face<Self> cmp(*this);
        return tds().incident_upper_faces(v, d, out, cmp);
    }
    */
    Orientation orientation(Full_cell_const_handle s, bool in_is_valid = false) const
    {
        if( ! in_is_valid )
            CGAL_assertion( ! is_infinite(s) );
        if( 0 == current_dimension() )
            return POSITIVE;
        if( current_dimension() == maximal_dimension() )
        {
            Orientation_d ori = geom_traits().orientation_d_object();
            return ori(points_begin(s), points_begin(s) + 1 + current_dimension());
        }
        else
        {
            return coaffine_orientation_predicate()(points_begin(s), points_begin(s) + 1 + current_dimension());
        }
    }

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - UPDATE OPERATIONS

    void clear()
    {
        tds_.clear();
        infinity_ = tds().insert_increase_dimension();
        // A full_cell has at most 1 + maximal_dimension() facets:
        orientations_.resize(1 + maximal_dimension());
        // Our coaffine orientation predicates HAS state member variables
        reset_flat_orientation();
#ifdef CGAL_TRIANGULATION_STATISTICS
        walk_size_ = 0;
#endif
    }

    void set_current_dimension(int d)
    {
        tds().set_current_dimension(d);
    }

    Full_cell_handle new_full_cell()
    { 
        return tds().new_full_cell();
    }

    Vertex_handle new_vertex()
    {
      return tds().new_vertex();
    }

    Vertex_handle new_vertex(const Point & p) 
    {
        return tds().new_vertex(p);
    }

    void set_neighbors(Full_cell_handle s, int i, Full_cell_handle s1, int j)
    {
        tds().set_neighbors(s, i, s1, j);
    }

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - VALIDITY

    bool is_valid(bool = false, int = 0) const;
    bool are_incident_full_cells_valid(Vertex_const_handle, bool = false, int = 0) const;

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - POINT LOCATION

protected:
    template< typename OrientationPredicate >
    Full_cell_handle do_locate(const Point &, Locate_type &, Face &, Facet &,
                               Full_cell_handle start,
                               const OrientationPredicate & o) const;
public:
    Full_cell_handle locate(const Point &, Locate_type &, Face &, Facet &,
                            Full_cell_handle start = Full_cell_handle()) const;
    Full_cell_handle locate(const Point &, Locate_type &, Face &, Facet &,
                            Vertex_handle) const;
    Full_cell_handle locate(const Point & p, Full_cell_handle s = Full_cell_handle()) const;
    Full_cell_handle locate(const Point & p, Vertex_handle v) const;

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - REMOVALS

    Vertex_handle contract_face(const Point &, const Face &);

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - POINT INSERTION

    template< typename ForwardIterator >
    size_type insert(ForwardIterator start, ForwardIterator end)
    {
        size_type n = number_of_vertices();
        std::vector<Point> points(start, end);
        spatial_sort(points.begin(), points.end(), geom_traits());
        Full_cell_handle hint = Full_cell_handle();
        typename std::vector<Point>::const_iterator s = points.begin();
        while( s != points.end() )
        {
            hint = insert(*s++, hint)->full_cell();
        }
        return number_of_vertices() - n;
    }
    Vertex_handle insert(const Point &, Locate_type, const Face &, const Facet &, Full_cell_handle);
    Vertex_handle insert(const Point &, Full_cell_handle start = Full_cell_handle());
    Vertex_handle insert(const Point &, Vertex_handle);
    template< typename ForwardIterator >
    Vertex_handle insert_in_hole(const Point & p, ForwardIterator start, ForwardIterator end, const Facet & ft)
    {
        Emptyset_iterator out;
        return insert_in_hole(p, start, end, ft, out);
    }
    template< typename ForwardIterator, typename OutputIterator >
    Vertex_handle insert_in_hole(const Point & p, ForwardIterator start, ForwardIterator end, const Facet & ft, 
                                 OutputIterator out)
    {
        Vertex_handle v = tds().insert_in_hole(start, end, ft, out);
        v->set_point(p);
        return v;
    }
    Vertex_handle insert_in_face(const Point &, const Face &);
    Vertex_handle insert_in_facet(const Point &, const Facet &);
    Vertex_handle insert_in_full_cell(const Point &, Full_cell_handle);
    Vertex_handle insert_outside_convex_hull_1(const Point &, Full_cell_handle);
    Vertex_handle insert_outside_convex_hull(const Point &, Full_cell_handle);
    Vertex_handle insert_outside_affine_hull(const Point &);

// - - - - - - - - - - - - - - - - - - - - - - - - - - - FACET-TRAVERSAL PREDICATES

    template< typename OrientationPredicate >
    class Outside_convex_hull_traversal_predicate
    {
        Triangulation & t_;
        const Point & p_;
        OrientationPredicate const& ori_;
        int cur_dim_;
    public:
        Outside_convex_hull_traversal_predicate(Triangulation & t, const Point & p,
                                    OrientationPredicate const& ori)
        : t_(t), p_(p), ori_(ori), cur_dim_(t.current_dimension()) {}
        // FUTURE change parameter to const reference
        bool operator()(Facet f) const
        {
            Full_cell_handle s = t_.full_cell(f);
            const int i = t_.index_of_covertex(f);
            Full_cell_handle n = s->neighbor(i);
            if( ! t_.is_infinite(n) )
                return false;
            int inf_v_index = n->index(t_.infinite_vertex());
            n->vertex(inf_v_index)->set_point(p_);
            bool ok = (POSITIVE == ori_(t_.points_begin(n), t_.points_begin(n) + cur_dim_ + 1));
            return ok;
        }
    };

    // make sure all full_cells have positive orientation
    void reorient_full_cells();

protected:
  // This is used in the |remove(v)| member function to manage sets of Full_cell_handles
  template< typename FCH >
  struct Full_cell_set : public std::vector<FCH>
  {
    typedef std::vector<FCH> Base_set;
    using Base_set::begin;
    using Base_set::end;
    void make_searchable()
    {   // sort the full cell handles
      std::sort(begin(), end());
    }
    bool contains(const FCH & fch) const
    {
      return std::binary_search(begin(), end(), fch);
    }
    bool contains_1st_and_not_2nd(const FCH & fst, const FCH & snd) const
    {
      return ( ! contains(snd) ) && ( contains(fst) );
    }
  };

  void display_all_full_cells__debugging() const
  {
    std::cerr << "ALL FULL CELLS:" << std::endl;
    for (Full_cell_const_iterator cit = full_cells_begin() ;
          cit != full_cells_end() ; ++cit )
    {
      std::cerr << std::hex << &*cit << ": ";
      for (int jj = 0 ; jj <= current_dimension() ; ++jj)
        std::cerr << (is_infinite(cit->vertex(jj)) ? 0xFFFFFFFF : (unsigned int)&*cit->vertex(jj)) << " - ";
      std::cerr << std::dec << std::endl;
    }
    std::cerr << std::endl;
  }


}; // Triangulation<...>

// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 

// CLASS MEMBER FUNCTIONS

template < class TT, class TDS >
void
Triangulation<TT, TDS>
::reorient_full_cells()
{
    if( current_dimension() < 1 )
        return;

    Full_cell_iterator sit = full_cells_begin();
    Full_cell_iterator send = full_cells_end();
    for ( ; sit != send ; ++sit)
    {
        if( ! (is_infinite(sit) && (1 == current_dimension())) )
        {
            sit->swap_vertices(current_dimension() - 1, current_dimension());
        }
    }
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
// - - - - - - - - - - - - - - - - - - - - - - - - THE REMOVAL METHODS

template < class TT, class TDS >
typename Triangulation<TT, TDS>::Vertex_handle
Triangulation<TT, TDS>
::contract_face(const Point & p, const Face & f)
{
    CGAL_precondition( ! is_infinite(f) );
    Vertex_handle v = tds().contract_face(f);
    v->set_point(p);
    CGAL_expensive_postcondition_msg(are_incident_full_cells_valid(v), "new point is not where it should be");
    return v;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
// - - - - - - - - - - - - - - - - - - - - - - - - THE INSERTION METHODS

template < class TT, class TDS >
typename Triangulation<TT, TDS>::Vertex_handle
Triangulation<TT, TDS>
::insert(const Point & p, Locate_type lt, const Face & f, const Facet & ft, Full_cell_handle s)
{
    switch( lt )
    {
        case IN_FULL_CELL:
            return insert_in_full_cell(p, s);
            break;
        case OUTSIDE_CONVEX_HULL:
            return insert_outside_convex_hull(p, s);
            break;
        case OUTSIDE_AFFINE_HULL:
            return insert_outside_affine_hull(p);
            break;
        case IN_FACET:
        {
            return insert_in_facet(p, ft);
            break;
        }
        case IN_FACE:
            return insert_in_face(p, f);
            break;
        case ON_VERTEX:
            s->vertex(f.index(0))->set_point(p);
            return s->vertex(f.index(0));
            break;
    }
    CGAL_assertion(false);
    return Vertex_handle();
}

template < class TT, class TDS >
typename Triangulation<TT, TDS>::Vertex_handle
Triangulation<TT, TDS>
::insert(const Point & p, Full_cell_handle start)
{
    Locate_type lt;
    Face f(maximal_dimension());
    Facet ft;
    Full_cell_handle s = locate(p, lt, f, ft, start);
    return insert(p, lt, f, ft, s);
}

template < class TT, class TDS >
typename Triangulation<TT, TDS>::Vertex_handle
Triangulation<TT, TDS>
::insert(const Point & p, Vertex_handle v)
{
    if( Vertex_handle() == v )
        v = infinite_vertex();
    return insert(p, v->full_cell());
}

template < class TT, class TDS >
typename Triangulation<TT, TDS>::Vertex_handle
Triangulation<TT, TDS>
::insert_in_face(const Point & p, const Face & f)
{
    CGAL_precondition( ! is_infinite(f) );
    Vertex_handle v = tds().insert_in_face(f);
    v->set_point(p);
    return v;
}

template < class TT, class TDS >
typename Triangulation<TT, TDS>::Vertex_handle
Triangulation<TT, TDS>
::insert_in_facet(const Point & p, const Facet & ft)
{
    CGAL_precondition( ! is_infinite(ft) );
    Vertex_handle v = tds().insert_in_facet(ft);
    v->set_point(p);
    return v;
}

template < class TT, class TDS >
typename Triangulation<TT, TDS>::Vertex_handle
Triangulation<TT, TDS>
::insert_in_full_cell(const Point & p, Full_cell_handle s)
{
    CGAL_precondition( ! is_infinite(s) );
    Vertex_handle v = tds().insert_in_full_cell(s);
    v->set_point(p);
    return v;
}

// NOT DOCUMENTED...
template < class TT, class TDS >
typename Triangulation<TT, TDS>::Vertex_handle
Triangulation<TT, TDS>
::insert_outside_convex_hull_1(const Point & p, Full_cell_handle s)
{
    // This is a special case for dimension 1, because in that case, the right
    // infinite full_cell is not correctly oriented... (sice its first vertex is the
    // infinite one...
    CGAL_precondition( is_infinite(s) );
    CGAL_precondition( 1 == current_dimension() );
    Vertex_handle v = tds().insert_in_full_cell(s);
    v->set_point(p);
    return v;
}

template < class TT, class TDS >
typename Triangulation<TT, TDS>::Vertex_handle
Triangulation<TT, TDS>
::insert_outside_convex_hull(const Point & p, Full_cell_handle s)
{
    if( 1 == current_dimension() )
    {
        return insert_outside_convex_hull_1(p, s);
    }
    CGAL_precondition( is_infinite(s) );
    CGAL_assertion( current_dimension() >= 2 );
    std::vector<Full_cell_handle> simps;
    simps.reserve(64);
    std::back_insert_iterator<std::vector<Full_cell_handle> > out(simps);
    if( current_dimension() < maximal_dimension() )
    {
        Coaffine_orientation_d ori = coaffine_orientation_predicate();
        Outside_convex_hull_traversal_predicate<Coaffine_orientation_d>
            ochtp(*this, p, ori);
        tds().gather_full_cells(s, ochtp, out);
    }
    else
    {
        Orientation_d ori = geom_traits().orientation_d_object();
        Outside_convex_hull_traversal_predicate<Orientation_d>
            ochtp(*this, p, ori);
        tds().gather_full_cells(s, ochtp, out);
    }
    int inf_v_index = s->index(infinite_vertex());
    Vertex_handle v = insert_in_hole(
      p, simps.begin(), simps.end(), Facet(s, inf_v_index));
    return v;
}

template < class TT, class TDS >
typename Triangulation<TT, TDS>::Vertex_handle
Triangulation<TT, TDS>
::insert_outside_affine_hull(const Point & p)
{
    CGAL_precondition( current_dimension() < maximal_dimension() );
    Vertex_handle v = tds().insert_increase_dimension(infinite_vertex());
    // reset the orientation predicate:
    reset_flat_orientation();
    v->set_point(p);
    if( current_dimension() >= 1 )
    {
        Full_cell_handle inf_v_cell = infinite_vertex()->full_cell();
        int inf_v_index = inf_v_cell->index(infinite_vertex());
        Full_cell_handle s = inf_v_cell->neighbor(inf_v_index);
        Orientation o = orientation(s);
        CGAL_assertion( COPLANAR != o );
            if( NEGATIVE == o )
                reorient_full_cells();

            
        // We just inserted the second finite point and the right infinite
        // cell is like : (inf_v, v), but we want it to be (v, inf_v) to be
        // consistent with the rest of the cells
        if (current_dimension() == 1)
        {
            // Is "inf_v_cell" the right infinite cell? 
            // Then inf_v_index should be 1
            if (inf_v_cell->neighbor(inf_v_index)->index(inf_v_cell) == 0 
                && inf_v_index == 0)
            {
                inf_v_cell->swap_vertices(
                    current_dimension() - 1, current_dimension());
            }
            // Otherwise, let's find the right infinite cell
            else
            {
                inf_v_cell = inf_v_cell->neighbor((inf_v_index + 1) % 2);
                inf_v_index = inf_v_cell->index(infinite_vertex());
                // Is "inf_v_cell" the right infinite cell? 
                // Then inf_v_index should be 1
                if (inf_v_cell->neighbor(inf_v_index)->index(inf_v_cell) == 0 
                    && inf_v_index == 0)
                {
                    inf_v_cell->swap_vertices(
                        current_dimension() - 1, current_dimension());
                }
            }
        }
    }
    return v;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
// - - - - - - - - - - - - - - - - - - - - THE MAIN LOCATE(...) FUNCTION

template < class TT, class TDS >
template< typename OrientationPredicate >
typename Triangulation<TT, TDS>::Full_cell_handle
Triangulation<TT, TDS>
::do_locate(const Point & p, // query point
            Locate_type & loc_type,// type of result (full_cell, face, vertex)
            Face & face,// the face containing the query in its interior (when appropriate)
            Facet & facet,// the facet containing the query in its interior (when appropriate)
            Full_cell_handle start, // starting full_cell for the walk
            OrientationPredicate const& orientation_pred
        ) const
{
    const int cur_dim = current_dimension();

    if( cur_dim == -1 )
    {
        loc_type = OUTSIDE_AFFINE_HULL;
        return Full_cell_handle();
    }
    else if( cur_dim == 0 )
    {
        Vertex_handle vit = infinite_full_cell()->neighbor(0)->vertex(0);
        if( EQUAL != geom_traits().compare_lexicographically_d_object()(p, vit->point()) )
        {
            loc_type = OUTSIDE_AFFINE_HULL;
            return Full_cell_handle();
        }
        else
        {
            loc_type = ON_VERTEX;
            face.set_full_cell(vit->full_cell());
            face.set_index(0, 0);
            return vit->full_cell();
        }
    }

    Full_cell_handle s;

    // if we don't know where to start, we start from any bounded full_cell
    if( Full_cell_handle() == start )
    {
        // THE HACK THAT NOBODY SHOULD DO... BUT DIFFICULT TO WORK AROUND
        // THIS... TODO: WORK AROUND IT
        Full_cell_handle inf_c = const_cast<Self*>(this)->infinite_full_cell();
        int inf_v_index = inf_c->index(infinite_vertex());
        s = inf_c->neighbor(inf_v_index);
    }
    else
    {
        s = start;
        if( is_infinite(s) )
        {
            int inf_v_index = s->index(infinite_vertex());
            s = s->neighbor(inf_v_index);
        }
    }

    // Check if query |p| is outside the affine hull
    if( cur_dim < maximal_dimension() )
    {
        if( ! geom_traits().contained_in_affine_hull_d_object()(
            points_begin(s),
            points_begin(s) + current_dimension() + 1,
            p) )
        {
            loc_type = OUTSIDE_AFFINE_HULL;
            return Full_cell_handle();
        }
    }

    // we remember the |previous|ly visited full_cell to avoid the evaluation
    // of one |orientation| predicate
    Full_cell_handle previous = Full_cell_handle();
    bool full_cell_not_found = true;
    while(full_cell_not_found) // we walk until we locate the query point |p|
    {
    #ifdef CGAL_TRIANGULATION_STATISTICS
        ++walk_size_;
    #endif
        // For the remembering stochastic walk, we need to start trying
        // with a random index:
        int j, i = rng_.get_int(0, cur_dim);
        // we check |p| against all the full_cell's hyperplanes in turn
       
        for(j = 0; j <= cur_dim; ++j, i = (i + 1) % (cur_dim + 1) )
        {
            Full_cell_handle next = s->neighbor(i);
            if( previous == next )
            {   // no need to compute the orientation, we already know it
                orientations_[i] = POSITIVE;
                continue; // go to next full_cell's facet
            }

            Substitute_point_in_vertex_iterator<
              typename Full_cell::Vertex_handle_const_iterator> 
              spivi(s->vertex(i), &p);

            orientations_[i] = orientation_pred(
              boost::make_transform_iterator(s->vertices_begin(), spivi),
              boost::make_transform_iterator(s->vertices_begin() + cur_dim + 1, 
                                             spivi));

            if( orientations_[i] != NEGATIVE )
            {
                // from this facet's point of view, we are inside the
                // full_cell or on its boundary, so we continue to next facet
                continue;
            }

            // At this point, we know that we have to jump to the |next|
            // full_cell because orientation_[i] == NEGATIVE
            previous = s;
            s = next;
            if( is_infinite(next) )
            {   // we have arrived OUTSIDE the convex hull of the triangulation,
                // so we stop the search
                full_cell_not_found = false;
                loc_type = OUTSIDE_CONVEX_HULL;
                face.set_full_cell(s);
            }
            break;
        } // end of the 'for' loop
        if( ( cur_dim + 1 ) == j ) // we found the full_cell containing |p|
            full_cell_not_found = false;
    }
    // Here, we know in which full_cell |p| is in.
    // We now check more precisely where |p| landed:
    // vertex, facet, face or full_cell.
    if( ! is_infinite(s) )
    {
        face.set_full_cell(s);
        int num(0);
        int verts(0);
        for(int i = 0; i < cur_dim; ++i)
        {
            if( orientations_[i] == COPLANAR )
            {
                ++num;
                facet = Facet(s, i);
            }
            else
                face.set_index(verts++, i);
        }
        //-- We could put the if{}else{} below in the loop above, but then we would
        // need to test if (verts < cur_dim) many times... we do it only once
        // here:
        if( orientations_[cur_dim] == COPLANAR )
        {
            ++num;
            facet = Facet(s, cur_dim);
        }
        else if( verts < cur_dim )
            face.set_index(verts, cur_dim);
        //-- end of remark above //
        if( 0 == num )
        {
            loc_type = IN_FULL_CELL;
            face.clear();
        }
        else if( cur_dim == num )
            loc_type = ON_VERTEX;
        else if( 1 == num )
            loc_type = IN_FACET;
        else
            loc_type = IN_FACE;
    }
    return s;
}

template < class TT, class TDS >
typename Triangulation<TT, TDS>::Full_cell_handle
Triangulation<TT, TDS>
::locate(   const Point & p, // query point
            Locate_type & loc_type,// type of result (full_cell, face, vertex)
            Face & face,// the face containing the query in its interior (when appropriate)
            Facet & facet,// the facet containing the query in its interior (when appropriate)
            Full_cell_handle start// starting full_cell for the walk
        ) const
{
    if( current_dimension() == maximal_dimension() )
    {
        Orientation_d ori = geom_traits().orientation_d_object();
        return do_locate(p, loc_type, face, facet, start, ori);
    }
    else
        return do_locate(p, loc_type, face, facet, start, coaffine_orientation_predicate());
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
// - - - - - - - - - - - - - - - - - - - - the locate(...) variants

template < class TT, class TDS >
typename Triangulation<TT, TDS>::Full_cell_handle
Triangulation<TT, TDS>
::locate(   const Point & p,
            Locate_type & loc_type,
            Face & face,
            Facet & facet,
            Vertex_handle start) const
{
    if( Vertex_handle() == start )
        start = infinite_vertex();
    return locate(p, loc_type, face, facet, start->full_cell());
}

template < class TT, class TDS >
typename Triangulation<TT, TDS>::Full_cell_handle
Triangulation<TT, TDS>
::locate(const Point & p, Full_cell_handle s) const
{
    Locate_type lt;
    Face face(maximal_dimension());
    Facet facet;
    return locate(p, lt, face, facet, s);
}

template < class TT, class TDS >
typename Triangulation<TT, TDS>::Full_cell_handle
Triangulation<TT, TDS>
::locate(const Point & p, Vertex_handle v) const
{
    if( Vertex_handle() != v )
        v = infinite_vertex();
    return this->locate(p, v->full_cell());
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - VALIDITY

template < class TT, class TDS >
bool
Triangulation<TT, TDS>
::is_valid(bool verbose, int level) const
{ 
    if( ! tds().is_valid(verbose, level) )
        return false;

    Full_cell_const_iterator c;
    if( current_dimension() < 0 )
        return true;
    Orientation o;
    for( c = full_cells_begin(); c != full_cells_end(); ++c )
    {
        if( is_infinite(c) )
        {
            if( current_dimension() > 1 )
            {
                int i = c->index( infinite_vertex() );
                Full_cell_handle n = c->neighbor(i);
                infinite_vertex()->set_point(n->vertex(c->mirror_index(i))->point());
                o = - orientation(c, true);
            }
            else
                o = POSITIVE;
        }
        else
            o = orientation(c, true);
        if( NEGATIVE == o )
        {
            if( verbose ) CGAL_warning_msg(false, "full_cell is not correctly oriented");
            return false;
        }
        if( COPLANAR == o )
        {
            if( verbose ) CGAL_warning_msg(false, "full_cell is flat");
            return false;
        }
    }
    return true;
}

template < class TT, class TDS >
bool Triangulation<TT, TDS>::are_incident_full_cells_valid(Vertex_const_handle v, bool verbose, int) const
{
    if( current_dimension() <= 0 )
        return true;
    typedef std::vector<Full_cell_const_handle> Simps;
    Simps simps;
    simps.reserve(64);
    std::back_insert_iterator<Simps> out(simps);
    incident_full_cells(v, out);
    typename Simps::const_iterator sit = simps.begin();
    for( ; sit != simps.end(); ++sit )
    {
        if( is_infinite(*sit) )
            continue;
        Orientation o = orientation(*sit);
        if( NEGATIVE == o )
        {
            if( verbose ) CGAL_warning_msg(false, "full_cell is not correctly oriented");
            return false;
        }
        if( COPLANAR == o )
        {
            if( verbose ) CGAL_warning_msg(false, "full_cell is flat");
            return false;
        }
    }
    return true;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

// FUNCTIONS THAT ARE NOT MEMBER FUNCTIONS:

template < class TT, class TDS >
std::istream & 
operator>>(std::istream & is, Triangulation<TT, TDS> & tr)
  // reads :
  // - the dimensions (maximal and current)
  // - the number of finite vertices
  // - the non combinatorial information on vertices (point, etc)
  // - the number of full_cells
  // - the full_cells by the indices of their vertices in the preceding list
  // of vertices, plus the non combinatorial information on each full_cell
  // - the neighbors of each full_cell by their index in the preceding list
{
    typedef Triangulation<TT, TDS>            T;
    typedef typename T::Vertex_handle         Vertex_handle;

    // read current dimension and number of vertices
    size_t n;
    int cd;
    if( is_ascii(is) )
        is >> cd >> n;
    else
    {
        read(is, cd);
        read(is, n);
    }

    CGAL_assertion_msg( cd <= tr.maximal_dimension(), "input Triangulation has too high dimension");

    tr.clear();
    tr.set_current_dimension(cd);

    if( n == 0 )
        return is;

    std::vector<Vertex_handle> vertices;
    vertices.resize(n+1);
    vertices[0] = tr.infinite_vertex();
    is >> (*vertices[0]);

    // read the vertices:
    size_t i(1);
    while( i <= n )
    {
        vertices[i] = tr.new_vertex();
        is >> (*vertices[i]); // read a vertex
        ++i;
    }

    // now, read the combinatorial information
   return tr.tds().read_full_cells(is, vertices);
}

template < class TT, class TDS >
std::ostream & 
operator<<(std::ostream & os, const Triangulation<TT, TDS> & tr)
  // writes :
  // - the dimensions (maximal and current)
  // - the number of finite vertices
  // - the non combinatorial information on vertices (point, etc)
  // - the number of full_cells
  // - the full_cells by the indices of their vertices in the preceding list
  // of vertices, plus the non combinatorial information on each full_cell
  // - the neighbors of each full_cell by their index in the preceding list
{
    typedef Triangulation<TT, TDS> T;
    typedef typename T::Vertex_const_handle         Vertex_handle;
    typedef typename T::Vertex_const_iterator       Vertex_iterator;

    // outputs dimensions and number of vertices
    size_t n = tr.number_of_vertices();
    if( is_ascii(os) )
        os << tr.current_dimension() << std::endl << n << std::endl;
    else
    {
        write(os, tr.current_dimension());
        write(os, n);
    }

    if( n == 0 )
        return os;

    int i = 0;
    // write the vertices
    std::map<Vertex_handle, int> index_of_vertex;

    // infinite vertex has index 0 (among all the vertices)
    index_of_vertex[tr.infinite_vertex()] = i++;
    if(is_ascii(os))
      os << *tr.infinite_vertex() <<"\n";
    else
      write(os, *tr.infinite_vertex());
       
    for( Vertex_iterator it = tr.vertices_begin(); it != tr.vertices_end(); ++it )
    {
        if( tr.is_infinite(it) )
            continue;
        if(is_ascii(os))
          os << *it <<"\n"; // write the vertex
        else
          write(os, *it);
        index_of_vertex[it] = i++;
    }
    CGAL_assertion( size_t(i) == n+1 );

    // output the combinatorial information
    return tr.tds().write_full_cells(os, index_of_vertex);
}

} //namespace CGAL

#include <CGAL/enable_warnings.h>

#endif // CGAL_TRIANGULATION_H
