// Copyright (c) 2006,2007,2009,2010,2011 Tel-Aviv University (Israel).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL: https://github.com/CGAL/cgal/blob/v6.2/Arrangement_on_surface_2/include/CGAL/Arr_Bezier_curve_traits_2.h $
// $Id: include/CGAL/Arr_Bezier_curve_traits_2.h cac3e9d75e2 $
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
// Author(s): Ron Wein     <wein@post.tau.ac.il>
//            Iddo Hanniel <iddoh@cs.technion.ac.il>
//            Waqar Khan   <wkhan@mpi-inf.mpg.de>
//            Efi Fogel    <efifogel@gmail.com>

#ifndef CGAL_ARR_BEZIER_CURVE_TRAITS_2_H
#define CGAL_ARR_BEZIER_CURVE_TRAITS_2_H

#include <CGAL/license/Arrangement_on_surface_2.h>
#include <CGAL/disable_warnings.h>

/*! \file
 * Definition of the Arr_Bezier_curve_traits_2 class.
 */

#include <CGAL/tags.h>
#include <CGAL/Arr_tags.h>
#include <CGAL/Arr_geometry_traits/Bezier_curve_2.h>
#include <CGAL/Arr_geometry_traits/Bezier_point_2.h>
#include <CGAL/Arr_geometry_traits/Bezier_x_monotone_2.h>
#include <CGAL/Arr_geometry_traits/Bezier_cache.h>
#include <CGAL/Arr_geometry_traits/Bezier_bounding_rational_traits.h>

namespace CGAL {

/*! \class
 * A traits class for maintaining an arrangement of Bezier curves with
 * rational control points.
 *
 * The class is templated with four parameters:
 * Rat_kernel A kernel that defines the type of control points.
 * Alg_kernel A geometric kernel, where Alg_kernel::FT is the number type
 *            for the coordinates of arrangement vertices and is used to
 *            represent algebraic numbers.
 * Nt_traits A number-type traits class. This class defines the Rational
 *           number type (should be the same as Rat_kernel::FT) and the
 *           Algebraic number type (should be the same as Alg_kernel::FT)
 *           and supports various operations on them.
 * Bounding_traits A traits class for filtering the exact computations.
 *                 By default we use the rational bounding traits.
 */
template <typename RatKernel_, typename AlgKernel_, typename NtTraits_,
          typename BoundingTraits_ = Bezier_bounding_rational_traits<RatKernel_> >
class Arr_Bezier_curve_traits_2 {
public:
  using Rat_kernel = RatKernel_;
  using Alg_kernel = AlgKernel_;
  using Nt_traits = NtTraits_;
  using Bounding_traits = BoundingTraits_;
  using Self = Arr_Bezier_curve_traits_2<Rat_kernel, Alg_kernel, Nt_traits, Bounding_traits>;

  using Integer = typename Nt_traits::Integer;
  using Rational = typename Rat_kernel::FT;
  using Algebraic = typename Alg_kernel::FT;

  using Rat_point_2 = typename Rat_kernel::Point_2;
  using Alg_point_2 = typename Alg_kernel::Point_2;

  // Category tags:
  using Has_left_category = Tag_true;
  using Has_merge_category = Tag_true;

  using Left_side_category = Arr_oblivious_side_tag;
  using Bottom_side_category = Arr_oblivious_side_tag;
  using Top_side_category = Arr_oblivious_side_tag;
  using Right_side_category = Arr_oblivious_side_tag;

  // Traits-class types:
  using Curve_2 = _Bezier_curve_2<Rat_kernel, Alg_kernel, Nt_traits, Bounding_traits>;

  using X_monotone_curve_2 = _Bezier_x_monotone_2<Rat_kernel, Alg_kernel, Nt_traits, Bounding_traits>;

  using Point_2 = _Bezier_point_2<Rat_kernel, Alg_kernel, Nt_traits, Bounding_traits>;

  using Multiplicity = typename X_monotone_curve_2::Multiplicity;

  // Type definition for the vertical-tangnecy and intersection point cache.
  using Bezier_cache = _Bezier_cache<Nt_traits>;

private:

  // Type definition for the bounded intersection points mapping.
  using Intersection_map = typename X_monotone_curve_2::Intersection_map;

  // Data members:
  mutable Bezier_cache* p_cache;          /*!< Caches vertical tangency points
                                           * and intersection points that have
                                           * been computed in an exact manner.
                                           */
  mutable Intersection_map* p_inter_map;  /*!< Maps curve pairs to their
                                           * intersection points.
                                           */
  bool m_owner;                           /*!< Does this instance own its cache
                                           * and map structures.
                                           */

public:
  /// \name Construction.
  //@{

  /*! constructs default. */
  Arr_Bezier_curve_traits_2() {
    p_cache = new Bezier_cache;
    p_inter_map = new Intersection_map;
    m_owner = true;
  }

  /*! constructs copy. */
  Arr_Bezier_curve_traits_2(const Self& tr) :
    p_cache(tr.p_cache),
    p_inter_map(tr.p_inter_map),
    m_owner(false)
  {}

  /*! assigns. */
  Self& operator= (const Self& tr) {
    if (this == &tr) return (*this);

    p_cache = tr.p_cache;
    p_inter_map = tr.p_inter_map;
    m_owner = false;
    return (*this);
  }

  /*! destructs. */
  ~Arr_Bezier_curve_traits_2() {
    if (m_owner) {
      delete p_cache;
      delete p_inter_map;
    }
    p_cache = nullptr;
    p_inter_map = nullptr;
  }
  //@}

  /// \name Basic functor definitions for the arrangement traits
  //@{

  /*! \class Compare_x_2
   * The Compare_x_2 functor.
   */
  class Compare_x_2 {
  private:
    const Bezier_cache* p_cache;

  public:
    /*! constructs. */
    Compare_x_2(const Bezier_cache* cache) : p_cache (cache) {}

    /*! compares the x-coordinates of two points.
     * \param p1 The first point.
     * \param p2 The second point.
     * \return LARGER if x(p1) > x(p2);
     *         SMALLER if x(p1) < x(p2);
     *         EQUAL if x(p1) = x(p2).
     */
    Comparison_result operator()(const Point_2& p1, const Point_2& p2) const
    { return (p1.compare_x(p2, const_cast<Bezier_cache&> (*p_cache))); }
  };

  /*! obtains a `Compare_x_2` functor object. */
  Compare_x_2 compare_x_2_object() const { return (Compare_x_2 (p_cache)); }

  /*! \class Compare_xy_2
   * The Compare_xy_2 functor.
   */
  class Compare_xy_2 {
  private:
    const Bezier_cache* p_cache;

  public:
    /*! constructs. */
    Compare_xy_2(const Bezier_cache* cache) : p_cache (cache) {}

    /*! compares two points lexigoraphically: by x, then by y.
     * \param p1 The first point.
     * \param p2 The second point.
     * \return LARGER if x(p1) > x(p2), or if x(p1) = x(p2) and y(p1) > y(p2);
     *         SMALLER if x(p1) < x(p2), or if x(p1) = x(p2) and y(p1) < y(p2);
     *         EQUAL if the two points are equal.
     */
    Comparison_result operator()(const Point_2& p1, const Point_2& p2) const
    { return (p1.compare_xy(p2, const_cast<Bezier_cache&> (*p_cache))); }
  };

  /*! obtains a `Compare_xy_2` functor object. */
  Compare_xy_2 compare_xy_2_object() const { return (Compare_xy_2 (p_cache)); }

  /*! \class Construct_min_vertex_2
   * The Construct_min_vertex_2 functor.
   */
  class Construct_min_vertex_2 {
  public:
    /*! obtains the left endpoint of the x-monotone curve (segment).
     * \param cv The curve.
     * \return The left endpoint.
     */
    const Point_2& operator()(const X_monotone_curve_2 & cv) const { return (cv.left()); }
  };

  /*! obtains a `Construct_min_vertex_2` functor object. */
  Construct_min_vertex_2 construct_min_vertex_2_object() const { return Construct_min_vertex_2(); }

  /*! \class Construct_max_vertex_2
   * The Construct_max_vertex_2 functor.
   */
  class Construct_max_vertex_2 {
  public:
    /*! obtains the right endpoint of the x-monotone curve (segment).
     * \param cv The curve.
     * \return The right endpoint.
     */
    const Point_2& operator()(const X_monotone_curve_2 & cv) const { return (cv.right()); }
  };

  /*! obtains a `Construct_max_vertex_2` functor object. */
  Construct_max_vertex_2 construct_max_vertex_2_object() const { return Construct_max_vertex_2(); }

  /*! \class Is_vertical_2
   * The Is_vertical_2 functor.
   */
  class Is_vertical_2 {
  public:
    /*! checks whether the given x-monotone curve is a vertical segment.
     * \param cv The curve.
     * \return (true) if the curve is a vertical segment; (false) otherwise.
     */
    bool operator()(const X_monotone_curve_2& cv) const { return (cv.is_vertical()); }
  };

  /*! obtains an `Is_vertical_2` functor object. */
  Is_vertical_2 is_vertical_2_object() const { return Is_vertical_2(); }

  /*! \class Compare_y_at_x_2
   * The Compare_y_at_x_2 functor.
   */
  class Compare_y_at_x_2 {
  private:
    const Bezier_cache* p_cache;

  public:
    /*! constructs. */
    Compare_y_at_x_2(const Bezier_cache* cache) : p_cache(cache) {}

    /*! returns the location of the given point with respect to the input curve.
     * \param cv The curve.
     * \param p The point.
     * \pre p is in the x-range of cv.
     * \return SMALLER if y(p) < cv(x(p)), i.e. the point is below the curve;
     *         LARGER if y(p) > cv(x(p)), i.e. the point is above the curve;
     *         EQUAL if p lies on the curve.
     */
    Comparison_result operator()(const Point_2& p, const X_monotone_curve_2& cv) const
    { return (cv.point_position(p, const_cast<Bezier_cache&>(*p_cache))); }
  };

  /*! obtains a `Compare_y_at_x_2` functor object. */
  Compare_y_at_x_2 compare_y_at_x_2_object() const { return (Compare_y_at_x_2(p_cache)); }

  /*! \class Compare_y_at_x_left_2
   * The Compare_y_at_x_left_2 functor.
   */
  class Compare_y_at_x_left_2 {
  private:
    const Bezier_cache* p_cache;

  public:
    /*! constructs. */
    Compare_y_at_x_left_2 (const Bezier_cache* cache) : p_cache(cache) {}

    /*! compares the y value of two x-monotone curves immediately to the left
     * of their intersection point.
     * \param cv1 The first curve.
     * \param cv2 The second curve.
     * \param p The intersection point.
     * \pre The point p lies on both curves, and both of them must be also be
     *      defined (lexicographically) to its left.
     * \return The relative position of cv1 with respect to cv2 immdiately to
     *         the left of p: SMALLER, LARGER or EQUAL.
     */
    Comparison_result operator()(const X_monotone_curve_2& cv1,
                                 const X_monotone_curve_2& cv2,
                                 const Point_2& p) const
    { return (cv1.compare_to_left(cv2, p, const_cast<Bezier_cache&>(*p_cache))); }
  };

  /*! obtains a `Compare_y_at_x_left_2` functor object. */
  Compare_y_at_x_left_2 compare_y_at_x_left_2_object() const
  { return (Compare_y_at_x_left_2(p_cache)); }

  /*! \class Compare_y_at_x_right_2
   * The Compare_y_at_x_right_2 functor.
   */
  class Compare_y_at_x_right_2 {
  private:
    const Bezier_cache* p_cache;

  public:
    /*! constructs. */
    Compare_y_at_x_right_2 (const Bezier_cache* cache) : p_cache(cache) {}

    /*! compares the y value of two x-monotone curves immediately to the right
     * of their intersection point.
     * \param cv1 The first curve.
     * \param cv2 The second curve.
     * \param p The intersection point.
     * \pre The point p lies on both curves, and both of them must be also be
     *      defined (lexicographically) to its right.
     * \return The relative position of cv1 with respect to cv2 immdiately to
     *         the right of p: SMALLER, LARGER or EQUAL.
     */
    Comparison_result operator()(const X_monotone_curve_2& cv1,
                                 const X_monotone_curve_2& cv2,
                                 const Point_2& p) const
    { return (cv1.compare_to_right(cv2, p, const_cast<Bezier_cache&>(*p_cache))); }
  };

  /*! obtains a `Compare_y_at_x_right_2` functor object. */
  Compare_y_at_x_right_2 compare_y_at_x_right_2_object() const
  { return (Compare_y_at_x_right_2(p_cache)); }

  /*! \class Equal_2
   * The Equal_2 functor.
   */
  class Equal_2 {
  private:
    const Bezier_cache* p_cache;

  public:
    /*! constructs. */
    Equal_2(const Bezier_cache* cache) : p_cache(cache) {}

    /*! checks if the two x-monotone curves are the same (have the same graph).
     * \param cv1 The first curve.
     * \param cv2 The second curve.
     * \return (true) if the two curves are the same; (false) otherwise.
     */
    bool operator()(const X_monotone_curve_2& cv1, const X_monotone_curve_2& cv2) const
    { return (cv1.equals(cv2, const_cast<Bezier_cache&>(*p_cache))); }

    /*! checks if the two points are the same.
     * \param p1 The first point.
     * \param p2 The second point.
     * \return (true) if the two point are the same; (false) otherwise.
     */
    bool operator()(const Point_2& p1, const Point_2& p2) const
    { return (p1.equals(p2, const_cast<Bezier_cache&>(*p_cache))); }
  };

  /*! obtains an `Equal_2` functor object. */
  Equal_2 equal_2_object() const { return (Equal_2(p_cache)); }

  /*! \class Do_intersect
   * A functor for intersection detection
   */
  class Do_intersect_2 {
  protected:
    using Traits = Arr_Bezier_curve_traits_2<Rat_kernel, Alg_kernel, Nt_traits, Bounding_traits>;

    /*! The traits (in case it has state) */
    const Traits& m_traits;

    /*! constructs.
     * \param traits The traits.
     */
    Do_intersect_2(const Traits& traits) : m_traits(traits) {}

    friend class Arr_Bezier_curve_traits_2<Rat_kernel, Alg_kernel, Nt_traits, Bounding_traits>;

  public:
    /*! determines whether two given \f$x\f$-monotone curves intersect.
     * \param xcv1 the first curve.
     * \param xcv2 the second curve.
     * \param consider_common_endpoints indicates whether common endpoints should be counted as intersections.
     * \return `true` if `consider_common_endpoints` is true and `xcv1` and `xcv2` intersect or if
     *  `consider_common_endpoints` is `false and at least one of the interiors of `xcv1` and `xcv2` intersect,
     *   and `false` otherwise.
     * \todo Reimplement without using Intersect_2 to make robust (and efficient) with inexact constructions.
     */
    bool operator()(const X_monotone_curve_2& xcv1, const X_monotone_curve_2& xcv2,
                    bool consider_common_endpoints = true) const {
      using Intersection_point = std::pair<Point_2, Multiplicity>;
      using Intersection_result = std::variant<Intersection_point, X_monotone_curve_2>;
      std::vector<Intersection_result> intersections;
      m_traits.intersect_2_object()(xcv1, xcv2, std::back_inserter(intersections));
      if (consider_common_endpoints) return ! intersections.empty();

      // Check whether the open curves intersect

      // If the curves do not intersect at all, so endpoints do not matter
      if (intersections.empty()) return false;

      // If there are more than 2 intersections, return true
      if (intersections.size() > 2) return true;

      // If the intersection is an overlap, return true
      auto cmp_xy = m_traits.compare_xy_2_object();
      const Intersection_point* p_first_p = std::get_if<Intersection_point>(&(intersections.front()));
      if (! p_first_p) return true;

      auto ctr_min_vertex = m_traits.construct_min_vertex_2_object();
      auto ctr_max_vertex = m_traits.construct_max_vertex_2_object();

      // If the first intersection point of the curves is not an endpoint of the first curve, return true
      const auto& min_p1 = ctr_min_vertex(xcv1);
      const auto& max_p1 = ctr_max_vertex(xcv1);
      if ((cmp_xy(min_p1, p_first_p->first) != EQUAL) && (cmp_xy(max_p1, p_first_p->first) != EQUAL)) return true;

      // If the first intersection point of the curves is not an endpoint of the second curve, return true
      const auto& min_p2 = ctr_min_vertex(xcv2);
      const auto& max_p2 = ctr_max_vertex(xcv2);
      if ((cmp_xy(min_p2, p_first_p->first) != EQUAL) && (cmp_xy(max_p2, p_first_p->first) != EQUAL)) return true;

      // If there is only one intersection, it is an endpoint; return false
      if (intersections.size() == 1) return false;

      // repeat the above for the last point
      const Intersection_point* p_last_p = std::get_if<Intersection_point>(&(intersections.back()));
      if (! p_last_p) return true;
      if ((cmp_xy(min_p1, p_last_p->first) != EQUAL) && (cmp_xy(max_p1, p_last_p->first) != EQUAL)) return true;
      if ((cmp_xy(min_p2, p_last_p->first) != EQUAL) && (cmp_xy(max_p2, p_last_p->first) != EQUAL)) return true;
      return false;
    }
  };

  /*! obtains a `Do_intersect_2` functor object. */
  Do_intersect_2 do_intersect_2_object() const { return Do_intersect_2(*this); }
  //@}

  //! \name Intersections, subdivisions, and mergings
  //@{

  /*! \class Make_x_monotone_2
   * A functor for subdividing a curve into x-monotone curves.
   */
  class Make_x_monotone_2 {
  private:
    Bezier_cache* p_cache;

  public:
    /*! constructs. */
    Make_x_monotone_2(Bezier_cache* cache) : p_cache(cache) {}

    /*! subdivides a given Bezier curve into x-monotone subcurves and insert them
     * into a given output iterator.
     * \param cv the curve.
     * \param oi an output iterator for the result. Its value type is a variant
     *           that wraps Point_2 or an X_monotone_curve_2 objects.
     * \return the past-the-end iterator.
     */
    template <typename OutputIterator>
    OutputIterator operator()(const Curve_2& B, OutputIterator oi) const {
      using Make_x_monotone_result = std::variant<Point_2, X_monotone_curve_2>;
      using Vertical_tangency_point = typename Bounding_traits::Vertical_tangency_point;

      // Try to compute the bounds of the vertical tangency points.
      Bounding_traits bound_tr;
      typename Bounding_traits::Control_points cpts;
      std::list<Vertical_tangency_point> vpt_bounds;

      std::copy(B.control_points_begin(), B.control_points_end(), std::back_inserter(cpts));

      bound_tr.compute_vertical_tangency_points(cpts, std::back_inserter (vpt_bounds));

      // Construct Point_2 from bounded tangency points.
      std::list<Point_2> vpts;
      bool app_ok = true;

      for (auto iter = vpt_bounds.begin(); iter != vpt_bounds.end(); ++iter) {
        const typename Bounding_traits::Bez_point_bound& bound = iter->bound;
        const typename Bounding_traits::Bez_point_bbox& bbox = iter->bbox;

        if (! bound.can_refine) {
          // If we cannot refine the vertical-tangency bound anymore, then
          // we failed to bound the vertical tangency point.
          // \todo In the future, we might want to use the info.
          app_ok = false;
          break;
        }

        // Construct an approximate vertical tangency point.
        Point_2 pt;

        if (bound.type == Bounding_traits::Bez_point_bound::RATIONAL_PT) {
          CGAL_assertion(CGAL::compare(bound.t_min, bound.t_max) == EQUAL);
          Rational t0 = bound.t_min;

          pt = Point_2 (B, t0);
        }
        else {
          pt.add_originator(typename Point_2::Originator(B, bound));
        }
        pt.set_bbox(bbox);

        vpts.push_back(pt);
      }

      // If bounding the approximated vertical-tangency points went fine,
      // use these points as endpoint for the x-monotone subcurves.
      if (app_ok) {
        // Create the x-monotone subcurves with approximate endpoints.
        typename std::list<Point_2>::const_iterator pit;
        unsigned int  xid = 1;            // Serial number of the subcurve.
        Point_2       p0(B, xid, Rational(0)); // A rational start point.
        // Note: xid is needed in ctr of p0 (and of p1 below),
        // for handling end case of start point == end point.

        for (pit = vpts.begin(); pit != vpts.end(); ++pit) {
          *oi++ = Make_x_monotone_result(X_monotone_curve_2(B, xid, p0, *pit, *p_cache));
          xid++;
          p0 = *pit;
        }

        Point_2    p1(B, xid, Rational(1)); // A rational end point.
        *oi++ = Make_x_monotone_result(X_monotone_curve_2(B, xid, p0, p1, *p_cache));
        return (oi);
      }

      // If we reached here then we have to compute the vertical-tangency
      // points in an exact manner. We do this by considering all t-values
      // on B(t) = (X(t), Y(t)), such that X'(t) = 0.
      const typename Bezier_cache::Vertical_tangency_list&
        vt_list = p_cache->get_vertical_tangencies(B.id(), B.x_polynomial(), B.x_norm());

      // Create the x-monotone subcurves.
      Point_2 p1;
      unsigned int xid = 1;            // Serial number of the subcurve.
      Point_2 p0(B, xid, Rational(0));


      for (auto it = vt_list.begin(); it != vt_list.end(); ++it) {
        p1 = Point_2(B, *it);
        *oi++ = Make_x_monotone_result(X_monotone_curve_2(B, xid, p0, p1, *p_cache));
        xid++;
        p0 = p1;
      }

      // Create the final subcurve.
      p1 = Point_2(B, xid, Rational(1));
      *oi++ = Make_x_monotone_result(X_monotone_curve_2(B, xid, p0, p1, *p_cache));
      return oi;
    }
  };

  /*! obtains a `Make_x_monotone_2` functor object. */
  Make_x_monotone_2 make_x_monotone_2_object() const { return (Make_x_monotone_2(p_cache)); }

  /*! \class Split_2
   * The Split_2 functor.
   */
  class Split_2 {
  public:
    /*! splits a given x-monotone curve at a given point into two sub-curves.
     * \param cv The curve to split
     * \param p The split point.
     * \param c1 Output: The left resulting subcurve (p is its right endpoint).
     * \param c2 Output: The right resulting subcurve (p is its left endpoint).
     * \pre p lies on cv but is not one of its end-points.
     */
    void operator()(const X_monotone_curve_2& cv, const Point_2 & p,
                    X_monotone_curve_2& c1, X_monotone_curve_2& c2) const
    { cv.split(p, c1, c2); }
  };

  /*! obtains a `Split_2` functor object. */
  Split_2 split_2_object() const { return Split_2(); }

  /*! \class Intersect_2
   * The Intersect_2 functor.
   */
  class Intersect_2 {
  private:
    Bezier_cache* p_cache;
    Intersection_map* p_imap;

  public:
    /*! constructs. */
    Intersect_2(Bezier_cache* cache, Intersection_map* imap) :
      p_cache(cache),
      p_imap(imap)
    {}

    /*! finds the intersections of the two given curves and insert them to the
     * given output iterator.
     * \param cv1 The first curve.
     * \param cv2 The second curve.
     * \param oi The output iterator.
     * \return The past-the-end iterator.
     */
    template <typename OutputIterator>
    OutputIterator operator()(const X_monotone_curve_2& cv1,
                              const X_monotone_curve_2& cv2,
                              OutputIterator oi) const
    { return (cv1.intersect(cv2, *p_imap, *p_cache, oi)); }
  };

  /*! obtains an `Intersect_2` functor object. */
  Intersect_2 intersect_2_object() const { return (Intersect_2(p_cache, p_inter_map)); }

  /*! \class Are_mergeable_2
   * The Are_mergeable_2 functor.
   */
  class Are_mergeable_2 {
  public:
    /*! checks whether it is possible to merge two given x-monotone curves.
     * \param cv1 The first curve.
     * \param cv2 The second curve.
     * \return (true) if the two curves are mergeable - if they are supported
     *         by the same line and share a common endpoint; (false) otherwise.
     */
    bool operator()(const X_monotone_curve_2& cv1, const X_monotone_curve_2& cv2) const
    { return (cv1.can_merge_with (cv2)); }
  };

  /*! obtains an `Are_mergeable_2` functor object. */
  Are_mergeable_2 are_mergeable_2_object() const { return Are_mergeable_2(); }

  /*! \class Merge_2
   * A functor that merges two x-monotone arcs into one.
   */
  class Merge_2 {
    using Traits = Arr_Bezier_curve_traits_2<Rat_kernel, Alg_kernel, Nt_traits, Bounding_traits>;

    /*! The traits (in case it has state) */
    const Traits* m_traits;

    /*! constructs
     * \param traits the traits (in case it has state)
     */
    Merge_2(const Traits* traits) : m_traits(traits) {}

    friend class Arr_Bezier_curve_traits_2<Rat_kernel, Alg_kernel, Nt_traits, Bounding_traits>;

  public:
    /*! merges two given x-monotone curves into a single curve (segment).
     * \param cv1 The first curve.
     * \param cv2 The second curve.
     * \param c Output: The merged curve.
     * \pre The two curves are mergeable.
     */
    void operator()(const X_monotone_curve_2& cv1,
                    const X_monotone_curve_2& cv2,
                    X_monotone_curve_2& c) const {
      CGAL_precondition(m_traits->are_mergeable_2_object()(cv2, cv1));

      c = cv1.merge(cv2);
    }
  };

  /*! obtains a `Merge_2` functor object. */
  Merge_2 merge_2_object() const { return Merge_2(this); }
  //@}

  /// \name Functor definitions for the Boolean set-operation traits.
  //@{

  /*! \class Compare_endpoints_xy_2
   * The Compare_endpoints_xy_2 functor.
   */
  class Compare_endpoints_xy_2 {
  public:
    /*! compares the endpoints of an $x$-monotone curve lexicographically.
     * (assuming the curve has a designated source and target points).
     * \param cv The curve.
     * \return SMALLER if the curve is directed right;
     *         LARGER if the curve is directed left.
     */
    Comparison_result operator() (const X_monotone_curve_2& cv) const {
      if (cv.is_directed_right()) return (SMALLER);
      else return (LARGER);
    }
  };

  /*! obtains a `Compare_endpoints_xy_2` functor object. */
  Compare_endpoints_xy_2 compare_endpoints_xy_2_object() const { return Compare_endpoints_xy_2(); }

  class Trim_2 {
    using Traits = Arr_Bezier_curve_traits_2<Rat_kernel, Alg_kernel, Nt_traits, Bounding_traits>;

    /*! The traits (in case it has state) */
    const Traits& m_traits;

    /*! constructs
     * \param traits the traits (in case it has state)
     */
    Trim_2(const Traits& traits) : m_traits(traits) {}

    friend class Arr_Bezier_curve_traits_2<Rat_kernel, Alg_kernel,
                                           Nt_traits, Bounding_traits>;
    /*! returns a trimmed version of an arc
     * \param xcv The arc
     * \param src the new first endpoint
     * \param tgt the new second endpoint
     * \return The trimmed arc
     *
     * \pre src != tgt
     * \pre both points must be interior and must lie on \c cv
     */
  public:
    X_monotone_curve_2 operator()(const X_monotone_curve_2& xcv,
                                  const Point_2& src,
                                  const Point_2& tgt) const {
      // make functor objects
      CGAL_precondition_code(Compare_y_at_x_2 compare_y_at_x_2 =
                             m_traits.compare_y_at_x_2_object());
      CGAL_precondition_code(Equal_2 equal_2 = m_traits.equal_2_object());
      Compare_x_2 compare_x_2 = m_traits.compare_x_2_object();
      // Check whether source and taeget are two distinct points and they lie
      // on the line.
      CGAL_precondition(compare_y_at_x_2(src, xcv) == EQUAL);
      CGAL_precondition(compare_y_at_x_2(tgt, xcv) == EQUAL);
      CGAL_precondition(! equal_2(src, tgt));

      //check if the orientation conforms to the src and tgt.
      if (xcv.is_directed_right() && compare_x_2(src, tgt) == LARGER) return (xcv.trim(tgt, src));
      else if (! xcv.is_directed_right() && compare_x_2(src, tgt) == SMALLER) return (xcv.trim(tgt, src));
      else return (xcv.trim(src, tgt));
    }
  };

  /*! obtains a `Trim_2` functor object. */
  Trim_2 trim_2_object() const { return Trim_2(*this); }

  /*! \class Construct_opposite_2
   * The Construct_opposite_2 functor.
   */
  class Construct_opposite_2 {
  public:
    /*! constructs an opposite x-monotone curve (with swapped source and target).
     * \param cv The curve.
     * \return The opposite curve.
     */
    X_monotone_curve_2 operator()(const X_monotone_curve_2& cv) { return (cv.flip()); }
  };

  /*! obtains a `Construct_opposite_2` functor object. */
  Construct_opposite_2 construct_opposite_2_object() const { return Construct_opposite_2(); }

  //@}
};

} // namespace CGAL

#include <CGAL/enable_warnings.h>

#endif
