/*
 *  Copyright (c) 2000-2022 Inria
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation
 *  and/or other materials provided with the distribution.
 *  * Neither the name of the ALICE Project-Team nor the names of its
 *  contributors may be used to endorse or promote products derived from this
 *  software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *
 *  Contact: Bruno Levy
 *
 *     https://www.inria.fr/fr/bruno-levy
 *
 *     Inria,
 *     Domaine de Voluceau,
 *     78150 Le Chesnay - Rocquencourt
 *     FRANCE
 *
 */

#ifndef GEOGRAM_DELAUNAY_CDT_2D
#define GEOGRAM_DELAUNAY_CDT_2D

#include <geogram/basic/common.h>
#include <geogram/basic/geometry.h>
#include <geogram/numerics/predicates.h>
#include <geogram/numerics/exact_geometry.h>
#include <geogram/mesh/index.h>
#include <functional>

/**
 * \file geogram/delaunay/CDT_2d.h
 * \brief Constained Delaunay triangulation in 2D
 * \details See documentation of CDT at the end of this file.
 */

namespace GEO {

    /**
     * \brief Forward declaration of a small data structure
     *  used internally by CDTBase2d::find_intersected_edges()
     */
    struct CDT2d_ConstraintWalker;

    /**
     * \brief Base class for constrained Delaunay triangulation
     * \details Manages the combinatorics of the constrained Delaunay
     *  triangulation. The points need to be stored elsewhere, and manipulated
     *  through indices, with two predicates:
     *  - orient2d(i,j,k)
     *  - incircle(i,j,k,l)
     *  and one construction:
     *  - create_intersection(i,j,k,l)
     *  See \ref CDT2d for an example of implementation
     */
    class GEOGRAM_API CDTBase2d {
    public:
    /**
     * \brief CDTBase2d constructor
     */
    CDTBase2d();

    /**
     * \brief CDTBase2d destructor
     */
    virtual ~CDTBase2d();

    /**
     * \brief Removes everything from this triangulation
     */
    virtual void clear();

    /**
     * \brief Inserts a constraint
     * \param[in] i , j the indices of the two vertices
     *  of the constrained segment
     */
    void insert_constraint(index_t i, index_t j);

    /**
     * \brief Recursively removes all the triangles adjacent to
     *  the border, and keeps what's surrounded by constraints
     * \param[in] remove_internal_holes if set, triangles inside
     *  the internal closed loops of constrained edges are removed
     *  as well.
     * \details If \p remove_internal_holes is set,
     *  closed loops inside holes are considered as
     *  "matter" (and kept), and so on and so forth. This also works
     *  if there are overlapping constraints (what counts is the
     *  number of constraints associated with each triangle edge).
     *  Note that this does not work if there is a
     *  chain of constrained internal edges (as opposed to a loop).
     */
    void remove_external_triangles(bool remove_internal_holes=false);

    /**
     * \brief Specifies whether a constrained Delaunay
     *  triangulation should be constructed, or just a
     *  plain constrained triangulation
     * \param[in] delaunay true if a Delaunay triangulation
     *  should be constructed, false otherwise.
     */
    void set_delaunay(bool delaunay) {
        delaunay_ = delaunay;
    }

    /**
     * \brief Gets the number of triangles
     */
    index_t nT() const {
        return T_.size()/3;
    }

    /**
     * \brief Gets the number of vertices
     */
    index_t nv() const {
        return nv_;
    }

    /**
     * \brief Gets the number of constraints
     */
    index_t ncnstr() const {
        return ncnstr_;
    }

    /**
     * \brief Gets a vertex of a triangle
     * \param[in] t the triangle
     * \param[in] lv the local index of the vertex, in 0,1,2
     * \return the global index of the vertex
     */
    index_t Tv(index_t t, index_t lv) const {
        geo_debug_assert(t<nT());
        geo_debug_assert(lv<3);
        return T_[3*t+lv];
    }

    /**
     * \brief Finds the local index of a vertex in a triangle
     * \param[in] t the triangle
     * \param[in] v the vertex
     * \return lv such that Tv(t,lv) = v
     */
    index_t Tv_find(index_t t, index_t v) const {
        geo_debug_assert(t<nT());
        geo_debug_assert(v<nv());
        return find_3(T_.data()+3*t, v);
    }

    /**
     * \brief Gets a triangle adjacent to a triangle
     * \param[in] t the triangle
     * \param[in] le the local edge index, in 0,1,2
     * \return the triangle adjacent to \p t accross \p le,
     *  or NO_INDEX if there is no such triangle
     */
    index_t Tadj(index_t t, index_t le) const {
        geo_debug_assert(t<nT());
        geo_debug_assert(le<3);
        return Tadj_[3*t+le];
    }

    /**
     * \brief Finds the edge accross which a triangle is
     *  adjacent to another one
     * \param[in] t1 , t2 the two triangles
     * \return the local edge index le such that
     *  Tadj(t1,le) = t2
     */
    index_t Tadj_find(index_t t1, index_t t2) const {
        geo_debug_assert(t1<nT());
        geo_debug_assert(t2<nT());
        return find_3(Tadj_.data()+3*t1, t2);
    }

    /**
     * \brief Gets a triangle incident to a given vertex
     * \param[in] v a vertex
     * \return a triangle t such that there exists lv in
     *  0,1,2 such that Tv(t,lv) = v
     */
    index_t vT(index_t v) const {
        geo_debug_assert(v < nv());
        return v2T_[v];
    }


    /**
     * \brief Gets the constraint associated with an edge
     * \details
     *  When constraining segments on a CDT2d by calling
     *  insert_constraint(), some segments in the triangulation may
     *  be included in several constraints (it the constraints are
     *  co-linear and overlapping).
     *  One iterates on the constraints associated with an edge as follows:
     * \code
     *    for(
     *       index_t ecit = Tedge_cnstr_first(t,le);
     *       ecit != NO_INDEX;
     *       ecit = edge_cnstr_next(ecit)
     *    ) {
     *       index_t cnstr = edge_cnstr(ecit);
     *       ... // do something with cnstr
     *    }
     * \endcode
     * where 'cnstr' corresponds to the value of ncnstr() when
     *  insert_constraint() was called for that constraint.
     * \param[in] t a triangle
     * \param[in] le local edge index, in 0,1,2
     * \return the edge constraints iterator associated with this edge or
     *  NO_INDEX if the edge is not constrained.
     * \see edge_cnstr_next(), edge_cnstr()
     */
    index_t Tedge_cnstr_first(index_t t, index_t le) const {
        geo_debug_assert(t < nT());
        geo_debug_assert(le < 3);
        return Tecnstr_first_[3*t+le];
    }

    /**
     * \brief Gets the successor of an edge constraint iterator
     * \param[in] ecit the edge constraint iterator
     * \return the edge constraint iterator to the successor of \p ecit
     *  or NO_INDEX if \p ecit is the last of the list.
     * \see Tedge_cnstr_first(), edge_cnstr()
     */
    index_t edge_cnstr_next(index_t ecit) const {
        return ecnstr_next_[ecit];
    }

    /**
     * \brief Gets an edge constraint from an edge constraint iterator
     * \param[in] ecit the edge constraint iterator. Needs to be a valid
     *   iterator, different from NO_INDEX.
     * \return the edge constraint associated with the iterator.
     * \see Tedge_cnstr_first(), edge_cnstr_next()
     */
    index_t edge_cnstr(index_t ecit) const {
        return ecnstr_val_[ecit];
    }

    /**
     * \brief Gets the number of constraints associated with a triange edge
     * \details There can be several constraints associated with the same
     *  edge, whenever there are overlapping constraints. For instance,
     *  this function is useful to test the parity of the number of
     *  constraints when classifying inside/outside triangles
     *  in a CSG operation.
     * \param[in] t the triangle
     * \param[in] le the local index of the edge (0,1,2) in the triangle
     * \return the number of constraints associated with the edge
     */
    index_t Tedge_cnstr_nb(index_t t, index_t le) const {
        index_t result = 0;
        for(
            index_t ecit = Tedge_cnstr_first(t,le);
            ecit != NO_INDEX;
            ecit = edge_cnstr_next(ecit)
        ) {
            ++result;
        }
        return result;
    }


    /**
     * \brief Saves this CDT to a geogram mesh file.
     * \param[in] filename where to save this CDT
     */
    virtual void save(const std::string& filename) const = 0;

    /**
     * \brief Tests whether a triangle edge is Delaunay
     * \details returns true also for constrained edges and edges on borders
     */
    bool Tedge_is_Delaunay(index_t t, index_t le) const;

    protected:
    virtual void begin_insert_transaction();
    virtual void commit_insert_transaction();
    virtual void rollback_insert_transaction();

    /**
     * \brief Inserts a new point
     * \param[in] v the index of the new point, supposed to be
     *  equal to nv()
     * \param[in] hint an optional triangle, not too far away
     *  from the point to be inserted
     * \return the index of the created point. May be different
     *  from v if the point already existed in the triangulation
     */
    index_t insert(index_t v, index_t hint = NO_INDEX);

    /**
     * \brief Creates the combinatorics for a first large enclosing
     *  triangle
     * \param[in] v1 , v2 , v3 the three vertices of the first triangle,
     *  in 0,1,2
     * \details create_enclosing_triangle() or create_enclosing_quad()
     *  need to be called before anything else
     */
    void create_enclosing_triangle(index_t v1, index_t v2, index_t v3);

    /**
     * \brief Creates the combinatorics for a first large enclosing
     *  quad
     * \param[in] v1 , v2 , v3 , v4 the four vertices of the quad,
     *  in 0,1,2,3
     * \details create_enclosing_triangle() or create_enclosing_quad()
     *  need to be called before anything else
     */
    void create_enclosing_quad(
        index_t v1, index_t v2, index_t v3, index_t v4
    );

    /**
     * \brief Sets a triangle flag
     * \param[in] t the triangle
     * \param[in] flag the flag, in 0..7
     */
    void Tset_flag(index_t t, index_t flag) {
        geo_debug_assert(t < nT());
        geo_debug_assert(flag < 8);
        Tflags_[t] |= Numeric::uint8(1u << flag);
    }

    /**
     * \brief Resets a triangle flag
     * \param[in] t the triangle
     * \param[in] flag the flag, in 0..7
     */
    void Treset_flag(index_t t, index_t flag) {
        geo_debug_assert(t < nT());
        geo_debug_assert(flag < 8);
        Tflags_[t] &= Numeric::uint8(~(1u << flag));
    }

    /**
     * \brief Tests a triangle flag
     * \param[in] t the triangle
     * \param[in] flag the flag, in 0..7
     * \retval true if the flag is set
     * \retval false otherwise
     */
    bool Tflag_is_set(index_t t, index_t flag) {
        geo_debug_assert(t < nT());
        geo_debug_assert(flag < 8);
        return ((Tflags_[t] & (1u << flag)) != 0);
    }

    /**
     * \brief Constants for list_id
     */
    enum {
        DLIST_S_ID=0,
        DLIST_Q_ID=1,
        DLIST_N_ID=2,
        DLIST_NB=3
    };

    /**
     * \brief Constants for triangle flags
     */
    enum {
        T_MARKED_FLAG  = DLIST_NB,
        T_VISITED_FLAG = DLIST_NB+1
    };

    /**
     * \brief Tests whether a triangle is in a DList
     * \param[in] t the triangle
     * \retval true if the triangle is in a list
     * \retval false otherwise
     */
    bool Tis_in_list(index_t t) const {
        return (
            (Tflags_[t] &
             Numeric::uint8((1 << DLIST_NB)-1)
            ) != 0
        );
    }

    /**
     * \brief Doubly connected triangle list
     * \details DList is used to implement:
     *  - the stack S of triangles to flip in insert()
     *  - the queue Q of intersected edges in
     *    detect_intersected_edges() and constrain_edges()
     *  - the list N of new edges in constrain_edges()
     *  Everything is stored in CDBase
     *  vectors Tnext_, Tprev_ and Tflags_. As
     *  a consequence, the same triangle can be only
     *  in a single DList at the same time.
     */
    struct DList {
        /**
         * \brief Constructs an empty DList
         * \param[in] cdt a reference to the CDTBase2d
         * \param[in] list_id the DList id, in 0..DLIST_NB-1
         */
        DList(CDTBase2d& cdt, index_t list_id) :
            cdt_(cdt), list_id_(list_id),
            back_(NO_INDEX), front_(NO_INDEX) {
            geo_debug_assert(list_id < DLIST_NB);
        }

        /**
         * \brief Creates an uninitialized DList
         * \details One cannot do anything with an
         *   uninitialized Dlist, except:
         *   - initializing it with DList::initialize()
         *   - testing its status with DList::initialized()
         *   - display it with DList::show()
         */
        DList(CDTBase2d& cdt) :
            cdt_(cdt), list_id_(NO_INDEX),
            back_(NO_INDEX), front_(NO_INDEX) {
        }

        /**
         * \brief Initializes a list
         * \param[in] list_id the DList id, in 0..DLIST_NB-1
         */
        void initialize(index_t list_id) {
            geo_debug_assert(!initialized());
            geo_debug_assert(list_id < DLIST_NB);
            list_id_ = list_id;
        }

        /**
         * \brief Tests whether a DList is initialized
         */
        bool initialized() const {
            return (list_id_ != NO_INDEX);
        }

        ~DList() {
            if(initialized()) {
                clear();
            }
        }

        bool empty() const {
            geo_debug_assert(initialized());
            geo_debug_assert(
                (back_==NO_INDEX)==(front_==NO_INDEX)
            );
            return (back_==NO_INDEX);
        }

        bool contains(index_t t) const {
            geo_debug_assert(initialized());
            return cdt_.Tflag_is_set(t, list_id_);
        }

        index_t front() const {
            geo_debug_assert(initialized());
            return front_;
        }

        index_t back() const {
            geo_debug_assert(initialized());
            return back_;
        }

        index_t next(index_t t) const {
            geo_debug_assert(initialized());
            geo_debug_assert(contains(t));
            return cdt_.Tnext_[t];
        }

        index_t prev(index_t t) const {
            geo_debug_assert(initialized());
            geo_debug_assert(contains(t));
            return cdt_.Tprev_[t];
        }

        void clear() {
            for(index_t t=front_; t!=NO_INDEX; t = cdt_.Tnext_[t]) {
                cdt_.Treset_flag(t,list_id_);
            }
            back_ = NO_INDEX;
            front_ = NO_INDEX;
        }

        index_t size() const {
            geo_debug_assert(initialized());
            index_t result = 0;
            for(index_t t=front(); t!=NO_INDEX; t = next(t)) {
                ++result;
            }
            return result;
        }

        void push_back(index_t t) {
            geo_debug_assert(initialized());
            geo_debug_assert(!cdt_.Tis_in_list(t));
            cdt_.Tset_flag(t,list_id_);
            if(empty()) {
                back_ = t;
                front_ = t;
                cdt_.Tnext_[t] = NO_INDEX;
                cdt_.Tprev_[t] = NO_INDEX;
            } else {
                cdt_.Tnext_[t] = NO_INDEX;
                cdt_.Tnext_[back_] = t;
                cdt_.Tprev_[t] = back_;
                back_ = t;
            }
        }

        index_t pop_back() {
            geo_debug_assert(initialized());
            geo_debug_assert(!empty());
            index_t t = back_;
            back_ = cdt_.Tprev_[back_];
            if(back_ == NO_INDEX) {
                geo_debug_assert(front_ == t);
                front_ = NO_INDEX;
            } else {
                cdt_.Tnext_[back_] = NO_INDEX;
            }
            geo_debug_assert(contains(t));
            cdt_.Treset_flag(t,list_id_);
            return t;
        }

        void push_front(index_t t) {
            geo_debug_assert(initialized());
            geo_debug_assert(!cdt_.Tis_in_list(t));
            cdt_.Tset_flag(t,list_id_);
            if(empty()) {
                back_ = t;
                front_ = t;
                cdt_.Tnext_[t] = NO_INDEX;
                cdt_.Tprev_[t] = NO_INDEX;
            } else {
                cdt_.Tprev_[t] = NO_INDEX;
                cdt_.Tprev_[front_] = t;
                cdt_.Tnext_[t] = front_;
                front_ = t;
            }
        }

        index_t pop_front() {
            geo_debug_assert(initialized());
            geo_debug_assert(!empty());
            index_t t = front_;
            front_ = cdt_.Tnext_[front_];
            if(front_ == NO_INDEX) {
                geo_debug_assert(back_ == t);
                back_ = NO_INDEX;
            } else {
                cdt_.Tprev_[front_] = NO_INDEX;
            }
            geo_debug_assert(contains(t));
            cdt_.Treset_flag(t,list_id_);
            return t;
        }

        void remove(index_t t) {
            geo_debug_assert(initialized());
            if(t == front_) {
                pop_front();
            } else if(t == back_) {
                pop_back();
            } else {
                geo_debug_assert(contains(t));
                index_t t_prev = cdt_.Tprev_[t];
                index_t t_next = cdt_.Tnext_[t];
                cdt_.Tprev_[t_next] = t_prev;
                cdt_.Tnext_[t_prev] = t_next;
                cdt_.Treset_flag(t,list_id_);
            }
        }

        void show(std::ostream& out = std::cerr) const {
            switch(list_id_) {
            case DLIST_S_ID:
                out << "S";
                break;
            case DLIST_Q_ID:
                out << "Q";
                break;
            case DLIST_N_ID:
                out << "N";
                break;
            case NO_INDEX:
                out << "<uninitialized list>";
                break;
            default:
                out << "<unknown list id:" << list_id_ << ">";
                break;
            }
            out << "=";
            for(index_t t=front(); t!=NO_INDEX; t = next(t)) {
                out << t << ";";
            }
            out << std::endl;
        }

    private:
        CDTBase2d& cdt_;
        index_t list_id_;
        index_t back_;
        index_t front_;
    };

    /**
     * \brief Inserts a vertex in an edge
     * \param[in] v the vertex to be inserted
     * \param[in] t a triangle incident to the edge
     * \param[in] le the local index of the edge in \p t
     * \param[out] S DList of created triangles, ignored if uninitialized
     */
    void insert_vertex_in_edge(index_t v, index_t t, index_t le, DList& S);

    /**
     * \brief Inserts a vertex in an edge
     * \param[in] v the vertex to be inserted
     * \param[in] t a triangle incident to the edge
     * \param[in] le the local index of the edge in \p t
     */
    void insert_vertex_in_edge(index_t v, index_t t, index_t le) {
        DList S(*this);
        insert_vertex_in_edge(v,t,le,S);
    }

    /**
     * \brief Inserts a vertex in a triangle
     * \param[in] v the vertex to be inserted
     * \param[in] t the triangle
     * \param[out] S optional DList of created triangles
     */
    void insert_vertex_in_triangle(index_t v, index_t t, DList& S);

    /**
     * \brief Finds the edges intersected by a constraint
     * \param[in] i , j the two vertices of the constraint
     * \param[out] Q for each intersected edge, a triangle t
     *  will be pushed-back to Q, such that vT(t,1) and
     *  vT(t,2) are the extremities of the intersected edge.
     *  In addition, each triangle t is marked.
     * \details If a vertex k that is exactly on the constraint
     *  is found, then traversal stops there and k is returned.
     *  One can find the remaining intersections by continuing
     *  to call the function with (k,j) until \p j is returned.
     * \return the first vertex on [i,j] encountered when
     *  traversing the segment [i,j].
     */
    index_t find_intersected_edges(index_t i, index_t j, DList& Q);

    /**
     * \brief Used by find_intersected_edges()
     */
    void walk_constraint_v(CDT2d_ConstraintWalker& W);

    /**
     * \brief Used by find_intersected_edges()
     */
    void walk_constraint_t(CDT2d_ConstraintWalker& W, DList& Q);

    /**
     * \brief Constrains an edge by iteratively flipping
     *  the intersected edges.
     * \param[in] i , j the extremities of the edge
     * \param[in] Q the list of intersected edges, computed by
     *  find_intersected_edges()
     * \param[out] N optional  DList with the new edges
     *  that need to be re-Delaunized by find_intersected_edges(),
     *  ignored if uninitialized
     */
    void constrain_edges(index_t i, index_t j, DList& Q, DList& N);

    /**
     * \brief Restores Delaunay condition starting from the
     *  triangles incident to a given vertex.
     * \details This version uses internally a stack, initialized
     *  with the triangles incident to the vertex.
     * \param[in] from_v the vertex. Cannot be a vertex incident to the
     *  border.
     */
    void Delaunayize_vertex_neighbors(index_t from_v);

    /**
     * \brief Restores Delaunay condition starting from the
     *  triangles incident to a given vertex.
     * \param[in] v the vertex
     * \param[in] S a stack of triangles, initialized with
     *  the triangles incident to the vertex. Each triangle t
     *  is Trot()-ed in such a way that the vertex v
     *  corresponds to Vt(t,0)
     * \details Each time a triangle edge is swapped, the
     *  two new neighbors are recursively examined.
     */
    void Delaunayize_vertex_neighbors(index_t v, DList& S);

    /**
     * \brief Restores Delaunay condition for a set of
     *  edges after inserting a constrained edge
     * \param[in] N the edges for which Delaunay condition
     *  should be restored.
     */
    void Delaunayize_new_edges(DList& N);


    /**
     * \brief Sets all the combinatorial information
     *  of a triangle and edge flags
     * \param[in] t the triangle
     * \param[in] v1 , v2 , v3 the three vertices
     * \param[in] adj1 , adj2 , adj3 the three triangles
     *  adjacent to \p t
     * \param[in] e1cnstr , e2cnstr , e3cnstr optional
     *  edge constraints
     */
    void Tset(
        index_t t,
        index_t v1,   index_t v2,   index_t v3,
        index_t adj1, index_t adj2, index_t adj3,
        index_t e1cnstr = NO_INDEX,
        index_t e2cnstr = NO_INDEX,
        index_t e3cnstr = NO_INDEX
    ) {
        geo_debug_assert(t < nT());
        geo_debug_assert(v1 < nv());
        geo_debug_assert(v2 < nv());
        geo_debug_assert(v3 < nv());
        geo_debug_assert(adj1 < nT() || adj1 == NO_INDEX);
        geo_debug_assert(adj2 < nT() || adj2 == NO_INDEX);
        geo_debug_assert(adj3 < nT() || adj3 == NO_INDEX);
        geo_debug_assert(v1 != v2);
        geo_debug_assert(v2 != v3);
        geo_debug_assert(v3 != v1);
        geo_debug_assert(adj1 != adj2 || adj1 == NO_INDEX);
        geo_debug_assert(adj2 != adj3 || adj2 == NO_INDEX);
        geo_debug_assert(adj3 != adj1 || adj3 == NO_INDEX);
        geo_debug_assert(orient2d(v1,v2,v3) != ZERO);
        T_[3*t  ]    = v1;
        T_[3*t+1]    = v2;
        T_[3*t+2]    = v3;
        Tadj_[3*t  ] = adj1;
        Tadj_[3*t+1] = adj2;
        Tadj_[3*t+2] = adj3;
        Tecnstr_first_[3*t]   = e1cnstr;
        Tecnstr_first_[3*t+1] = e2cnstr;
        Tecnstr_first_[3*t+2] = e3cnstr;
        v2T_[v1] = t;
        v2T_[v2] = t;
        v2T_[v3] = t;
    }

    /**
     * \brief Rotates indices in triangle t in such a way
     *  that a given vertex becomes vertex 0
     * \details On exit, vertex \p lv of \p t becomes vertex 0
     * \param[in] t a triangle index
     * \param[in] lv local vertex index in 0,1,2
     */
    void Trot(index_t t, index_t lv) {
        geo_debug_assert(t < nT());
        geo_debug_assert(lv < 3);
        if(lv != 0) {
            index_t i = 3*t+lv;
            index_t j = 3*t+((lv+1)%3);
            index_t k = 3*t+((lv+2)%3);
            Tset(
                t,
                T_[i], T_[j], T_[k],
                Tadj_[i], Tadj_[j], Tadj_[k],
                Tecnstr_first_[i], Tecnstr_first_[j], Tecnstr_first_[k]
            );
        }
    }

    /**
     * \brief Swaps an edge.
     * \details Swaps edge 0 of \p t1.
     *    Vertex 0 of \p t1 is vertex 0 of
     *    the two new triangles.
     * \param[in] t1 a triangle index. Its edge
     *    opposite to vertex 0 is swapped
     * \param[in] swap_t1_t2 if set, swap which triangle will be
     *    t1 and which triangle will be Tadj(t1,0) in the
     *    new pair of triange (needed for two configurations
     *    of the optimized constraint enforcement algorithm).
     */
    void swap_edge(index_t t1, bool swap_t1_t2=false);

    /**
     * \brief Sets a triangle adjacency relation
     * \param[in] t a triangle
     * \param[in] le local edge index, in 0,1,2
     * \param[in] adj the triangle adjacent to \p t
     *  accross \p le
     */
    void Tadj_set(index_t t, index_t le, index_t adj) {
        geo_debug_assert(t < nT());
        geo_debug_assert(adj < nT());
        geo_debug_assert(le < 3);
        Tadj_[3*t+le] = adj;
    }

    /**
     * \brief Gets the neighboring triangle vertex
     *  opposite to a given vertex
     */
    index_t Topp(index_t t, index_t e=0) const {
        index_t t2 = Tadj(t,e);
        if(t2 == NO_INDEX) {
            return NO_INDEX;
        }
        index_t e2 = Tadj_find(t2,t);
        return Tv(t2,e2);
    }

    /**
     * \brief After having changed connections from triangle
     *  to a neighbor, creates connections from neighbor
     *  to triangle.
     * \details edge flags are copied from the neighbor to \p t1.
     *  If there is no triangle accross \p le1, then
     *  nothing is done
     * \param[in] t1 a triangle
     * \param[in] le1 a local edge of \p t1, in 0,1,2
     * \param[in] prev_t2_adj_e2 the triangle adjacent to t2 that
     *  \p t1 will replace, where t2 = Tadj(t1,le1)
     */
    void Tadj_back_connect(
        index_t t1, index_t le1, index_t prev_t2_adj_e2
    ) {
        geo_debug_assert(t1 < nT());
        geo_debug_assert(le1 < 3);
        index_t t2 = Tadj(t1,le1);
        if(t2 == NO_INDEX) {
            return;
        }
        index_t le2 = Tadj_find(t2,prev_t2_adj_e2);
        Tadj_set(t2,le2,t1);
        Tset_edge_cnstr_first(t1,le1,Tedge_cnstr_first(t2,le2));
    }

    /**
     * \brief Creates a new triangle
     * \return the index of the new triange
     */
    index_t Tnew() {
        index_t t = nT();
        index_t nc = (t+1)*3; // new number of corners
        T_.resize(nc, NO_INDEX);
        Tadj_.resize(nc, NO_INDEX);
        Tecnstr_first_.resize(nc, NO_INDEX);
        Tflags_.resize(t+1,0);
        Tnext_.resize(t+1,NO_INDEX);
        Tprev_.resize(t+1,NO_INDEX);
        return t;
    }

    /**
     * \brief Sets the constraints list associated with an edge
     * \param[in] t a triangle
     * \param[in] le local edge index, in 0,1,2
     * \param[in] ecit the edge constraint iterator that points to
     *  the first constraint associated with the edge
     */
    void Tset_edge_cnstr_first(
        index_t t, index_t le, index_t ecit
    ) {
        geo_debug_assert(t < nT());
        geo_debug_assert(le < 3);
        Tecnstr_first_[3*t+le] = ecit;
    }

    /**
     * \brief Adds a constraint to a triangle edge
     * \param[in] t a triangle
     * \param[in] le local edge index, in 0,1,2
     * \param[in] cnstr_id the constraint
     */
    void Tadd_edge_cnstr(
        index_t t, index_t le, index_t cnstr_id
    ) {
        geo_debug_assert(t < nT());
        geo_debug_assert(le < 3);
        // Check whether the edge is already constrained with the
        // same constraint.
        // TODO (if possible): understand how this can happen and
        // remove this bloc of code that is not super elegant
        // (it seems to be when we arrive at j and coming from a vertex
        // traversed by the edge, both conditions make the constraint
        // added to the traversed edge).
        for(
            index_t ecit = Tedge_cnstr_first(t,le);
            ecit != NO_INDEX;
            ecit = edge_cnstr_next(ecit)
        ) {
            if(edge_cnstr(ecit) == cnstr_id) {
                return;
            }
        }
        ecnstr_val_.push_back(cnstr_id);
        ecnstr_next_.push_back(Tedge_cnstr_first(t,le));
        Tset_edge_cnstr_first(t,le, ecnstr_val_.size()-1);
    }

    /**
     * \brief Adds a constraint to a triangle edge and to the neighboring
     *   edge if it exists
     * \param[in] t a triangle
     * \param[in] le local edge index, in 0,1,2
     * \param[in] cnstr_id the constraint
     */
    void Tadd_edge_cnstr_with_neighbor(
        index_t t, index_t le, index_t cnstr_id
    ) {
        geo_debug_assert(t < nT());
        geo_debug_assert(le < 3);
#ifdef GEO_DEBUG
        index_t t_e_cnstr_first = Tedge_cnstr_first(t,le);
#endif
        Tadd_edge_cnstr(t, le, cnstr_id);
        index_t t2 = Tadj(t,le);
        if(t2 != NO_INDEX) {
            index_t le2 = Tadj_find(t2,t);
            // Sanity check: make sure the two edges always share the
            // same constraint list.
            geo_debug_assert(Tedge_cnstr_first(t2,le2) == t_e_cnstr_first);
            Tset_edge_cnstr_first(t2,le2,Tedge_cnstr_first(t,le));
        }
    }

    /**
     * \brief Tests whether an edge is constrained
     * \param[in] t a triangle
     * \param[in] le local edge index, in 0,1,2
     * \retval true if the edge is constrained
     * \retval false otherwise
     */
    bool Tedge_is_constrained(index_t t, index_t le) const {
        return (Tedge_cnstr_first(t,le) != NO_INDEX);
    }

    /**
     * \brief Calls a user-defined function for each triangle
     * around a vertex
     * \param[in] v the vertex
     * \param[in] doit the function, that takes as argument the
     *  current triangle t and the local index lv of \p v in t.
     *  The function returns true if iteration is finished and can be
     *  exited, false otherwise.
     */
    void for_each_T_around_v(
        index_t v, std::function<bool(index_t t, index_t lv)> doit
    ) {
        index_t t = vT(v);
        index_t lv = NO_INDEX;
        do {
            lv = Tv_find(t,v);
            if(doit(t,lv)) {
                return;
            }
            t = Tadj(t, (lv+1)%3);
        } while(t != vT(v) && t != NO_INDEX);

        // We are done, this was an interior vertex
        if(t != NO_INDEX) {
            return;
        }

        // It was a vertex on the border, so we need
        // to traverse the triangle fan in the other
        // direction until we reach the border again
        t = vT(v);
        lv = Tv_find(t,v);
        t = Tadj(t, (lv+2)%3);
        while(t != NO_INDEX) {
            lv = Tv_find(t,v);
            if(doit(t,lv)) {
                return;
            }
            t = Tadj(t, (lv+2)%3);
        }
    }


    /**
     * \brief Locates a vertex
     * \param[in] v the vertex index
     * \param[in] hint an optional triangle, not too far away from the
     *  point to be inserted
     * \param[out] orient a pointer to the three orientations in the
     *  triangle. If one of them is zero, the point is on an edge, and
     *  if two of them are zero, it is on a vertex.
     * \return a triangle that contains \p v
     */
    index_t locate(
        index_t v, index_t hint = NO_INDEX, Sign* orient = nullptr
    ) const;

    /**
     * \brief Tests whether triange t and its neighbor accross edge 0 form
     *  a strictly convex quad
     * \retval true if triange \p t and its neighbor accross edge 0 form
     *  a strictly convex quad
     * \retval false otherwise
     */
    bool is_convex_quad(index_t t) const;

    /**
     * \brief Orientation predicate
     * \param[in] i , j , k three vertices
     * \return the sign of det(pj-pi,pk-pi)
     */
    virtual Sign orient2d(index_t i,index_t j,index_t k) const=0;

    /**
     * \brief Incircle predicate
     * \param[in] i , j , k the three vertices of a triangle
     * \param[in] l another vertex
     * \retval POSITIVE if \p l is inside the circumscribed circle of
     *  the triangle
     * \retval ZERO if \p l is on the circumscribed circle of
     *  the triangle
     * \retval NEGATIVE if \p l is outside the circumscribed circle of
     *  the triangle
     */
    virtual Sign incircle(index_t i,index_t j,index_t k,index_t l) const=0;

    /**
     * \brief Given two segments that have an intersection, create the
     *  intersection
     * \details The intersection is given both as the indices of segment
     *  extremities (i,j) and (k,l), that one can use to retreive the
     *  points in derived classes, and constraint indices E1 and E2, that
     *  derived classes may use to retreive symbolic information attached
     *  to the constraint
     * \param[in] E1 the index of the first edge, corresponding to the
     *  value of ncnstr() when insert_constraint() was called for
     *  that edge
     * \param[in] i , j the vertices of the first segment
     * \param[in] E2 the index of the second edge, corresponding to the
     *  value of ncnstr() when insert_constraint() was called for
     *  that edge
     * \param[in] k , l the vertices of the second segment
     * \return the index of a newly created vertex that corresponds to
     *  the intersection between [\p i , \p j] and [\p k , \p l]
     */
    virtual index_t create_intersection(
        index_t E1, index_t i, index_t j,
        index_t E2, index_t k, index_t l
    ) = 0;

    /**
     * \brief Finds the index of an integer in an array of three integers.
     * \param[in] T a const pointer to an array of three integers
     * \param[in] v the integer to retrieve in \p T
     * \return the index (0,1 or 2) of \p v in \p T
     * \pre The three entries of \p T are different and one of them is
     *  equal to \p v.
     */
    static inline index_t find_3(const index_t* T, index_t v) {
        // The following expression is 10% faster than using
        // if() statements. This uses the C++ norm, that
        // ensures that the 'true' boolean value converted to
        // an int is always 1. With most compilers, this avoids
        // generating branching instructions.
        // Thank to Laurent Alonso for this idea.
        index_t result = index_t( (T[1] == v) | ((T[2] == v) * 2) );
        // Sanity check, important if it was T[0], not explicitly
        // tested (detects input that does not meet the precondition).
        geo_debug_assert(T[result] == v);
        return result;
    }

    /*******************************************************************/

    /**
     * \brief Removes all the triangles that have the flag T_MARKED_FLAG
     *  set.
     * \details This compresses the triangle array, and updates triangle
     *  adjacencies, as well as the vertex to triangle array. Note that
     *  it uses Tnext_'s storage for internal bookkeeping, so this function
     *  should not be used if there exists a non-empty DList.
     */
    void remove_marked_triangles();

    /******************** Debugging ************************************/

    /**
     * \brief Consistency check for a triangle
     * \details aborts if inconsistency is detected
     * \param[in] t the triangle to be tested
     */
    void Tcheck(index_t t) const {
        if(t == NO_INDEX) {
            return;
        }
        for(index_t e=0; e<3; ++e) {
            geo_assert(Tv(t,e) != Tv(t,(e+1)%3));
            if(Tadj(t,e) == NO_INDEX) {
                continue;
            }
            geo_assert(Tadj(t,e) != Tadj(t,(e+1)%3));
            index_t t2 = Tadj(t,e);
            index_t e2 = Tadj_find(t2,t);
            geo_assert(Tadj(t2,e2) == t);
        }
    }

    /**
     * \brief Consistency check for a triangle in debug mode,
     *  ignored in release mode
     * \details aborts if inconsistency is detected
     * \param[in] t the triangle to be tested
     */
    void debug_Tcheck(index_t t) const {
#ifdef GEO_DEBUG
        Tcheck(t);
#else
        geo_argused(t);
#endif
    }

    /**
     * \brief Consistency combinatorial check for all the triangles
     * \details aborts if inconsistency is detected
     */
    void check_combinatorics() const {
        for(index_t t=0; t<nT(); ++t) {
            Tcheck(t);
        }
    }

    /**
     * \brief Consistency combinatorial check for all the triangles
     *  in debug mode, ignored in release mode
     * \details aborts if inconsistency is detected
     */
    void debug_check_combinatorics() const {
#ifdef GEO_DEBUG
        check_combinatorics();
#endif
    }

    /**
     * \brief Consistency geometrical check for all the triangles
     * \details aborts if inconsistency is detected
     */
    virtual void check_geometry() const;

    /**
     * \brief Consistency geometrical check for all the triangles
     *  in debug mode, ignored in release mode
     * \details aborts if inconsistency is detected
     */
    void debug_check_geometry() const {
#ifdef GEO_DEBUG
        check_geometry();
#endif
    }


    public:
    /**
     * \brief Checks both combinatorics and geometry,
     *  aborts on unconsistency
     */
    void check_consistency() const {
        check_combinatorics();
        check_geometry();
    }

    protected:
    /**
     * \brief Checks both combinatorics and geometry
     *  in debug mode, ignored in release mode,
     *  aborts on unconsistency
     */
    void debug_check_consistency() const {
        debug_check_combinatorics();
        debug_check_geometry();
    }

    /**
     * \brief Tests whether two segments have a frank intersection
     * \param[in] u1 , u2 the two extremities of the first segment
     * \param[in] v1 , v2 the two extremities of the second segment
     * \retval true if \p u1 , \p u2 has a frank intersection with
     *  \p v1 , \p v2
     * \retval false otherwise
     */
    bool segment_segment_intersect(
        index_t u1, index_t u2, index_t v1, index_t v2
    ) const {
        if(orient2d(u1,u2,v1)*orient2d(u1,u2,v2) > 0) {
            return false;
        }
        return (orient2d(v1,v2,u1)*orient2d(v1,v2,u2) < 0);
    }

    /**
     * \brief Tests whether an edge triangle and a segment have a frank
     *  intersection
     * \param[in] v1 , v2 the two extremities of the segment
     * \param[in] t a triangle
     * \param[in] le local edge index in 0,1,2
     * \retval true if edge \p le of \p t has a frank intersection with edge
     *  \p v1 , \p v2
     * \retval false otherwise
     */
    bool segment_edge_intersect(
        index_t v1, index_t v2, index_t t, index_t le
    ) const {
        index_t u1 = Tv(t,(le + 1)%3);
        index_t u2 = Tv(t,(le + 2)%3);
        return segment_segment_intersect(u1,u2,v1,v2);
    }

    /**
     * \brief Checks that the edges stored in a DList exactly correspond
     *  to all edge intersections between a segment and the triangle edges
     * \param[in] v1 , v2 the two vertices of the constrained segment
     * \param[in] Q a list of triangle. For each triangle in Q, edge 0
     *  is supposed to have an intersection with \p v1 , \p v2
     */
    void check_edge_intersections(
        index_t v1, index_t v2, const DList& Q
    );

    typedef std::pair<index_t, index_t> Edge;

    /**
     * \brief Gets a triangle incident a a given edge
     * \param[in] E the edge
     * \return a triangle with E as its edge 0
     */
    index_t eT(Edge E) {
        index_t v1 = E.first;
        index_t v2 = E.second;
        index_t result = NO_INDEX;
        for_each_T_around_v(
            v1, [&](index_t t, index_t lv)->bool {
                if(Tv(t, (lv+1)%3) == v2) {
                    if(Tv(t, (lv+2)%3) != v1) {
                        Trot(t, (lv+2)%3);
                    }
                    result = t;
                    return true;
                } else if(Tv(t, (lv+1)%3) == v1) {
                    if(Tv(t, (lv+2)%3) != v2) {
                        Trot(t, (lv+2)%3);
                    }
                    result = t;
                    return true;
                }
                return false;
            }
        );
        geo_debug_assert(result != NO_INDEX);
        geo_debug_assert(
            (Tv(result,1) == v1 && Tv(result,2) == v2) ||
            (Tv(result,1) == v2 && Tv(result,2) == v1)
        );
        return result;
    }

    /**
     * \brief Simpler version of locate() kept for reference
     * \see locate()
     */
    index_t locate_naive(
        index_t v, index_t hint = NO_INDEX, Sign* orient = nullptr
    ) const;

    /**
     * \brief Simpler version of constrain_edges() kept for reference
     * \see constrain_edges()
     */
    void constrain_edges_naive(
        index_t i, index_t j, DList& Q, vector<Edge>& N
    );

    /**
     * \brief Simpler version of Delaunayize_new_edges() that uses a vector
     *  instead of a DList, kept for reference
     */
    void Delaunayize_new_edges_naive(vector<Edge>& N);

    protected:
    index_t nv_;
    index_t ncnstr_;
    vector<index_t> T_;        /**< triangles vertices array              */
    vector<index_t> Tadj_;     /**< triangles adjacency array             */
    vector<index_t> v2T_;      /**< vertex to triangle back pointer       */
    vector<uint8_t> Tflags_;   /**< triangle flags                        */
    vector<index_t> Tecnstr_first_;  /**< index in edge constraints list  */
    vector<index_t> ecnstr_val_;     /**< edge constraints list, indices  */
    vector<index_t> ecnstr_next_;    /**< edge constraints list, links    */
    vector<index_t> Tnext_;    /**< doubly connected triangle list        */
    vector<index_t> Tprev_;    /**< doubly connected triangle list        */
    bool delaunay_;            /**< if set, compute a CDT, else just a CT */
    Sign orient_012_;          /**< global triangles orientation          */
    bool exact_incircle_;      /**< true if incircle() is 100% exact      */
    bool exact_intersections_; /**< true if intersections are 100% exact  */
    };

    /*****************************************************************/

    /**
     * \brief Constrained Delaunay triangulation
     * \details
     *   Example:
     *   \code
     *    CDT cdt;
     *    vec2 p1(.,.), p2(.,.), p3(.,.);
     *    cdt.create_enclosing_triangle(p1,p2,p3);
     *         // or create_enclosing_quad() or create_enclosing_rect()
     *    // insert points
     *    for(...) {
     *      vec2 p(.,.);
     *      index_t v = cdt.insert(p);
     *      ...
     *    }
     *    // insert constraints
     *    for(...) {
     *       index_t v1=..., v2=...;
     *       cdt.insert_constraint(v1,v2);
     *    }
     *    // get triangles
     *    for(index_t t=0; t<cdt.nT(); ++t) {
     *       index_t v1 = cdt.Tv(t,0);
     *       index_t v2 = cdt.Tv(t,1);
     *       index_t v3 = cdt.Tv(t,2);
     *       ... do something with v1,v2,v3
     *    }
     *   \endcode
     *   If some constraints are intersecting, new vertices are generated. They
     *   can be accessed using the function vec2 CDT::point(index_t v). Vertices
     *   coming from an intersection are between indices nv1 and CDT::nv(),
     *   where nv1 is the value of CDT::nv() before inserting the constraints
     *   (nv1 corresponds to the number of times CDT::insert() was called plus
     *   the number of points in the enclosing polygon). Note that like input
     *   points, constraint intersections are represented using double-precision
     *   floating point numbers, which is not always sufficient to ensure
     *   robustness. If the input has intersecting constraints and bullet-proof
     *   guarantees are needed, one can use ExactCDT2d instead.
     *
     *   If you want only a constrained triangulation (not Delaunay),
     *   you can call CDT::set_Delaunay(false) before inserting the points.
     *
     *   If you have many points to insert, you can use the function:
     *   \code
     *     void CDT::insert(index_t nb_points, const double* points);
     *   \endcode
     *   It is much much faster than inserting the points one by one. Internally
     *   it uses Amenta et.al's BRIO method (multi-resolution spatial sort).
     */
    class GEOGRAM_API CDT2d: public CDTBase2d {
    public:

        CDT2d();

        ~CDT2d() override;

        /**
         * \copydoc CDTBase2d::clear()
         */
        void clear() override;

        /**
         * \brief Creates a first large enclosing triangle
         * \param[in] p1 , p2 , p3 the three vertices of the first triangle
         * \details create_enclosing_triangle(), create_enclosing_rectangle()
         *  or create_enclosing_quad()  need to be called before anything else
         */
        void create_enclosing_triangle(
            const vec2& p1, const vec2& p2, const vec2& p3
        );

        /**
         * \brief Creates a first large enclosing quad
         * \param[in] p1 , p2 , p3 , p4 the four vertices of the quad
         * \details The quad needs to be convex.
         * create_enclosing_triangle(), create_enclosing_rectangle()
         *  or create_enclosing_quad()  need to be called before anything else
         */
        void create_enclosing_quad(
            const vec2& p1, const vec2& p2, const vec2& p3, const vec2& p4
        );


        /**
         * \brief Creates a first large enclosing rectangle
         * \param[in] x1 , y1 , x2 , y2 rectangle bounds
         * \details create_enclosing_triangle(), create_enclosing_rectangle()
         *  or create_enclosing_quad() need to be called before anything else
         */
        void create_enclosing_rectangle(
            double x1, double y1, double x2, double y2
        ) {
            create_enclosing_quad(
                vec2(x1,y1),
                vec2(x2,y1),
                vec2(x2,y2),
                vec2(x1,y2)
            );
        }

        /**
         * \brief Inserts a point
         * \param[in] p the point to be inserted
         * \param[in] hint a triangle not too far away from the point to
         *  be inserted
         * \return the index of the created point. Duplicated points are
         *  detected (and then the index of the existing point is returned)
         */
        index_t insert(const vec2& p, index_t hint = NO_INDEX) {
            debug_check_consistency();
            point_.push_back(p);
            index_t v = CDTBase2d::insert(point_.size()-1, hint);
            // If inserted point already existed in
            // triangulation, then nv() did not increase
            if(point_.size() > nv()) {
                point_.pop_back();
            }
            debug_check_consistency();
            return v;
        }

        /**
         * \brief Batch-inserts a set of point
         * \details In general, it is much faster than calling
         *  insert() multiple times. Internally it uses a spatial
         *  sort (Amenta et.al's BRIO method).
         *  On exit, the optional \p indices array contains the index mapping.
         *  indices[i] may be different from i if there were duplicated points.
         *  If there may be duplicated points and if one wants to insert
         *  constraint using CDTBase2d::insert_constraint(), one needs to call
         *  insert_constraint(indices[i],indices[j]) to get the correct
         *  translation of the indices
         * \param[in] points a contiguous array of all point coordinates
         * \param[in] nb_points number of points
         * \param[out] indices an optional pointer to an array of size \p
         *   nb_points of indices. On exit, indices[i] contains the index
         *   of the mesh vertex that corresponds to the i-th point. If there
         *   are duplicated points or if \p remove_unreferenced_vertices is set,
         *   then indices[i] may be different from i
         * \param[in] remove_unreferenced_vertices if set, then duplicated
         *   vertices are not stored in the vertices array. Internally, this
         *   systematically changes the order of the points. For this reason,
         *   if this flag is set, then one needs to do index mapping with
         *   \p indices, even when there is no duplicated point
         */
        void insert(
            index_t nb_points, const double* points,
            index_t* indices = nullptr,
            bool remove_unreferenced_vertices = false
        );

        /**
         * \copydoc CDTBase2d::save()
         */
        void save(const std::string& filename) const override;

        /**
         * \brief Gets a point by index
         * \param[in] v point index
         * \return the point at index \p v
         */
        const vec2 point(index_t v) const {
            geo_debug_assert(v < nv());
            return point_[v];
        }

    protected:
        /**
         * \copydoc CDTBase2d::orient_2d()
         */
        Sign orient2d(index_t i, index_t j, index_t k) const override;

        /**
         * \copydoc CDTBase2d::incircle()
         */
        Sign incircle(index_t i,index_t j,index_t k,index_t l) const override;

        /**
         * \copydoc CDTBase2d::create_intersection()
         */
        index_t create_intersection(
            index_t E1, index_t i, index_t j,
            index_t E2, index_t k, index_t l
        ) override;

    protected:
        vector<vec2> point_;
    };

    /*****************************************************************/

    /**
     * \brief Constrained Delaunay Triangulation with vertices that are
     *  exact points. Can be used to implement 2D CSG.
     * \details Points are represented using exact 2d homogeneous coordinates.
     *  Unlike CDT2d, this ensures exact representation of constraints
     *  intersections with guaranteed behavior.
     *  Under the hood, it inherits CDTBase2d (constrained Delaunay
     *  triangulation), and redefines orient2d(), incircle2d() and
     *  create_intersection() using vectors with homogeneous coordinates
     *  stored as arithmetic expansions (vec2HE) or arbitrary-precision
     *  floating point numbers (vec2HEx) if compiled with Tessael's
     *  geogramplus extension package.
     * \see CDT2d
     */
    class GEOGRAM_API ExactCDT2d : public CDTBase2d {
    public:
        typedef exact::vec2h ExactPoint;

        /**
         * \brief ExactCDT2d constructor
         */
        ExactCDT2d();

        /**
         * \brief ExactCDT2d destructor
         */
        ~ExactCDT2d() override;

        /**
         * \copydoc CDTBase2d::clear()
         */
        void clear() override;

        /**
         * \brief Inserts a point
         * \param[in] p the point to be inserted
         * \param[in] hint a triangle not too far away from the point to
         *  be inserted
         * \param[in] id an opaque identifier attached to the vertex that
         *   can be used by client code for instance to keep relations
         *   with a mesh. It can be queried using the vertex_id() function.
         * \return the index of the created point. Duplicated points are
         *  detected (and then the index of the existing point is returned)
         */
        index_t insert(
            const ExactPoint& p, index_t id=0, index_t hint = NO_INDEX
        );

        /**
         * \brief Inserts a constraint
         * \param[in] v1 , v2 the two extremities of the constraint, as returned
         *  by insert()
         * \param[in] operand_bits optional bitfield used by 2D CSG, indicating
         *  on which primitive boundaries the constraint is. Each bit set
         *  corresponds to a primitive. It is used by the classify() function.
         * \see CDTBase::insert_constraint() and classify()
         */
        void insert_constraint(index_t v1, index_t v2, index_t operand_bits=0) {
            constraints_.push_back(bindex(v1,v2,bindex::KEEP_ORDER));
            cnstr_operand_bits_.push_back(operand_bits);
            CDTBase2d::insert_constraint(v1,v2);
        }

        /**
         * \brief Creates a first large enclosing triangle
         * \param[in] p1 , p2 , p3 the three vertices of the triangle
	 * \details create_enclosing_triangle(), create_enclosing_rectangle() or
	 *  create_enclosing_quad() need to be called before anything else
         */
        void create_enclosing_triangle(
            const ExactPoint& p1, const ExactPoint& p2, const ExactPoint& p3
        );

        /**
         * \brief Creates a first large enclosing quad
         * \param[in] p1 , p2 , p3 , p4 the four vertices of the quad
         * \details The quad needs to be convex.
	 *  create_enclosing_triangle(), create_enclosing_rectangle() or
	 *  create_enclosing_quad() need to be called before anything else
         */
        void create_enclosing_quad(
            const ExactPoint& p1, const ExactPoint& p2,
            const ExactPoint& p3, const ExactPoint& p4
        );

        /**
         * \brief Creates a first large enclosing rectangle
         * \param[in] x1 , y1 , x2 , y2 rectangle bounds
	 * \details create_enclosing_triangle(), create_enclosing_rectangle() or
	 *  create_enclosing_quad() need to be called before anything else
         */
        void create_enclosing_rectangle(
            double x1, double y1, double x2, double y2
        ) {
            create_enclosing_quad(
                ExactPoint(vec2(x1,y1)),
                ExactPoint(vec2(x2,y1)),
                ExactPoint(vec2(x2,y2)),
                ExactPoint(vec2(x1,y2))
            );
        }

        /**
         * \brief Gets a point by vertex index
         * \param[in] v vertex index
         * \return the point at index \p v
         */
        const ExactPoint& point(index_t v) const {
            geo_debug_assert(v < nv());
            return point_[v];
        }

        /**
         * \brief Gets a vertex id by vertex index
         * \param[in] v vertex index
         * \return the point at index \p v
         */
        index_t vertex_id(index_t v) const {
            geo_debug_assert(v < nv());
            return id_[v];
        }

        /**
         * \brief Sets a vertex id by vertex index
         * \param[in] v vertex index
         * \param[in] id vertex id
         */
        void set_vertex_id(index_t v, index_t id) {
            geo_debug_assert(v < nv());
            id_[v] = id;
        }

        /**
         * \brief Used by 2D CSG operations, discards triangles according
         *  to a boolean operation.
         * \details Discards all the triangles that are outside the object
         *  defined by the boolean expression. It uses the operand bits
         *  associated with the constraints.
         * \param[in] boolean_expression a string with the boolean expression,
         *  as defined by BooleanExpression constructor. Each variable
         *  corresponds to an operand bit associated with the constraints. There
         *  can be up to 32 operands.
         * \param mark_only if set, triangles to be discarded are marked (but
         *  not discarded)
         * \see BooleanExpression, insert_constraint()
         */
        void classify_triangles(
            const std::string& boolean_expression, bool mark_only=false
        );

        /**
         * \see CDTBase2d::save()
         */
        void save(const std::string& filename) const override;

    protected:
        void add_point(const ExactPoint& p, index_t id = NO_INDEX);
        void begin_insert_transaction() override;
        void commit_insert_transaction() override;
        void rollback_insert_transaction() override;

        /**
         * \copydoc CDTBase2d::orient_2d()
         */
        Sign orient2d(index_t i, index_t j, index_t k) const override;

        /**
         * \copydoc CDTBase2d::incircle()
         */
        Sign incircle(index_t i,index_t j,index_t k,index_t l) const override;

        /**
         * \copydoc CDTBase2d::create_intersection()
         */
        index_t create_intersection(
            index_t E1, index_t i, index_t j,
            index_t E2, index_t k, index_t l
        ) override;

    protected:
        vector<ExactPoint> point_;
#ifndef GEOGRAM_USE_EXACT_NT
        vector<double> length_;
#endif
        vector<index_t> id_;
        vector<index_t> cnstr_operand_bits_;
        vector<index_t> facet_inclusion_bits_;
        mutable std::map<trindex, Sign> pred_cache_;
        bool use_pred_cache_insert_buffer_;
        mutable std::vector<std::pair<trindex, Sign>> pred_cache_insert_buffer_;
        vector<bindex> constraints_;
    };

    /*****************************************************************/

}

#endif
