#include <Rcpp.h>
// Copyright (c) 1997  INRIA Sophia-Antipolis (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL: https://github.com/CGAL/cgal/blob/v6.0.1/Triangulation_2/include/CGAL/Constrained_triangulation_2.h $
// $Id: include/CGAL/Constrained_triangulation_2.h 50cfbde3b84 $
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
//
// Author(s)     : Mariette Yvinec, Jean-Daniel Boissonnat


#ifndef CGAL_CONSTRAINED_TRIANGULATION_2_H
#define CGAL_CONSTRAINED_TRIANGULATION_2_H

#include <CGAL/license/Triangulation_2.h>

#include <CGAL/disable_warnings.h>

#include <set>
#include <exception>
#include <type_traits>

#include <CGAL/assertions.h>
#include <CGAL/Triangulation_2.h>
#include <CGAL/Constrained_triangulation_face_base_2.h>
#include <CGAL/iterator.h>
#include <CGAL/Default.h>
#include <CGAL/intersections.h>
#include <CGAL/squared_distance_2.h>
#include <CGAL/tags.h>
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Exact_rational.h>
#include <CGAL/Kernel_23/internal/Has_boolean_tags.h>

#include <boost/mpl/has_xxx.hpp>
#include <boost/iterator/filter_iterator.hpp>

namespace CGAL {

struct No_constraint_intersection_tag{};
struct No_constraint_intersection_requiring_constructions_tag{};
struct Exact_intersections_tag{}; // to be used with an exact number type
struct Exact_predicates_tag{}; // to be used with filtered exact number

// This was deprecated and replaced by ` No_constraint_intersection_tag` and `No_constraint_intersection_requiring_constructions_tag`
// due to an inconsistency between the code and the documentation.
struct CGAL_DEPRECATED No_intersection_tag :
  public No_constraint_intersection_requiring_constructions_tag
{ };

namespace internal {

#ifdef CGAL_CDT_2_DEBUG_INTERSECTIONS
  struct Indentation_level {
    int n;
    Indentation_level() : n(0) {}
    friend std::ostream& operator<<(std::ostream& os, Indentation_level level) {
      return os << std::setw(2) << level.n << ": " << std::string(2*level.n, ' ');
    }
    Indentation_level& operator++() { ++n; return *this; }
    Indentation_level& operator--() { --n; return *this; }
    struct Exit_guard {
      Exit_guard(Indentation_level& level): level(level) { ++level; }
      Exit_guard(const Exit_guard& other) : level(other.level) { ++level; }
      Indentation_level& level;
      ~Exit_guard() { --level; }
    };
    Exit_guard open_new_scope() { return Exit_guard(*this); }
  } cdt_2_indent_level;
#endif // CGAL_CDT_2_DEBUG_INTERSECTIONS

template <typename K>
struct Itag {
  using Is_exact = typename Algebraic_structure_traits<typename K::FT>::Is_exact;
  typedef std::conditional_t<Is_exact::value,
                             Exact_intersections_tag,
                             Exact_predicates_tag> type;
};

} // namespace internal

template < class Gt,
           class Tds_ = Default ,
           class Itag_ = Default >
class Constrained_triangulation_2
  : public Triangulation_2<Gt,typename Default::Get< Tds_,
                                                     Triangulation_data_structure_2 <
                                                       Triangulation_vertex_base_2<Gt>,
                                                       Constrained_triangulation_face_base_2<Gt> > >::type >
{

public:
  typedef typename Default::Get<Tds_, Triangulation_data_structure_2 <
                                        Triangulation_vertex_base_2<Gt>,
                                        Constrained_triangulation_face_base_2<Gt> > >::type Tds;

  typedef typename Default::Get<Itag_, No_constraint_intersection_requiring_constructions_tag>::type Itag;

  typedef Triangulation_2<Gt,Tds> Triangulation;
  typedef Constrained_triangulation_2<Gt,Tds_,Itag_>  Constrained_triangulation;

  typedef typename Triangulation::Edge Edge;
  typedef typename Triangulation::Vertex Vertex;
  typedef typename Triangulation::Vertex_handle Vertex_handle;
  typedef typename Triangulation::Face_handle Face_handle;
  typedef typename Triangulation::size_type size_type;
  typedef typename Triangulation::Locate_type Locate_type;
  typedef typename Triangulation::All_faces_iterator All_faces_iterator;
  typedef typename Triangulation::Finite_edges_iterator Finite_edges_iterator;
  typedef typename Triangulation::Face_circulator Face_circulator;
  typedef typename Triangulation::Edge_circulator Edge_circulator;
  typedef typename Triangulation::Vertex_circulator Vertex_circulator;
  typedef typename Triangulation::Line_face_circulator Line_face_circulator;

  struct Is_constrained {
    const Constrained_triangulation& ct;

    Is_constrained(const Constrained_triangulation& ct)
      : ct(ct)
    {}

    template <typename E>
    bool operator()(const E& e) const
    {
      return ct.is_constrained(e);
    }
  };


  class Intersection_of_constraints_exception : public std::exception
  {
    const char* what() const throw ()
    {
      return "Unauthorized intersections of constraints";
    }
  };

  typedef boost::filter_iterator<Is_constrained,
                                 typename Triangulation::All_edges_iterator>
                                                     Constrained_edges_iterator;

  typedef Iterator_range<Constrained_edges_iterator> Constrained_edges;

#ifndef CGAL_CFG_USING_BASE_MEMBER_BUG_2
  using Triangulation::number_of_vertices;
  using Triangulation::cw;
  using Triangulation::ccw;
  using Triangulation::dimension;
  using Triangulation::geom_traits;
  using Triangulation::all_faces_begin;
  using Triangulation::all_faces_end;
  using Triangulation::finite_edges_begin;
  using Triangulation::finite_edges_end;
  using Triangulation::side_of_oriented_circle;
  using Triangulation::is_infinite;
  using Triangulation::collinear_between;
  using Triangulation::incident_edges;
  using Triangulation::test_dim_down;
  using Triangulation::make_hole;
  using Triangulation::fill_hole;
  using Triangulation::delete_vertex;
  using Triangulation::delete_face;
  using Triangulation::create_face;
  using Triangulation::incident_faces;
  using Triangulation::locate;
  using Triangulation::includes_edge;
  using Triangulation::remove_first;
  using Triangulation::remove_second;
  using Triangulation::is_valid;
  using Triangulation::all_edges_begin;
  using Triangulation::all_edges_end;
  using Triangulation::mirror_index;
  using Triangulation::mirror_edge;
  using Triangulation::orientation;
#endif

  typedef Gt                                 Geom_traits;
  typedef Itag                               Intersection_tag;
  typedef typename Geom_traits::Point_2      Point;
  typedef typename Geom_traits::Segment_2    Segment;
  typedef std::pair<Point,Point>             Constraint;
  typedef std::list<Edge>                    List_edges;
  typedef std::list<Face_handle>             List_faces;
  typedef std::list<Vertex_handle>           List_vertices;
  typedef std::list<Constraint>              List_constraints;

  // Tag to mark the presence of a hierarchy of constraints
  typedef Tag_false                          Constraint_hierarchy_tag;

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

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

  Constrained_triangulation_2(const Gt& gt = Gt()) : Triangulation(gt) { }

  Constrained_triangulation_2(std::list<Constraint>& lc, const Gt& gt=Gt())
      : Triangulation_2<Gt,Tds>(gt)
  {
    typename List_constraints::iterator lcit=lc.begin();
    for( ;lcit != lc.end(); lcit++) {
      insert( (*lcit).first, (*lcit).second);
    }
     CGAL_postcondition( is_valid() );
  }

  template<class InputIterator>
  Constrained_triangulation_2(InputIterator it,
                              InputIterator last,
                              const Gt& gt=Gt() )
     : Triangulation_2<Gt,Tds>(gt)
  {
    for ( ; it != last; it++) {
              insert_constraint((*it).first, (*it).second);
      }
      CGAL_postcondition( is_valid() );
  }

  //TODO Is that destructor correct ?
  virtual ~Constrained_triangulation_2() {}

  // Ensure rule-of-five: define the copy- and move- constructors
  // as well as the copy- and move- assignment operators.
  Constrained_triangulation_2(const Constrained_triangulation_2 &) = default;
  Constrained_triangulation_2(Constrained_triangulation_2 &&) = default;

  Constrained_triangulation_2 &
  operator=(const Constrained_triangulation_2 &) = default;

  Constrained_triangulation_2 &
  operator=(Constrained_triangulation_2 &&) = default;


  Constrained_edges_iterator constrained_edges_begin() const
  {
    Is_constrained pred(*this);
    return Constrained_edges_iterator(pred,
                                      all_edges_begin(),
                                      all_edges_end());
  }

  Constrained_edges_iterator constrained_edges_end() const
  {
    Is_constrained pred(*this);
    return Constrained_edges_iterator(pred,
                                      all_edges_end(),
                                      all_edges_end());
  }

  Constrained_edges constrained_edges() const
  {
    return Constrained_edges(constrained_edges_begin(),constrained_edges_end());
  }

  // INSERTION
  Vertex_handle insert(const Point& p,
                               Face_handle start = Face_handle() );
  Vertex_handle insert(const Point& p,
                       Locate_type lt,
                       Face_handle loc,
                       int li );
  Vertex_handle push_back(const Point& a);
//   template < class InputIterator >
//   std::ptrdiff_t insert(InputIterator first, InputIterator last);



  // The iterator range [edge_it, edge_pteit) is a set of edges
  // that delimit areas in the triangulation.
  // This function writes the face handles that cover this
  // area to the output iterator faces_it
  template <class InputIterator, class OutputIterator>
  void
  get_bounded_faces(InputIterator edge_it, InputIterator edge_pteit,
                    OutputIterator faces_it) const
  {
    Edge e;
    Unique_hash_map<Face_handle,bool> visited;
    std::stack<Face_handle> st;

    for(; edge_it != edge_pteit; edge_it++){
      e = *edge_it;
      visited[e.first] = true;
      st.push(e.first->neighbor(e.second));
    }

    while(! st.empty()){
      Face_handle fh = st.top();
      st.pop();
      typename CGAL::Unique_hash_map<Face_handle, bool>::Data& data = visited[fh];
      if(! data) {
        data = true;
        *faces_it++ = fh;
        for(int i = 0 ; i < 3 ; ++i){
          Face_handle n = fh->neighbor(i);
          if(! visited[n]) {
            st.push(n);
          }
        }
      }
    }

  }

#if 1
  template <class Segment_2>
  static decltype(auto) get_source(const Segment_2& segment){
    return segment[0];
  }
  template <class Segment_2>
  static decltype(auto) get_target(const Segment_2& segment){
    return segment[1];
  }

  static const Point& get_source(const Constraint& cst){
    return cst.first;
  }
  static const Point& get_target(const Constraint& cst){
    return cst.second;
  }
#endif

  // A version of insert_constraint, that additionally writes
  // the new faces in an output iterator
  // We duplicate code, as to run this one with an Emptyset_iterator
  // is still too much overhead
template <class OutputIterator>
void
insert_constraint(Vertex_handle  vaa, Vertex_handle vbb, OutputIterator out)
// forces the constrained [vaa,vbb]
// [vaa,vbb] will potentially be split into several edges
// if a vertex vc of t lies on segment ab
// of if ab intersect some constrained edges
{
  CGAL_precondition( vaa != vbb);
  Vertex_handle vi;

  Face_handle fr;
  int i;
  if(includes_edge(vaa,vbb,vi,fr,i))
  {
    // if the segment (or a subpart of the segment) that we are trying to constraint is already
    // present in the triangulation and is already marked as constrained,
    // then this is an intersection
    if constexpr (std::is_same_v<Itag, No_constraint_intersection_tag>) {
      if(fr->is_constrained(dimension() == 1 ? 2 : i)) {
        throw Intersection_of_constraints_exception();
      }
    }

    mark_constraint(fr,i);
    if (vi != vbb)  {
      insert_constraint(vi,vbb,out);
    }
    return;
  }

  List_faces intersected_faces;
  List_edges conflict_boundary_ab, conflict_boundary_ba;

  bool intersection  = find_intersected_faces( vaa, vbb,
                                               intersected_faces,
                                               conflict_boundary_ab,
                                               conflict_boundary_ba,
                                               vi);
  if ( intersection) {
    if (vi != vaa && vi != vbb) {
      insert_constraint(vaa,vi,out);
      insert_constraint(vi,vbb,out);
     }
    else {
      insert_constraint(vaa,vbb,out);
    }
    return;
  }
  List_edges edges(conflict_boundary_ab);
  std::copy(conflict_boundary_ba.begin(), conflict_boundary_ba.end(), std::back_inserter(edges));

  typename List_edges::iterator last = edges.end();
  --last;
  for(typename List_edges::iterator it = edges.begin();
      it != last;
      ){
    typename List_edges::iterator n = it;
    ++n;
    if(it->first == n->first->neighbor(n->second) ){
      // remove dangling edge
      edges.erase(it);
      it = n;
      ++n;
      edges.erase(it);
    }
    it = n;
  }
  triangulate_hole(intersected_faces,
                   conflict_boundary_ab,
                   conflict_boundary_ba);

  get_bounded_faces(edges.begin(),
                    edges.end(),
                    out);

  if (vi != vbb) {
    insert_constraint(vi,vbb,out);
  }
  return;

}

  void insert_constraint(const Point& a, const Point& b);
  void insert_constraint(Vertex_handle va, Vertex_handle  vb);
  void push_back(const Constraint& c);


  template <class PointIterator>
  void insert_constraint(PointIterator first, PointIterator last, bool close=false)
  {
    if(first == last){
      return;
    }
    const Point& p0 = *first;
    Point p = p0;
    Vertex_handle v0 = insert(p0), v(v0), w(v0);
    Face_handle hint = v0->face();
    ++first;
    for(; first!=last; ++first){
      const Point& q = *first;
      if(p != q){
        w = insert(q,hint);
        insert_constraint(v,w);
        hint = w->face();
        v = w;
        p = q;
      }
    }
    if(close && (p != p0)){
      insert(w,v0);
    }
  }

  void remove(Vertex_handle  v);
  void remove_constrained_edge(Face_handle f, int i);
  void remove_incident_constraints(Vertex_handle  v);
  // to be used by Constrained_triangulation_plus_2
 template <class OutputItFaces>
 OutputItFaces
 remove_constrained_edge(Face_handle f, int i, OutputItFaces out)
 {
   remove_constrained_edge(f, i);
   return out;
 }

  //for backward compatibility
  void remove_constraint(Face_handle f, int i) {remove_constrained_edge(f,i);}
  void insert(Point a, Point b) { insert_constraint(a, b);}
  void insert(Vertex_handle va, Vertex_handle  vb) {insert_constraint(va,vb);}

  // QUERY
  bool is_constrained(Edge e) const;
  bool are_there_incident_constraints(Vertex_handle v) const;
  bool is_valid(bool verbose = false, int level = 0) const;
  // template<class OutputItEdges>
  // OutputItEdges incident_constraints(Vertex_handle v,
  //                                     OutputItEdges out) const;

  void file_output(std::ostream& os) const;

protected:
  virtual Vertex_handle virtual_insert(const Point& a,
                                       Face_handle start = Face_handle());
  virtual Vertex_handle virtual_insert(const Point& a,
                                       Locate_type lt,
                                       Face_handle loc,
                                       int li );
//Vertex_handle special_insert_in_edge(const Point & a, Face_handle f, int i);
  void update_constraints_incident(Vertex_handle va,
                                   Vertex_handle c1,
                                   Vertex_handle c2);
  void clear_constraints_incident(Vertex_handle va);
  void update_constraints_opposite(Vertex_handle va);
  void update_constraints(const List_edges &hole);

  void mark_constraint(Face_handle fr, int i);

  virtual Vertex_handle intersect(Face_handle f, int i,
                                  Vertex_handle vaa,
                                  Vertex_handle vbb);
  Vertex_handle intersect(Face_handle f, int i,
                          Vertex_handle vaa,
                          Vertex_handle vbb,
                          No_constraint_intersection_tag);
  Vertex_handle intersect(Face_handle f, int i,
                          Vertex_handle vaa,
                          Vertex_handle vbb,
                          No_constraint_intersection_requiring_constructions_tag);
  Vertex_handle intersect(Face_handle f, int i,
                          Vertex_handle vaa,
                          Vertex_handle vbb,
                          Exact_intersections_tag);
  Vertex_handle intersect(Face_handle f, int i,
                          Vertex_handle vaa,
                          Vertex_handle vbb,
                          Exact_predicates_tag);

  Vertex_handle insert_intersection(Face_handle f, int i,
                                    Vertex_handle vaa,
                                    Vertex_handle vbb,
                                    Vertex_handle vcc,
                                    Vertex_handle vdd,
                                    const Point& pa,
                                    const Point& pb,
                                    const Point& pc,
                                    const Point& pd,
                                    Exact_predicates_tag);
private:
  //made private to avoid using the Triangulation_2 version
  Vertex_handle move(Vertex_handle v, const Point &)
  {
    CGAL_error_msg("Do not use that function!");
    return v;
  }

public:
// made public for Laurent  to find out deleted faces
// when inserting a constraint with most probably
// no intersection
  bool find_intersected_faces(Vertex_handle va,
                              Vertex_handle vb,
                              List_faces & intersected_faces,
                              List_edges & list_ab,
                              List_edges & list_ba,
                              Vertex_handle& vi);
protected:
  virtual void triangulate_hole(List_faces& intersected_faces,
                                List_edges& conflict_boundary_ab,
                                List_edges& conflict_boundary_ba);

  void triangulate_hole(List_faces& intersected_faces,
                        List_edges& conflict_boundary_ab,
                        List_edges& conflict_boundary_ba,
                        List_edges& new_edges);

  void triangulate_half_hole(List_edges & list_edges,
                             List_edges & new_edges);

  void remove_1D(Vertex_handle v);
  void remove_2D(Vertex_handle v);

  //templated member function
public:
  // the int parameter is a work around for VC7 to compile
  template < class InputIterator >
#if defined(_MSC_VER)
  std::ptrdiff_t insert(InputIterator first, InputIterator last, int i = 0)
#else
    std::ptrdiff_t insert(InputIterator first, InputIterator last)
#endif
    {
#if defined(_MSC_VER)
      CGAL_USE(i);
#endif
      size_type n = number_of_vertices();

      std::vector<Point> points (first, last);
      CGAL::spatial_sort (points.begin(), points.end(), geom_traits());

      Face_handle hint;
      for (typename std::vector<Point>::const_iterator p = points.begin(), end = points.end();
              p != end; ++p)
          hint = insert (*p, hint)->face();

      return number_of_vertices() - n;
    }

  //deprecated
 template<class OutputIterator>
  bool are_there_incident_constraints(Vertex_handle v,
                                      OutputIterator out) const
    {
      Edge_circulator ec=incident_edges(v), done(ec);
      bool are_there = false;
      if (ec == nullptr) return are_there;
      do {
        if(is_constrained(*ec)) {
          *out++ = *ec;
          are_there = true;
        }
        ec++;
      } while (ec != done);
      return are_there;
    }

#ifdef CGAL_CDT_2_DEBUG_INTERSECTIONS
  auto display_vertex(Vertex_handle v) const {
    With_point_tag point_tag;
    using CGAL::IO::oformat;
    return IO::oformat(v, point_tag);
  }
#endif // CGAL_CDT_2_DEBUG_INTERSECTIONS

 template<class OutputItEdges>
 OutputItEdges  incident_constraints(Vertex_handle v,
                                      OutputItEdges out) const {
   Edge_circulator ec=incident_edges(v), done(ec);
   if (ec == nullptr) return  out;
   do {
     if(is_constrained(*ec))    *out++ = *ec;
     ec++;
   } while (ec != done);
   return out;
 }

  // the following functions are overloaded
  // to take care of constraint marks
  template<class EdgeIt>
  Vertex_handle star_hole( const Point& p,
                           EdgeIt edge_begin,
                           EdgeIt edge_end) {
    std::list<Face_handle> empty_list;
    return star_hole(p,
                     edge_begin,
                     edge_end,
                     empty_list.begin(),
                     empty_list.end());
  }

  template<class EdgeIt, class FaceIt>
  Vertex_handle star_hole( const Point& p,
                           EdgeIt edge_begin,
                           EdgeIt edge_end,
                           FaceIt face_begin,
                           FaceIt face_end)
{
    Vertex_handle v =  Triangulation::star_hole(p,
                                                edge_begin,
                                                edge_end,
                                                face_begin,
                                                face_end);
    // restore constraint status for new faces.
    int vindex;
    Face_handle fh;
    int ih;
    Face_circulator fc = incident_faces(v), done(fc);
    do {
      vindex = fc->index(v);
      fc->set_constraint(cw(vindex), false);
      fc->set_constraint(ccw(vindex), false);
      fh = fc->neighbor(vindex);
      ih = mirror_index(fc,vindex);
      fc->set_constraint(vindex, fh->is_constrained(ih));
    } while (++fc != done);
    return v;
}

};

template < class Gt, class Tds, class Itag >
inline
typename Constrained_triangulation_2<Gt,Tds,Itag>::Vertex_handle
Constrained_triangulation_2<Gt,Tds,Itag>::
virtual_insert(const Point& a, Face_handle start)
// virtual version of insert
{
  return insert(a,start);
}

template < class Gt, class Tds, class Itag >
inline
typename Constrained_triangulation_2<Gt,Tds,Itag>::Vertex_handle
Constrained_triangulation_2<Gt,Tds,Itag>::
virtual_insert(const Point& a,
               Locate_type lt,
               Face_handle loc,
               int li )
// virtual version of insert
{
  return insert(a,lt,loc,li);
}

template < class Gt, class Tds, class Itag >
inline
typename Constrained_triangulation_2<Gt,Tds,Itag>::Vertex_handle
Constrained_triangulation_2<Gt,Tds,Itag>::
insert(const Point& a, Face_handle start)
// inserts point a
// in addition to what is done for non constrained triangulations
// constrained edges are updated
{
  Face_handle loc;
  int li;
  Locate_type lt;
  loc = locate(a, lt, li, start);
  return Constrained_triangulation::insert(a,lt,loc,li);
}

template < class Gt, class Tds, class Itag >
typename Constrained_triangulation_2<Gt,Tds,Itag>::Vertex_handle
Constrained_triangulation_2<Gt,Tds,Itag>::
insert(const Point& a, Locate_type lt, Face_handle loc, int li)
// insert a point p, whose localization is known (lt, f, i)
// in addition to what is done for non constrained triangulations
// constrained edges are updated
{
  Vertex_handle va;
  Vertex_handle v1, v2;
  bool insert_in_constrained_edge = false;

  std::list<std::pair<Vertex_handle,Vertex_handle> > constrained_edges;
  bool one_dimensional = false;
  if(dimension() == 1){
    one_dimensional = true;
    for(Finite_edges_iterator it = finite_edges_begin();
        it != finite_edges_end();
        ++it){
      if(is_constrained(*it)){
        constrained_edges.emplace_back(it->first->vertex(cw(it->second)),
                                       it->first->vertex(ccw(it->second)));
      }
    }
  }
  if ( lt == Triangulation::EDGE && loc->is_constrained(li) )
  {
    if constexpr (std::is_same_v<Itag, No_constraint_intersection_tag>)
      throw Intersection_of_constraints_exception();

    insert_in_constrained_edge = true;
    v1=loc->vertex(ccw(li)); //endpoint of the constraint
    v2=loc->vertex(cw(li)); // endpoint of the constraint
  }

  va = Triangulation::insert(a,lt,loc,li);

  if(one_dimensional && (dimension() == 2)){
    for(const std::pair<Vertex_handle,Vertex_handle>& vp : constrained_edges){
      Face_handle fh;
      int i;
      if(this->is_edge(vp.first, vp.second, fh,i)){
        fh->set_constraint(i,true);
        boost::tie(fh,i) = mirror_edge(Edge(fh,i));
        fh->set_constraint(i,true);
      }
    }
  }

  if (insert_in_constrained_edge)
    update_constraints_incident(va, v1,v2);
  else if(lt != Triangulation::VERTEX)
    clear_constraints_incident(va);

  if (dimension() == 2)
    update_constraints_opposite(va);

  return va;
}

// template < class Gt, class Tds, class Itag >
// typename Constrained_triangulation_2<Gt,Tds,Itag>::Vertex_handle
// Constrained_triangulation_2<Gt, Tds, Itag>::
// special_insert_in_edge(const Point & a, Face_handle f, int i)
//   // insert  point p in edge(f,i)
//   // bypass the precondition for point a to be in edge(f,i)
//   // update constrained status
// {
//   Vertex_handle va;
//   Vertex_handle c1,c2;
//   c1 = f->vertex(cw(i));  //endpoint of edge
//   c2 = f->vertex(ccw(i)); //endpoint of edge
//   bool insert_in_constrained_edge = f->is_constrained(i);

//   va = this->_tds.insert_in_edge(f, i);
//   va->set_point(a);

//   if (insert_in_constrained_edge) update_constraints_incident(va, c1,c2);
//   else clear_constraints_incident(va);
//   if (dimension() == 2) update_constraints_opposite(va);
//   return va;
// }


template < class Gt, class Tds, class Itag >
inline void
Constrained_triangulation_2<Gt,Tds,Itag>::
insert_constraint(const Point& a, const Point& b)
// the algorithm first inserts a and b,
// and then forces the constraint [va,vb]
{
  Vertex_handle va= virtual_insert(a);
  Vertex_handle vb= virtual_insert(b);
  if ( va != vb)   insert_constraint(va,vb);
}


template <class Gt, class Tds, class Itag >
inline void
Constrained_triangulation_2<Gt,Tds,Itag>::
insert_constraint(Vertex_handle  vaa, Vertex_handle vbb)
// forces the constraint [va,vb]
// [va,vb] will eventually be split into several edges
// if a vertex vc of t lies on segment ab
// or if ab intersect some constrained edges
{
  if(vaa == vbb){
    return;
  }
  std::stack<std::pair<Vertex_handle, Vertex_handle> > stack;
  stack.push(std::make_pair(vaa,vbb));

#ifdef CGAL_CDT_2_DEBUG_INTERSECTIONS
  Rcpp::Rcerr << CGAL::internal::cdt_2_indent_level
            << "CT_2::insert_constraint( " << display_vertex(vaa)
            << " , " << display_vertex(vbb)
            << " )\n";
  internal::Indentation_level::Exit_guard exit_guard = CGAL::internal::cdt_2_indent_level.open_new_scope();
#endif // CGAL_CDT_2_DEBUG_INTERSECTIONS
  while(! stack.empty()){
    boost::tie(vaa,vbb) = stack.top();
    stack.pop();
    CGAL_precondition( vaa != vbb);
#ifdef CGAL_CDT_2_DEBUG_INTERSECTIONS
    Rcpp::Rcerr << CGAL::internal::cdt_2_indent_level
              << "CT_2::insert_constraint, stack pop=( " << display_vertex(vaa)
              << " , " << display_vertex(vbb)
              << " ) remaining stack size: "
              << stack.size() << '\n';
    CGAL_assertion(this->is_valid(true));
#endif // CGAL_CDT_2_DEBUG_INTERSECTIONS
    Vertex_handle vi;

    Face_handle fr;
    int i;
    if(includes_edge(vaa,vbb,vi,fr,i))
    {
      // if the segment (or a subpart of the segment) that we are trying to constraint is already
      // present in the triangulation and is already marked as constrained,
      // then this is an intersection
      if constexpr (std::is_same_v<Itag, No_constraint_intersection_tag>) {
        if(fr->is_constrained(dimension() == 1 ? 2 : i)) {
          throw Intersection_of_constraints_exception();
        }
      }

      mark_constraint(fr,i);
      if (vi != vbb)  {
        stack.push(std::make_pair(vi,vbb));
      }
      continue;
    }

    List_faces intersected_faces;
    List_edges conflict_boundary_ab, conflict_boundary_ba;
    bool intersection  = find_intersected_faces( vaa, vbb,
                                                 intersected_faces,
                                                 conflict_boundary_ab,
                                                 conflict_boundary_ba,
                                                 vi);
    if ( intersection) {
      if (vi != vaa && vi != vbb) {
#ifdef CGAL_CDT_2_DEBUG_INTERSECTIONS
  Rcpp::Rcerr << CGAL::internal::cdt_2_indent_level
            << "CT_2::insert_constraint stack push [vaa, vi] ( " << display_vertex(vaa)
            << " , " << display_vertex(vi)
            << " )\n";
  Rcpp::Rcerr << CGAL::internal::cdt_2_indent_level
            << "CT_2::insert_constraint stack push [vi, vbb] ( " << display_vertex(vi)
            << " , " << display_vertex(vbb)
            << " )\n";
#endif // CGAL_CDT_2_DEBUG_INTERSECTIONS
        stack.push(std::make_pair(vaa,vi));
        stack.push(std::make_pair(vi,vbb));
      }
      else{
#ifdef CGAL_CDT_2_DEBUG_INTERSECTIONS
  Rcpp::Rcerr << CGAL::internal::cdt_2_indent_level
            << "CT_2::insert_constraint stack push [vaa, vbb]( " << display_vertex(vaa)
            << " , " << display_vertex(vbb)
            << " )\n";
#endif // CGAL_CDT_2_DEBUG_INTERSECTIONS
        stack.push(std::make_pair(vaa,vbb));
      }
      continue;
    }

    //no intersection
    triangulate_hole(intersected_faces,
                     conflict_boundary_ab,
                     conflict_boundary_ba);

    if (vi != vbb) {
      stack.push(std::make_pair(vi,vbb));
    }
  }

}

template <class Gt, class Tds, class Itag >
bool
Constrained_triangulation_2<Gt,Tds,Itag>::
find_intersected_faces(Vertex_handle vaa,
                       Vertex_handle vbb,
                       List_faces & intersected_faces,
                       List_edges & list_ab,
                       List_edges & list_ba,
                       Vertex_handle & vi)
  // vi is set to the first vertex of the triangulation on [vaa,vbb].
  // Return true if an intersection with a constrained edge is
  // encountered, false otherwise
  // When false :
  // intersected_faces contains the list if faces intersected by [va,vi]
  // list_ab and list_ba represents the boundary of the union
  // of the intersected faces oriented cw
  // list_ab consists of the edges from vaa to vi (i.e. on the left of a->b)
  // list_ba    "         "        from vi to vaa (i.e. on the right of a->b)
{
  const Point& aa = vaa->point();
  const Point& bb = vbb->point();
  Line_face_circulator current_face=Line_face_circulator(vaa, this, bb);
  int ind=current_face->index(vaa);

  // to deal with the case where the first crossed edge
  // is constrained
#ifdef CGAL_CDT_2_DEBUG_INTERSECTIONS
  Rcpp::Rcerr << CGAL::internal::cdt_2_indent_level
            << "CT_2::find_intersected_faces ( " << display_vertex(vaa)
            << " , " << display_vertex(vbb)
            << " )\n"
            << CGAL::internal::cdt_2_indent_level
            << "> current constrained edges are:\n";
  for(Constrained_edges_iterator edge_it = this->constrained_edges_begin(),
        end = this->constrained_edges_end();
      edge_it != end; ++edge_it)
  {
    Rcpp::Rcerr <<CGAL::internal::cdt_2_indent_level
              << "> (#"
              << edge_it->first->vertex(cw(edge_it->second))->time_stamp()
              << ", #"
              << edge_it->first->vertex(ccw(edge_it->second))->time_stamp()
              << ")\n";
  }
  Rcpp::Rcerr << CGAL::internal::cdt_2_indent_level
            << "> current face is ( #" << current_face->vertex(0)->time_stamp()
            << " #" << current_face->vertex(1)->time_stamp()
            << " #" << current_face->vertex(2)->time_stamp() << " )\n";
#endif // CGAL_CDT_2_DEBUG_INTERSECTIONS

  if(current_face->is_constrained(ind)) {
    vi=intersect(current_face, ind, vaa, vbb);
    return true;
  }

  Face_handle lf= current_face->neighbor(ccw(ind));
  Face_handle rf= current_face->neighbor(cw(ind));
  Orientation orient;
  Face_handle previous_face;
  Vertex_handle current_vertex;

  list_ab.push_back(Edge(lf, lf->index(current_face)));
  list_ba.push_front(Edge(rf, rf->index(current_face)));
  intersected_faces.push_front(current_face);

  // initcd
  previous_face=current_face;
  ++current_face;
  ind=current_face->index(previous_face);
  current_vertex=current_face->vertex(ind);

  // loop over triangles intersected by ab
  bool done = false;
  while (current_vertex != vbb && !done)  {
    orient = orientation(aa,bb,current_vertex->point());
    int i1, i2;
    switch (orient) {
    case COLLINEAR :
      done = true; // current_vertex is the new endpoint
      break;
    case LEFT_TURN :
    case RIGHT_TURN :
      if (orient == LEFT_TURN) {
        i1 = ccw(ind) ; //index of second intersected edge of current_face
        i2 = cw(ind); //index of non intersected edge of current_face
      }
      else {
        i1 = cw(ind) ; //index of second intersected edge of current_face
        i2 = ccw(ind); //index of non intersected edge of current_face
      }
      if(current_face->is_constrained(i1)) {
        vi = intersect(current_face, i1, vaa,vbb);
        return true;
      }
      else {
        lf= current_face->neighbor(i2);
        intersected_faces.push_front(current_face);
        if (orient == LEFT_TURN)
          list_ab.push_back(Edge(lf, lf->index(current_face)));
        else // orient == RIGHT_TURN
          list_ba.push_front(Edge(lf, lf->index(current_face)));
        previous_face=current_face;
        ++current_face;
        ind=current_face->index(previous_face);
        current_vertex=current_face->vertex(ind);
      }
      break;
    }
  }

  // last triangle
  vi = current_vertex;
  intersected_faces.push_front(current_face);
  lf= current_face->neighbor(cw(ind));
  list_ab.push_back(Edge(lf, lf->index(current_face)));
  rf= current_face->neighbor(ccw(ind));
  list_ba.push_front(Edge(rf, rf->index(current_face)));
  return false;
}


template <class Gt, class Tds, class Itag >
typename Constrained_triangulation_2<Gt,Tds,Itag>::Vertex_handle
Constrained_triangulation_2<Gt,Tds,Itag>::
intersect(Face_handle f, int i,
          Vertex_handle vaa,
          Vertex_handle vbb)
{
  return intersect(f, i, vaa, vbb, Itag());
}

template <class Gt, class Tds, class Itag >
typename Constrained_triangulation_2<Gt,Tds,Itag>::Vertex_handle
Constrained_triangulation_2<Gt,Tds,Itag>::
intersect(Face_handle , int ,
          Vertex_handle ,
          Vertex_handle ,
          No_constraint_intersection_tag)
{
  throw Intersection_of_constraints_exception();
  return Vertex_handle() ;
}

template <class Gt, class Tds, class Itag >
typename Constrained_triangulation_2<Gt,Tds,Itag>::Vertex_handle
Constrained_triangulation_2<Gt,Tds,Itag>::
intersect(Face_handle , int ,
          Vertex_handle ,
          Vertex_handle ,
          No_constraint_intersection_requiring_constructions_tag)
{
  throw Intersection_of_constraints_exception();
  return Vertex_handle() ;
}

template <class Gt, class Tds, class Itag >
typename Constrained_triangulation_2<Gt,Tds,Itag>::Vertex_handle
Constrained_triangulation_2<Gt,Tds,Itag>::
intersect(Face_handle f, int i,
          Vertex_handle vaa,
          Vertex_handle vbb,
          Exact_intersections_tag itag)
// compute the intersection of the constraint edge (f,i)
// with the subconstraint (vaa,vbb) being inserted
// insert the intersection point
// split constraint edge (f,i)
// and return the Vertex_handle of the new Vertex
{
#ifndef CGAL_NO_CDT_2_WARNING
  CGAL_warning_msg(false,
  "You are using an exact number type,\n"
  "using a Constrained_triangulation_plus_2 class\n"
  "would avoid cascading intersection computation\n"
  " and be much more efficient\n"
  "This message is shown only if CGAL_NO_CDT_2_WARNING"
  " is not defined.\n");
#endif
  const Point& pa = vaa->point();
  const Point& pb = vbb->point();
  const Point& pc = f->vertex(cw(i))->point();
  const Point& pd = f->vertex(ccw(i))->point();
  Point pi;
  CGAL_assertion_code( bool ok = )
  intersection(geom_traits(), pa, pb, pc, pd, pi, itag );
  CGAL_assertion(ok);
  Vertex_handle vi = virtual_insert(pi, Triangulation::EDGE, f, i);
  return vi;
}


namespace internal {

  template <typename Type>
  class Has_barycenter_2
  {
    typedef char Yes;
    typedef struct { char a[2]; } No;

    template <typename U>
    static auto check(int)
        -> decltype(std::declval<typename U::Point_2>() =
                        std::declval<U>().construct_barycenter_2_object()(
                            std::declval<typename U::Point_2>(),
                            std::declval<typename U::FT>(),
                            std::declval<typename U::Point_2>()),
                    Yes());

    template <typename U>
    static No check(...);

  public:
    static const bool value = (sizeof(Yes) == sizeof(check<Type>(0)));
  };

  template <typename Type>
  class Has_exact_kernel
  {
    typedef char Yes;
    typedef struct { char a[2]; } No;

    template <typename U>
    static auto check(int)
        -> decltype(std::declval<typename U::Exact_kernel>() =
                        std::declval<U>().exact_kernel(),
                    Yes());

    template <typename U>
    static No check(...);

  public:
    static const bool value = (sizeof(Yes) == sizeof(check<Type>(0)));
  };

  template <typename Gt, bool = Has_exact_kernel<Gt>::value >
  struct Can_construct_almost_exact_intersection {
    enum { value =
      internal::Has_barycenter_2<typename Gt::Exact_kernel>::value &&
      internal::Has_filtered_predicates<Gt>::value };
  };

  template <typename Gt>
  struct Can_construct_almost_exact_intersection<Gt, false>
      : public CGAL::Tag_false {};

  template <typename Gt>
  static constexpr bool can_construct_almost_exact_intersection_v =
      Can_construct_almost_exact_intersection<Gt>::value;

  template <typename Gt>
  constexpr bool can_construct_almost_exact_intersection(const Gt&) {
    return can_construct_almost_exact_intersection_v<Gt>;
  }
} // namespace internal


template <class Gt, class Tds, class Itag >
typename Constrained_triangulation_2<Gt,Tds,Itag>::Vertex_handle
Constrained_triangulation_2<Gt,Tds,Itag>::
insert_intersection(Face_handle f, int i,
                    Vertex_handle vaa,
                    Vertex_handle vbb,
                    Vertex_handle vcc,
                    Vertex_handle vdd,
                    const Point& pa,
                    const Point& pb,
                    const Point& pc,
                    const Point& pd,
                    Exact_predicates_tag itag)
{
  Point pi; //creator for point is required here
  bool ok = intersection(geom_traits(), pa, pb, pc, pd, pi, itag);

  auto intersection_not_in_the_two_triangles = [&](const Point& pi) {
    if(orientation(pc,pd,pi) == RIGHT_TURN) {
      // check if `pi` is in the triangle (pc, pd, p0)
      const Point& p0 = f->vertex(i)->point();
      if(orientation(p0, pc, pi) == LEFT_TURN || orientation(pd, p0, pi) == LEFT_TURN) {
        return true;
      }
    } else {
      // orientation(pd, pc, pi) != LEFT_TURN
      const auto edge = mirror_edge({f, i});
      // check if `pi` is in the triangle (pd, pc, p1)
      const Point& p1 = edge.first->vertex(edge.second)->point();
      if(orientation(pc, p1, pi) == LEFT_TURN || orientation(p1, pd, pi) == LEFT_TURN) {
        return true;
      }
    }
    return false;
  };

  Vertex_handle vi;
  if ( !ok ) {  //intersection detected but not computed
    int int_index = limit_intersection(geom_traits(), pa, pb, pc, pd, itag);
    switch(int_index){
    case 0 : vi = vaa; break;
    case 1 : vi = vbb; break;
    case 2 : vi = vcc; break;
    case 3 : vi = vdd; break;
    }
    if(vi == vaa || vi == vbb) {
      remove_constrained_edge(f, i);
    }
  }
  else{ //intersection computed
    if(internal::can_construct_almost_exact_intersection(geom_traits()) &&
       intersection_not_in_the_two_triangles(pi))
    {
      // now compute the exact intersection point
      pi = almost_exact_intersection(geom_traits(), pa, pb, pc, pd);
      if (intersection_not_in_the_two_triangles(pi)) {
        // If the most-exact intersection point is not in the union of the two
        // triangles, then snap to `pc` or `pd`...
        if(compare_distance(pi, pc, pd) == SMALLER) {
          vi = vcc;
        } else {
          vi = vdd;
        }
      }
    }
    if(vi != vcc && vi != vdd) {
      remove_constrained_edge(f, i);
      vi = virtual_insert(pi, f);
    }
  }
#ifdef CGAL_CDT_2_DEBUG_INTERSECTIONS
  Rcpp::Rcerr << CGAL::internal::cdt_2_indent_level
            << "CT_2::insert_intersection, `vi` is ( " << display_vertex(vi)
            << " )\n";
#endif // CGAL_CDT_2_DEBUG_INTERSECTIONS
  return vi;
}

template <class Gt, class Tds, class Itag >
typename Constrained_triangulation_2<Gt,Tds,Itag>::Vertex_handle
Constrained_triangulation_2<Gt,Tds,Itag>::
intersect(Face_handle f, int i,
          Vertex_handle vaa,
          Vertex_handle vbb,
          Exact_predicates_tag itag)
{
  Vertex_handle  vcc, vdd;
  vcc = f->vertex(cw(i));
  vdd = f->vertex(ccw(i));

  const Point& pa = vaa->point();
  const Point& pb = vbb->point();
  const Point& pc = vcc->point();
  const Point& pd = vdd->point();

#ifdef CGAL_CDT_2_DEBUG_INTERSECTIONS
  Rcpp::Rcerr << CGAL::internal::cdt_2_indent_level
            << "CT_2::intersect segment ( " << display_vertex(vaa)
            << " , " << display_vertex(vbb)
            << " ) with edge ( " << display_vertex(vcc)
            << " , " << display_vertex(vdd)
            << " )\n";
#endif // CGAL_CDT_2_DEBUG_INTERSECTIONS
  Vertex_handle vi = insert_intersection(f, i, vaa, vbb, vcc, vdd, pa, pb, pc, pd, itag);
#ifdef CGAL_CDT_2_DEBUG_INTERSECTIONS
  Rcpp::Rcerr << CGAL::internal::cdt_2_indent_level
            << "CT_2::intersect, `vi` is ( " << display_vertex(vi)
            << " )\n";
#endif // CGAL_CDT_2_DEBUG_INTERSECTIONS

  // vi == vc or vi == vd may happen even if intersection==true
  // due to approximate construction of the intersection
  if (vi != vcc && vi != vdd) {
    insert_constraint(vcc,vi);
    insert_constraint(vi, vdd);
  }
  else {
    insert_constraint(vcc,vdd);
  }
  return vi;
}

template <class Gt, class Tds, class Itag >
inline
typename Constrained_triangulation_2<Gt,Tds,Itag>::Vertex_handle
Constrained_triangulation_2<Gt,Tds,Itag>::
push_back(const Point &p)
{
  return insert(p);
}


template <class Gt, class Tds, class Itag >
inline
void
Constrained_triangulation_2<Gt,Tds,Itag>::
push_back(const Constraint &c)
{
  insert(c.first, c.second);
}


template < class Gt, class Tds, class Itag >
void
Constrained_triangulation_2<Gt,Tds,Itag>::
update_constraints_incident(Vertex_handle va,
                            Vertex_handle c1,
                            Vertex_handle c2)
  // update status of edges incident to a
  // after insertion in the  constrained edge c1c2
{
  if (dimension() == 0) return;
  if (dimension()== 1) {
    Edge_circulator ec=incident_edges(va), done(ec);
    do {
      ((*ec).first)->set_constraint(2,true);
    }while (++ec != done);
  }
  else{
    //dimension() ==2
    int cwi, ccwi, indf;
    Face_circulator fc=incident_faces(va), done(fc);
    CGAL_assertion(fc != nullptr);
    do {
      indf = fc->index(va);
      cwi=cw(indf);
      ccwi=ccw(indf);
      if ((fc->vertex(cwi) == c1)||(fc->vertex(cwi) == c2)) {
          fc->set_constraint(ccwi,true);
          fc->set_constraint(cwi,false);
        }
        else {
          fc->set_constraint(ccwi,false);
          fc->set_constraint(cwi,true);
        }
        ++fc;
      } while (fc != done);
  }
}

template < class Gt, class Tds ,class Itag >
void
Constrained_triangulation_2<Gt,Tds,Itag>::
clear_constraints_incident(Vertex_handle va)
// make the edges incident to a newly created vertex unconstrained
{
 Edge_circulator ec=incident_edges(va), done(ec);
 Face_handle f;
 int indf;
  if ( ec != nullptr){
    do {
      f = (*ec).first ;
      indf = (*ec).second;
      f->set_constraint(indf,false);
      if (dimension() == 2) {
        f->neighbor(indf)->set_constraint(mirror_index(f,indf),false);
      }
    } while (++ec != done);
  }
  return;
}


template < class Gt, class Tds, class Itag >
void
Constrained_triangulation_2<Gt,Tds,Itag>::
update_constraints_opposite(Vertex_handle va)
  // update status of edges opposite to a
  // after insertion of a
{
  CGAL_assertion(dimension()==2);
  Face_handle f=va->face(), start=f;
  int indf;
  do {
    indf = f->index(va);
    if (f->neighbor(indf)->is_constrained(mirror_index(f,indf)) ) {
      f->set_constraint(indf,true);
    }
    else {
      f->set_constraint(indf,false);
    }
    f= f->neighbor(ccw(indf)); // turns ccw around va
  } while (f != start);
  return;
}

template < class Gt, class Tds, class Itag >
void
Constrained_triangulation_2<Gt,Tds,Itag>::
update_constraints( const List_edges &hole)
{
  typename List_edges::const_iterator it = hole.begin();
  Face_handle f;
  int i;
  for ( ; it != hole.end(); it ++) {
    f =(*it).first;
    i = (*it).second;
    if ( f->is_constrained(i) )
      (f->neighbor(i))->set_constraint(mirror_index(f,i),true);
    else (f->neighbor(i))->set_constraint(mirror_index(f,i),false);
  }
}


template < class Gt, class Tds, class Itag >
inline void
Constrained_triangulation_2<Gt,Tds,Itag>::
mark_constraint(Face_handle fr, int i)
{
  if (dimension()==1) fr->set_constraint(2, true);
  else{
    fr->set_constraint(i,true);
    fr->neighbor(i)->set_constraint(mirror_index(fr,i),true);
  }
  return;
}

template < class Gt, class Tds, class Itag >
inline void
Constrained_triangulation_2<Gt,Tds,Itag>::
triangulate_hole(List_faces& intersected_faces,
                 List_edges& conflict_boundary_ab,
                 List_edges& conflict_boundary_ba)
{
  List_edges new_edges;
  triangulate_hole(intersected_faces,
                   conflict_boundary_ab,
                   conflict_boundary_ba,
                   new_edges);
}



template < class Gt, class Tds, class Itag >
void
Constrained_triangulation_2<Gt,Tds,Itag>::
triangulate_hole(List_faces& intersected_faces,
                 List_edges& conflict_boundary_ab,
                 List_edges& conflict_boundary_ba,
                 List_edges& new_edges)
  // triangulate the hole limited by conflict_boundary_ab
  // and conflict_boundary_ba
  // insert the new edges in new-edges
  // delete the faces of intersected_faces
{
  if ( !conflict_boundary_ab.empty() ) {
    triangulate_half_hole(conflict_boundary_ab, new_edges);
    triangulate_half_hole(conflict_boundary_ba, new_edges);

    // the two faces that share edge ab are neighbors
    // their common edge ab is a constraint
    Face_handle fr,fl;
    fl=(*conflict_boundary_ab.begin()).first;
    fr=(*conflict_boundary_ba.begin()).first;
    fl->set_neighbor(2, fr);
    fr->set_neighbor(2, fl);
    fl->set_constraint(2, true);
    fr->set_constraint(2, true);

    // delete intersected faces
    while( ! intersected_faces.empty()) {
      fl = intersected_faces.front();
      intersected_faces.pop_front();
      delete_face(fl);
    }
  }
}



template < class Gt, class Tds, class Itag >
void
Constrained_triangulation_2<Gt,Tds,Itag>::
remove(Vertex_handle  v)
  // remove a vertex and updates the constrained edges of the new faces
  // precondition : there is no incident constraints
{
  CGAL_precondition( v != Vertex_handle() );
  CGAL_precondition( ! is_infinite(v));
  CGAL_precondition( ! are_there_incident_constraints(v));

  if  (number_of_vertices() == 1)     remove_first(v);
  else if (number_of_vertices() == 2) remove_second(v);
  else   if ( dimension() == 1) remove_1D(v);
  else  remove_2D(v);
  return;
}


template < class Gt, class Tds, class Itag >
void
Constrained_triangulation_2<Gt,Tds,Itag>::
remove_1D(Vertex_handle  v)
{
  Edge_circulator ec = incident_edges(v), done(ec);
  do {
    (*ec).first->set_constraint(2,false);
  } while (++ec != done);
  Triangulation::remove_1D(v);
}

template < class Gt, class Tds, class Itag >
void
Constrained_triangulation_2<Gt,Tds,Itag>::
remove_2D(Vertex_handle v)
{
  if (test_dim_down(v)) { this->_tds.remove_dim_down(v);}
  else {
    List_edges hole;
    make_hole(v, hole);
    List_edges shell=hole; //save hole because it will be emptied by fill_hole
    fill_hole(v, hole);
    update_constraints(shell);
    delete_vertex(v);
  }
  return;
}


template < class Gt, class Tds, class Itag >
void
Constrained_triangulation_2<Gt,Tds,Itag>::
remove_constrained_edge(Face_handle f, int i)
{
#ifdef CGAL_CDT_2_DEBUG_INTERSECTIONS
  Rcpp::Rcerr << CGAL::internal::cdt_2_indent_level
            << "CT_2::remove_constrained_edge ( #"
            << f->vertex(cw(i))->time_stamp()
            << ", #"
            << f->vertex(ccw(i))->time_stamp()
            << ")\n";
#endif // CGAL_CDT_2_DEBUG_INTERSECTIONS
  f->set_constraint(i, false);
  if (dimension() == 2)
    (f->neighbor(i))->set_constraint(mirror_index(f,i), false);
  return;
}

template < class Gt, class Tds, class Itag >
void
Constrained_triangulation_2<Gt,Tds,Itag>::
remove_incident_constraints(Vertex_handle v)
{
   Edge_circulator ec=incident_edges(v), done(ec);
   if (ec == nullptr) return;
   do {
        if(is_constrained(*ec)) { remove_constrained_edge((*ec).first,
                                                   (*ec).second);}
        ec++;
   } while (ec != done);
   return;
}

template < class Gt, class Tds, class Itag >
inline  bool
Constrained_triangulation_2<Gt,Tds,Itag>::
are_there_incident_constraints(Vertex_handle v) const
{
  return are_there_incident_constraints(v, Emptyset_iterator());
}

template < class Gt, class Tds, class Itag >
inline  bool
Constrained_triangulation_2<Gt,Tds,Itag>::
is_valid(bool verbose, int level) const
{
    bool result = Triangulation::is_valid(verbose,level);
    for( All_faces_iterator it = all_faces_begin();
                            it != all_faces_end() ; it++) {
      for(int i=0; i<3; i++) {
        Face_handle n = it->neighbor(i);
        result = result &&
          it->is_constrained(i) == n->is_constrained(n->index(it));
      }
    }
    return result;
}

template < class Gt, class Tds, class Itag >
inline  bool
Constrained_triangulation_2<Gt,Tds,Itag>::
is_constrained(Edge e) const
{
  return (e.first)->is_constrained(e.second);
}

template < class Gt, class Tds, class Itag >
void
Constrained_triangulation_2<Gt,Tds,Itag>::
triangulate_half_hole(List_edges & list_edges,  List_edges & new_edges)
  // triangulates the  polygon whose boundary consists of list_edges
  // plus the edge ab joining the two endpoints of list_edges
  // the orientation of the polygon (as provided by list_edges) must
  // be cw
  // the edges of list_edges are assumed to be edges of a
  // triangulation that will be updated by the procedure
  // the edges that are created are put in list new_edges
  // takes linear time
{
  Vertex_handle va; // first vertex of list_edges
  Face_handle newlf;
  Face_handle n1,n2,n;
  int ind1, ind2,ind;
  Orientation orient;

  typename List_edges::iterator current, next, tempo;
  current=list_edges.begin();

  va=((*current).first)->vertex(ccw((*current).second));
  next=current;
  ++next;

  do
    {
      n1=(*current).first;
      ind1=(*current).second;
      // in case n1 is no longer a triangle of the new triangulation
      if ( n1->neighbor(ind1) != Face_handle() ) {
        n=n1->neighbor(ind1);
        //ind=mirror_index(n1,ind1);
        // mirror_index does not work in this case
        ind = cw(n->index(n1->vertex(cw(ind1))));
        n1=n->neighbor(ind);
        ind1= mirror_index(n,ind);
      }
      n2=(*next).first;
      ind2=(*next).second;
      // in case n2 is no longer a triangle of the new triangulation
      if (n2->neighbor(ind2) != Face_handle() ) {
        n=n2->neighbor(ind2);
        // ind=mirror_index(n2,ind2);
        // mirror_index does not work in this case
        ind = cw(n->index(n2->vertex(cw(ind2))));
        n2=n->neighbor(ind);
        ind2= mirror_index(n,ind);
      }

      Vertex_handle v0=n1->vertex(ccw(ind1));
      Vertex_handle v1=n1->vertex(cw(ind1));
      Vertex_handle v2=n2->vertex(cw(ind2));
      orient = orientation(v0->point(),v1->point(),v2->point());
      switch (orient) {
      case RIGHT_TURN :
        // creates the new triangle v0v1v2
        // updates the neighbors, the constraints
        //and the list of new edges
        newlf = create_face(v0,v2,v1);
        new_edges.push_back(Edge(newlf,2));
        newlf->set_neighbor(1, n1);
        newlf->set_neighbor(0, n2);
        n1->set_neighbor(ind1, newlf);
        n2->set_neighbor(ind2, newlf);
        if (n1->is_constrained(ind1)) {
          newlf->set_constraint(1,true);
        }
        if (n2->is_constrained(ind2)) {
          newlf->set_constraint(0,true);
        }
        // v0, v1 or v2.face() may have been removed
        v0->set_face(newlf);
        v1->set_face(newlf);
        v2->set_face(newlf);
        // update list_edges
        tempo=current;
        current=list_edges.insert(current, Edge(newlf,2));
        list_edges.erase(tempo);
        list_edges.erase(next);
        next=current;
        if (v0 != va) {--current;}
        else {++next;}
        break;
      case LEFT_TURN :
        ++current; ++next;
        break;
      case COLLINEAR :
        ++current; ++next;
        break;
      }
    } while (next != list_edges.end());
}

template < class Gt, class Tds, class Itag >
void
Constrained_triangulation_2<Gt, Tds, Itag>::
file_output(std::ostream& os) const
{
  Triangulation_2<Gt, Tds>::file_output(os);

  // write constrained status
  typename Tds::Face_iterator ib = this->_tds.face_iterator_base_begin();
  for( ; ib != this->_tds.face_iterator_base_end(); ++ib) {
    for(int j = 0; j < 3; ++j){
      if (ib->is_constrained(j)) { os << "C";}
      else { os << "N";}
      if(IO::is_ascii(os)){
        if(j==2) {
          os << "\n";
        } else {
          os <<  ' ';
        }
      }
    }
  }
}

template < class Gt, class Tds, class Itag >
std::ostream &
operator<<(std::ostream& os,
           const Constrained_triangulation_2<Gt,Tds,Itag> &ct)
{
  ct.file_output(os);
  return os ;
}

template < class Gt, class Tds, class Itag >
std::istream &
operator>>(std::istream& is,
                 Constrained_triangulation_2<Gt,Tds,Itag> &ct)
{
  typedef Constrained_triangulation_2<Gt,Tds,Itag> CDT;
  ct.clear();
  is >> static_cast<typename CDT::Triangulation&>(ct);
  for (typename CDT::All_faces_iterator fit=ct.all_faces_begin(),
                                        fit_end=ct.all_faces_end();fit_end!=fit;++fit){
    char c[3];
    is >> c[0] >>  c[1] >> c[2];
    for (int k=0;k<3;++k){
      fit->set_constraint(k,c[k]=='C');
    }
  }
  return is;
}

//Helping functions to compute intersections of constrained edges
template<class Gt>
bool
intersection(const Gt& ,
             const typename Gt::Point_2& ,
             const typename Gt::Point_2& ,
             const typename Gt::Point_2& ,
             const typename Gt::Point_2& ,
             typename Gt::Point_2& ,
             No_constraint_intersection_tag)
{
  return false;
}

template<class Gt>
bool
intersection(const Gt& ,
             const typename Gt::Point_2& ,
             const typename Gt::Point_2& ,
             const typename Gt::Point_2& ,
             const typename Gt::Point_2& ,
             typename Gt::Point_2& ,
             No_constraint_intersection_requiring_constructions_tag)
{
  return false;
}

template<class Gt>
bool
intersection(const Gt& gt,
             const typename Gt::Point_2& pa,
             const typename Gt::Point_2& pb,
             const typename Gt::Point_2& pc,
             const typename Gt::Point_2& pd,
             typename Gt::Point_2& pi,
             Exact_intersections_tag)
{
  return compute_intersection(gt,pa,pb,pc,pd,pi);
}


template<class Gt>
inline bool
intersection(const Gt& gt,
             const typename Gt::Point_2& pa,
             const typename Gt::Point_2& pb,
             const typename Gt::Point_2& pc,
             const typename Gt::Point_2& pd,
             typename Gt::Point_2& pi,
             Exact_predicates_tag,
             CGAL::Tag_false /* not a FT is not floating-point */)
{
  return compute_intersection(gt,pa,pb,pc,pd,pi);
}

template<class Gt>
inline bool
intersection(const Gt& gt,
             const typename Gt::Point_2& pa,
             const typename Gt::Point_2& pb,
             const typename Gt::Point_2& pc,
             const typename Gt::Point_2& pd,
             typename Gt::Point_2& pi,
             Exact_predicates_tag,
             CGAL::Tag_true /* FT is a floating-point type */)
{
  const bool result = compute_intersection(gt,pa,pb,pc,pd,pi);
  if(!result) return result;
  if(pi == pa || pi == pb || pi == pc || pi == pd) {
#ifdef CGAL_CDT_2_DEBUG_INTERSECTIONS
    Rcpp::Rcerr << CGAL::internal::cdt_2_indent_level
              << "  CT_2::intersection: intersection is an existing point "
              << pi << std::endl;
#endif
    return result;
  }


#ifdef CGAL_CDT_2_INTERSECTION_SNAPPING_ULP_DISTANCE
  const int dist = CGAL_CDT_2_INTERSECTION_SNAPPING_ULP_DISTANCE;
#else
  const int dist = 4;
#endif
  typedef typename Gt::Construct_bbox_2 Construct_bbox_2;
  Construct_bbox_2 bbox = gt.construct_bbox_2_object();
  auto bb(bbox(pi));
  bb.dilate(dist);
  if(do_overlap(bb, bbox(pa))) pi = pa;
  if(do_overlap(bb, bbox(pb))) pi = pb;
  if(do_overlap(bb, bbox(pc))) pi = pc;
  if(do_overlap(bb, bbox(pd))) pi = pd;
#ifdef CGAL_CDT_2_DEBUG_INTERSECTIONS
  if(pi == pa || pi == pb || pi == pc || pi == pd) {
    Rcpp::Rcerr << CGAL::internal::cdt_2_indent_level
              << "  CT_2::intersection: intersection SNAPPED to an existing point "
              << pi << std::endl;
  }
#endif
  return result;
}

template<class Gt>
inline bool
intersection(const Gt& gt,
             const typename Gt::Point_2& pa,
             const typename Gt::Point_2& pb,
             const typename Gt::Point_2& pc,
             const typename Gt::Point_2& pd,
             typename Gt::Point_2& pi,
             Exact_predicates_tag exact_predicates_tag)
{
  typedef typename Gt::FT FT;
  return intersection(gt,pa,pb,pc,pd,pi,
                      exact_predicates_tag,
                      Boolean_tag<std::is_floating_point<FT>::value>());
}


template<class Gt>
bool
compute_intersection(const Gt& gt,
             const typename Gt::Point_2& pa,
             const typename Gt::Point_2& pb,
             const typename Gt::Point_2& pc,
             const typename Gt::Point_2& pd,
             typename Gt::Point_2& pi)
{
  typedef typename Gt::Point_2 Point_2;

  typename Gt::Intersect_2 compute_intersec = gt.intersect_2_object();
  typename Gt::Construct_segment_2 construct_segment = gt.construct_segment_2_object();

  auto result =
      compute_intersec(construct_segment(pa, pb), construct_segment(pc, pd));

#ifdef CGAL_CDT_2_DEBUG_INTERSECTIONS
  typedef typename Gt::Segment_2 Segment_2;
  if(result){
    if (const Segment_2* s = std::get_if<Segment_2>(&*result)){
      Rcpp::Rcerr << CGAL::internal::cdt_2_indent_level
                << "compute_intersection: " << *s << '\n';
    }else if(const Point_2* p = std::get_if<Point_2 >(&*result))
      Rcpp::Rcerr << CGAL::internal::cdt_2_indent_level
                << "compute_intersection: " << *p << '\n';
  }
#endif // CGAL_CDT_2_DEBUG_INTERSECTIONS
  if(result){
    if(const Point_2* p = std::get_if<Point_2 >(&*result)){
      pi = *p;
      return true;
    }
  }
  return false;
}

template<class Gt>
int
limit_intersection(const Gt& gt,
                   const typename Gt::Point_2& pa,
                   const typename Gt::Point_2& pb,
                   const typename Gt::Point_2& pc,
                   const typename Gt::Point_2& pd,
                   Exact_predicates_tag)
{
  typename Gt::Construct_line_2 line = gt.construct_line_2_object();
  typename Gt::Compute_squared_distance_2
    distance = gt.compute_squared_distance_2_object();
  typename Gt::Line_2 l1 = line(pa,pb);
  typename Gt::Line_2 l2 = line(pc,pd);
  int i = 0;
  typename Gt::FT dx = distance(l2,pa);
  typename Gt::FT db = distance(l2,pb);
  typename Gt::FT dc = distance(l1,pc);
  typename Gt::FT dd = distance(l1,pd);
  if ( db < dx  ) { dx = db; i = 1;}
  if ( dc < dx  ) { dx = dc; i = 2;}
  if ( dd < dx  ) { i = 3;}
  return i;
}

template <typename Gt>
typename Gt::Point_2
almost_exact_intersection(const Gt& gt,
                          const typename Gt::Point_2& pa,
                          const typename Gt::Point_2& pb,
                          const typename Gt::Point_2& pc,
                          const typename Gt::Point_2& pd)
{
  Boolean_tag<internal::can_construct_almost_exact_intersection_v<Gt>> tag;
  return almost_exact_intersection(gt, pa, pb, pc, pd, tag);
}

template <typename Gt>
typename Gt::Point_2
almost_exact_intersection(const Gt&,
                          const typename Gt::Point_2&,
                          const typename Gt::Point_2&,
                          const typename Gt::Point_2&,
                          const typename Gt::Point_2&,
                          Tag_false)
{
  CGAL_error_msg("this function should be call only with Exact_predicates_tag"
                 " and with an appropriate traits class");
}

template <typename K2_Point_2, typename K1>
auto convert_point_type(const CGAL::Point_2<K1> &p) {
  NT_converter<typename K1::FT, typename Kernel_traits<K2_Point_2>::Kernel::FT> c;
  return K2_Point_2(c(p.x()), c(p.y()));
}

template <typename K2_Point_3, typename K1>
auto convert_point_type(const CGAL::Point_3<K1> &p) {
  NT_converter<typename K1::FT, typename Kernel_traits<K2_Point_3>::Kernel::FT> c;
  return K2_Point_3(c(p.x()), c(p.y()), c(p.z()));
}

template <class Gt>
typename Gt::Point_2
exact_intersection_point_for_cdt_2(const typename Gt::Point_2& pa,
                                   const typename Gt::Point_2& pb,
                                   const typename Gt::Point_2& pc,
                                   const typename Gt::Point_2& pd,
                                   const Gt& gt)
{
  using Exact_kernel = typename Gt::Exact_kernel;
  using Exact_point = typename Exact_kernel::Point_2;
  using Exact_FT = typename Exact_kernel::FT;
  using Point = typename Gt::Point_2;

  const auto ea = convert_point_type<Exact_point>(pa);
  const auto eb = convert_point_type<Exact_point>(pb);
  const auto ec = convert_point_type<Exact_point>(pc);
  const auto ed = convert_point_type<Exact_point>(pd);

  auto det = [](const auto& v1, const auto& v2) -> Exact_FT {
    return v1.x() * v2.y() - v1.y() * v2.x();
  };

  const Exact_FT det_bd_cd = det(ed - eb, ed - ec);
  const Exact_FT det_ba_cd = det(ea - eb, ec - ed);
  const Exact_FT alpha = det_bd_cd / det_ba_cd;

  return convert_point_type<Point>(
      gt.exact_kernel().construct_barycenter_2_object()(ea, alpha, eb));
}

template <typename Gt>
typename Gt::Point_2
almost_exact_intersection(const Gt& gt,
                          const typename Gt::Point_2& pa,
                          const typename Gt::Point_2& pb,
                          const typename Gt::Point_2& pc,
                          const typename Gt::Point_2& pd,
                          Tag_true /* gt has Construct_exact_intersection_point_2 */)
{
  return exact_intersection_point_for_cdt_2(pa, pb, pc, pd, gt);
}

} //namespace CGAL

#include <CGAL/enable_warnings.h>

#endif //CGAL_CONSTRAINED_TRIANGULATION_2_H

