// Boost.Geometry (aka GGL, Generic Geometry Library)

// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.

// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_TANGENCIES_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_TANGENCIES_HPP

#include <algorithm>

#include <boost/geometry/algorithms/detail/ring_identifier.hpp>
#include <boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp>
#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
#include <boost/geometry/algorithms/detail/recalculate.hpp>

#include <boost/geometry/policies/robustness/robust_point_type.hpp>
#include <boost/geometry/policies/robustness/segment_ratio_type.hpp>
#include <boost/geometry/policies/robustness/robust_type.hpp>

#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES)
#include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp>
#endif

#include <boost/geometry/geometries/point.hpp>
#include <boost/geometry/geometries/segment.hpp>


// TODO: the approach below should be completely replaced by the new
// get_left_turns, to keep the outgoing vector which has open space one of its
// sides.


namespace boost { namespace geometry
{

#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace overlay
{


template
<
    typename TurnPoints,
    typename Indexed,
    typename Geometry1, typename Geometry2,
    typename RobustPolicy,
    bool Reverse1, bool Reverse2,
    typename Strategy
>
struct sort_in_cluster
{
    inline sort_in_cluster(TurnPoints const& turn_points
            , Geometry1 const& geometry1
            , Geometry2 const& geometry2
            , RobustPolicy const& robust_policy
            , Strategy const& strategy)
        : m_turn_points(turn_points)
        , m_geometry1(geometry1)
        , m_geometry2(geometry2)
        , m_rescale_policy(robust_policy)
        , m_strategy(strategy)
    {}

private :

    TurnPoints const& m_turn_points;
    Geometry1 const& m_geometry1;
    Geometry2 const& m_geometry2;
    RobustPolicy const& m_rescale_policy;
    Strategy const& m_strategy;

    typedef typename Indexed::type turn_operation_type;
    typedef typename geometry::point_type<Geometry1>::type point_type;

    typedef typename geometry::robust_point_type
    <
        point_type,
        RobustPolicy
    >::type robust_point_type;

    inline bool default_order(Indexed const& left, Indexed const& right) const
    {
        // We've nothing to sort on. Take the indexes
        return left.turn_index < right.turn_index;
    }

    // Still necessary in some situations,
    // for example #case_102_multi, #case_107_multi, #case_recursive_boxes_3
    inline void get_situation_map(Indexed const& left, Indexed const& right,
                              robust_point_type& pi_rob, robust_point_type& pj_rob,
                              robust_point_type& ri_rob, robust_point_type& rj_rob,
                              robust_point_type& si_rob, robust_point_type& sj_rob) const
    {
        point_type pi, pj, ri, rj, si, sj;

        geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2,
            left.subject->seg_id,
            pi, pj);
        geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2,
            *left.other_seg_id,
            ri, rj);
        geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2,
            *right.other_seg_id,
            si, sj);

        geometry::recalculate(pi_rob, pi, m_rescale_policy);
        geometry::recalculate(pj_rob, pj, m_rescale_policy);
        geometry::recalculate(ri_rob, ri, m_rescale_policy);
        geometry::recalculate(rj_rob, rj, m_rescale_policy);
        geometry::recalculate(si_rob, si, m_rescale_policy);
        geometry::recalculate(sj_rob, sj, m_rescale_policy);
    }

#if BOOST_GEOMETRY_HANDLE_TANGENCIES_WITH_OVERLAP_INFO
    // This method was still called but did no effect whatsoever on the results,
    // with or without robustness-rescaling.
    // Probable cause: we rescale in this file ourselves, ignoring passed policy
    // TODO: check this more.
    // Besides this, it currently does not compile for yet unknown reasons
    // (does not find specialization for segment_ratio_type)
    // It is currently only called from the Unit Test "multi_intersection.cpp"

    // Determine how p/r and p/s are located.
    inline void overlap_info(
        robust_point_type const& pi, robust_point_type const& pj,
        robust_point_type const& ri, robust_point_type const& rj,
        robust_point_type const& si, robust_point_type const& sj,
        bool& pr_overlap, bool& ps_overlap, bool& rs_overlap) const
    {
        // Determine how p/r and p/s are located.
        // One of them is coming from opposite direction.

        typedef segment_intersection_points
                <
                    point_type,
                    typename segment_ratio_type
                    <
                        point_type, RobustPolicy
                    >::type
                > intersection_return_type;

        typedef strategy::intersection::relate_cartesian_segments
            <
                policies::relate::segments_intersection_points
                    <
                        intersection_return_type
                    >
            > policy;

        typedef model::referring_segment<robust_point_type const> segment_type;
        segment_type p(pi, pj);
        segment_type r(ri, rj);
        segment_type s(si, sj);

        // Get the intersection point (or two points)
        intersection_return_type pr = policy::apply(p, r, m_rescale_policy, pi, pj, ri, rj);
        intersection_return_type ps = policy::apply(p, s, m_rescale_policy, pi, pj, si, sj);
        intersection_return_type rs = policy::apply(r, s, m_rescale_policy, ri, rj, si, sj);

        // Check on overlap
        pr_overlap = pr.count == 2;
        ps_overlap = ps.count == 2;
        rs_overlap = rs.count == 2;
    }
#endif


#ifdef BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES
    inline void debug_consider(int order, Indexed const& left,
            Indexed const& right, std::string const& header,
            bool skip = true,
            std::string const& extra = "", bool ret = false
        ) const
    {
        if (skip) return;

        std::cout << "Case: " << header << " for " << left.turn_index << " / " << right.turn_index << std::endl;

        robust_point_type pi, pj, ri, rj, si, sj;
        get_situation_map(left, right, pi, pj, ri, rj, si, sj);

#if BOOST_GEOMETRY_HANDLE_TANGENCIES_WITH_OVERLAP_INFO
        bool prc = false, psc = false, rsc = false;
        overlap_info(pi, pj, ri, rj, si, sj, prc, psc, rsc);
#endif

        int const side_ri_p = m_strategy.apply(pi, pj, ri);
        int const side_rj_p = m_strategy.apply(pi, pj, rj);
        int const side_si_p = m_strategy.apply(pi, pj, si);
        int const side_sj_p = m_strategy.apply(pi, pj, sj);
        int const side_si_r = m_strategy.apply(ri, rj, si);
        int const side_sj_r = m_strategy.apply(ri, rj, sj);

#ifdef BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES_MORE
        std::cout << " Segment p:" << geometry::wkt(pi) << " .. " << geometry::wkt(pj) << std::endl;
        std::cout << " Segment r:" << geometry::wkt(ri) << " .. " << geometry::wkt(rj) << std::endl;
        std::cout << " Segment s:" << geometry::wkt(si) << " .. " << geometry::wkt(sj) << std::endl;

        std::cout << " r//p: " << side_ri_p << " / " << side_rj_p << std::endl;
        std::cout << " s//p: " << side_si_p << " / " << side_sj_p << std::endl;
        std::cout << " s//r: " << side_si_r << " / " << side_sj_r << std::endl;
#endif

        std::cout << header
                //<< " order: " << order
                << " ops: " << operation_char(left.subject->operation)
                    << "/" << operation_char(right.subject->operation)
                << " ri//p: " << side_ri_p
                << " si//p: " << side_si_p
                << " si//r: " << side_si_r
#if BOOST_GEOMETRY_HANDLE_TANGENCIES_WITH_OVERLAP_INFO
                << " cnts: " << int(prc) << ","  << int(psc) << "," << int(rsc)
#endif
                //<< " idx: " << left.turn_index << "/" << right.turn_index
                ;

        if (! extra.empty())
        {
            std::cout << " " << extra << " " << (ret ? "true" : "false");
        }
        std::cout << std::endl;
    }
#else
    inline void debug_consider(int, Indexed const& ,
            Indexed const& , std::string const& ,
            bool = true,
            std::string const& = "", bool = false
        ) const
    {}
#endif


    // ux/ux
    inline bool consider_ux_ux(Indexed const& left,
            Indexed const& right
            , std::string const& // header
        ) const
    {
        bool ret = left.turn_index < right.turn_index;

        // In combination of u/x, x/u: take first union, then blocked.
        // Solves #88, #61, #56, #80
        if (left.subject->operation == operation_union
            && right.subject->operation == operation_blocked)
        {
            ret = true;
        }
        else if (left.subject->operation == operation_blocked
            && right.subject->operation == operation_union)
        {
            ret = false;
        }
        else
        {
#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES)
            std::cout << "ux/ux unhandled" << std::endl;
#endif
        }

        //debug_consider(0, left, right, header, false, "-> return ", ret);

        return ret;
    }

    inline bool consider_iu_ux(Indexed const& left,
            Indexed const& right,
            int order // 1: iu first, -1: ux first
            , std::string const& // header
        ) const
    {
        bool ret = false;

        if (left.subject->operation == operation_union
            && right.subject->operation == operation_union)
        {
            ret = order == 1;
        }
        else if (left.subject->operation == operation_union
            && right.subject->operation == operation_blocked)
        {
            ret = true;
        }
        else if (right.subject->operation == operation_union
            && left.subject->operation == operation_blocked)
        {
            ret = false;
        }
        else if (left.subject->operation == operation_union)
        {
            ret = true;
        }
        else if (right.subject->operation == operation_union)
        {
            ret = false;
        }
        else
        {
#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES)
            // this still happens in the traverse.cpp test
            std::cout << " iu/ux unhandled" << std::endl;
#endif
            ret = order == 1;
        }

        //debug_consider(0, left, right, header, false, "-> return", ret);
        return ret;
    }

    inline bool consider_iu_ix(Indexed const& left,
            Indexed const& right,
            int order // 1: iu first, -1: ix first
            , std::string const& // header
        ) const
    {
        //debug_consider(order, left, right, header, false, "iu/ix");

        return left.subject->operation == operation_intersection
                && right.subject->operation == operation_intersection ? order == 1
            : left.subject->operation == operation_intersection ? false
            : right.subject->operation == operation_intersection ? true
            : order == 1;
    }

    inline bool consider_ix_ix(Indexed const& left, Indexed const& right
            , std::string const& // header
            ) const
    {
        // Take first intersection, then blocked.
        if (left.subject->operation == operation_intersection
            && right.subject->operation == operation_blocked)
        {
            return true;
        }
        else if (left.subject->operation == operation_blocked
            && right.subject->operation == operation_intersection)
        {
            return false;
        }

        // Default case, should not occur

#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES)
        std::cout << "ix/ix unhandled" << std::endl;
#endif
        //debug_consider(0, left, right, header, false, "-> return", ret);

        return default_order(left, right);
    }


    inline bool consider_iu_iu(Indexed const& left, Indexed const& right,
                    std::string const& header, bool redo = false) const
    {
        //debug_consider(0, left, right, header);

        // In general, order it like "union, intersection".
        if (left.subject->operation == operation_intersection
            && right.subject->operation == operation_union)
        {
            //debug_consider(0, left, right, header, false, "i,u", false);
            return false;
        }
        else if (left.subject->operation == operation_union
            && right.subject->operation == operation_intersection)
        {
            //debug_consider(0, left, right, header, false, "u,i", true);
            return true;
        }

        robust_point_type pi, pj, ri, rj, si, sj;
        get_situation_map(left, right, pi, pj, ri, rj, si, sj);

        int const side_ri_p = m_strategy.apply(pi, pj, ri);
        int const side_si_p = m_strategy.apply(pi, pj, si);
        int const side_si_r = m_strategy.apply(ri, rj, si);

        // Both located at same side (#58, pie_21_7_21_0_3)
        if (side_ri_p * side_si_p == 1 && side_si_r != 0)
        {
            if (left.subject->operation == operation_union
                && right.subject->operation == operation_union)
            {
                int const side_ri_s = m_strategy.apply(si, sj, ri);
                if (side_si_r == side_ri_s)
                {
                    return default_order(left, right);
                }

                // Take the most left one
                bool const ret = side_si_r == 1;
                //debug_consider(0, left, right, header, false, "same side", ret);
                return ret;
            }
        }


        // Coming from opposite sides (#59, #99)
        if (side_ri_p * side_si_p == -1)
        {
            bool ret = false;

            {
                ret = side_ri_p == 1; // #100
                debug_consider(0, left, right, header, false, "opp.", ret);
                return ret;
            }
#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES)
            std::cout << " iu/iu coming from opposite unhandled" << std::endl;
#endif
        }

#if BOOST_GEOMETRY_HANDLE_TANGENCIES_WITH_OVERLAP_INFO
        // We need EXTRA information here: are p/r/s overlapping?
        bool pr_ov = false, ps_ov = false, rs_ov = false;
        overlap_info(pi, pj, ri, rj, si, sj, pr_ov, ps_ov, rs_ov);
#else
        // std::cout << "Boost.Geometry: skipping overlap_info" << std::endl;
#endif

        // One coming from right (#83,#90)
        // One coming from left (#90, #94, #95)
        if (side_si_r != 0 && (side_ri_p != 0 || side_si_p != 0))
        {
            int const side_ri_s = m_strategy.apply(si, sj, ri);
            if (side_si_r == side_ri_s)
            {
                return default_order(left, right);
            }

            bool ret = false;

#if BOOST_GEOMETRY_HANDLE_TANGENCIES_WITH_OVERLAP_INFO
            if (pr_ov || ps_ov)
            {
                int r = side_ri_p != 0 ? side_ri_p : side_si_p;
                ret = r * side_si_r == 1;
            }
            else
#endif
            {
                ret = side_si_r == 1;
            }

            debug_consider(0, left, right, header, false, "left or right", ret);
            return ret;
        }

        // All aligned (#92, #96)
        if (side_ri_p == 0 && side_si_p == 0 && side_si_r == 0)
        {
            // One of them is coming from opposite direction.

            // Take the one NOT overlapping
            bool ret = false;
            bool found = false;
#if BOOST_GEOMETRY_HANDLE_TANGENCIES_WITH_OVERLAP_INFO
            if (pr_ov && ! ps_ov)
            {
                ret = true;
                found = true;
            }
            else if (!pr_ov && ps_ov)
            {
                ret = false;
                found = true;
            }
#endif

            debug_consider(0, left, right, header, false, "aligned", ret);
            if (found)
            {
                return ret;
            }
        }

#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES)
        std::cout << " iu/iu unhandled" << std::endl;
        debug_consider(0, left, right, header, false, "unhandled", left.turn_index < right.turn_index);
#endif
        if (! redo)
        {
            // In some cases behaviour is not symmetrical. TODO: fix this properly
            //   OR: alternatively we might consider calling all these functions one-way anyway
            return ! consider_iu_iu(right, left, header, true);
        }

        return default_order(left, right);
    }

    inline bool consider_ii(Indexed const& left, Indexed const& right,
                    std::string const& header) const
    {
        debug_consider(0, left, right, header);

        robust_point_type pi, pj, ri, rj, si, sj;
        get_situation_map(left, right, pi, pj, ri, rj, si, sj);

        int const side_ri_p = m_strategy.apply(pi, pj, ri);
        int const side_si_p = m_strategy.apply(pi, pj, si);

        // Two other points are (mostly) lying both right of the considered segment
        // Take the most left one
        int const side_si_r = m_strategy.apply(ri, rj, si);
        if (side_ri_p == -1
            && side_si_p == -1
            && side_si_r != 0)
        {
            bool const ret = side_si_r != 1;
            return ret;
        }
        return default_order(left, right);
    }


public :
    inline bool operator()(Indexed const& left, Indexed const& right) const
    {
        if ((m_turn_points[left.turn_index].discarded || left.discarded)
            && (m_turn_points[right.turn_index].discarded || right.discarded))
        {
            return default_order(left, right);
        }
        else if (m_turn_points[left.turn_index].discarded || left.discarded)
        {
            // Be careful to sort discarded first, then all others
            return true;
        }
        else if (m_turn_points[right.turn_index].discarded || right.discarded)
        {
            // See above so return false here such that right (discarded)
            // is sorted before left (not discarded)
            return false;
        }
        else if (m_turn_points[left.turn_index].combination(operation_blocked, operation_union)
                && m_turn_points[right.turn_index].combination(operation_blocked, operation_union))
        {
            // ux/ux
            return consider_ux_ux(left, right, "ux/ux");
        }
        else if (m_turn_points[left.turn_index].both(operation_union)
            && m_turn_points[right.turn_index].both(operation_union))
        {
            // uu/uu, Order is arbitrary
            // Note: uu/uu is discarded now before so this point will
            //       not be reached.
            return default_order(left, right);
        }
        else if (m_turn_points[left.turn_index].combination(operation_intersection, operation_union)
                && m_turn_points[right.turn_index].combination(operation_intersection, operation_union))
        {
            return consider_iu_iu(left, right, "iu/iu");
        }
        else if (m_turn_points[left.turn_index].combination(operation_intersection, operation_blocked)
                && m_turn_points[right.turn_index].combination(operation_intersection, operation_blocked))
        {
            return consider_ix_ix(left, right, "ix/ix");
        }
        else if (m_turn_points[left.turn_index].both(operation_intersection)
                && m_turn_points[right.turn_index].both(operation_intersection))
        {
            return consider_ii(left, right, "ii/ii");
        }
        else  if (m_turn_points[left.turn_index].combination(operation_union, operation_blocked)
                && m_turn_points[right.turn_index].combination(operation_intersection, operation_union))
        {
            return consider_iu_ux(left, right, -1, "ux/iu");
        }
        else if (m_turn_points[left.turn_index].combination(operation_intersection, operation_union)
                && m_turn_points[right.turn_index].combination(operation_union, operation_blocked))
        {
            return consider_iu_ux(left, right, 1, "iu/ux");
        }
        else  if (m_turn_points[left.turn_index].combination(operation_intersection, operation_blocked)
                && m_turn_points[right.turn_index].combination(operation_intersection, operation_union))
        {
            return consider_iu_ix(left, right, 1, "ix/iu");
        }
        else if (m_turn_points[left.turn_index].combination(operation_intersection, operation_union)
                && m_turn_points[right.turn_index].combination(operation_intersection, operation_blocked))
        {
            return consider_iu_ix(left, right, -1, "iu/ix");
        }
        else if (m_turn_points[left.turn_index].method != method_equal
            && m_turn_points[right.turn_index].method == method_equal
            )
        {
            // If one of them was EQUAL or CONTINUES, it should always come first
            return false;
        }
        else if (m_turn_points[left.turn_index].method == method_equal
            && m_turn_points[right.turn_index].method != method_equal
            )
        {
            return true;
        }

        // Now we have no clue how to sort.

#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES)
        std::cout << " Consider: " << operation_char(m_turn_points[left.turn_index].operations[0].operation)
                << operation_char(m_turn_points[left.turn_index].operations[1].operation)
                << "/" << operation_char(m_turn_points[right.turn_index].operations[0].operation)
                << operation_char(m_turn_points[right.turn_index].operations[1].operation)
                << " " << " Take " << left.turn_index << " < " << right.turn_index
                << std::endl;
#endif

        return default_order(left, right);
    }
};



template
<
    typename IndexType,
    typename Iterator,
    typename TurnPoints,
    typename Geometry1,
    typename Geometry2,
    typename Strategy
>
inline void inspect_cluster(Iterator begin_cluster, Iterator end_cluster,
            TurnPoints& turn_points,
            operation_type ,
            Geometry1 const& , Geometry2 const& ,
            Strategy const& )
{
    int count = 0;

    // Make an analysis about all occuring cases here.
    std::map<std::pair<operation_type, operation_type>, int> inspection;
    for (Iterator it = begin_cluster; it != end_cluster; ++it)
    {
        operation_type first = turn_points[it->turn_index].operations[0].operation;
        operation_type second = turn_points[it->turn_index].operations[1].operation;
        if (first > second)
        {
            std::swap(first, second);
        }
        inspection[std::make_pair(first, second)]++;
        count++;
    }


    bool keep_cc = false;

    // Decide about which is going to be discarded here.
    if (inspection[std::make_pair(operation_union, operation_union)] == 1
        && inspection[std::make_pair(operation_continue, operation_continue)] == 1)
    {
        // In case of uu/cc, discard the uu, that indicates a tangency and
        // inclusion would disturb the (e.g.) cc-cc-cc ordering
        // NOTE: uu is now discarded anyhow.
        keep_cc = true;
    }
    else if (count == 2
        && inspection[std::make_pair(operation_intersection, operation_intersection)] == 1
        && inspection[std::make_pair(operation_union, operation_intersection)] == 1)
    {
        // In case of ii/iu, discard the iu. The ii should always be visited,
        // Because (in case of not discarding iu) correctly ordering of ii/iu appears impossible
        for (Iterator it = begin_cluster; it != end_cluster; ++it)
        {
            if (turn_points[it->turn_index].combination(operation_intersection, operation_union))
            {
                it->discarded = true;
            }
        }
    }

    // Discard any continue turn, unless it is the only thing left
    //    (necessary to avoid cc-only rings, all being discarded
    //    e.g. traversal case #75)
    int nd_count= 0, cc_count = 0;
    for (Iterator it = begin_cluster; it != end_cluster; ++it)
    {
        if (! it->discarded)
        {
            nd_count++;
            if (turn_points[it->turn_index].both(operation_continue))
            {
                cc_count++;
            }
        }
    }

    if (nd_count == cc_count)
    {
        keep_cc = true;
    }

    if (! keep_cc)
    {
        for (Iterator it = begin_cluster; it != end_cluster; ++it)
        {
            if (turn_points[it->turn_index].both(operation_continue))
            {
                it->discarded = true;
            }
        }
    }
}


template
<
    typename IndexType,
    bool Reverse1, bool Reverse2,
    typename Iterator,
    typename TurnPoints,
    typename Geometry1,
    typename Geometry2,
    typename RobustPolicy,
    typename Strategy
>
inline void handle_cluster(Iterator begin_cluster, Iterator end_cluster,
            TurnPoints& turn_points,
            operation_type for_operation,
            Geometry1 const& geometry1, Geometry2 const& geometry2,
            RobustPolicy& robust_policy,
            Strategy const& strategy)
{
    // First inspect and (possibly) discard rows
    inspect_cluster<IndexType>(begin_cluster, end_cluster, turn_points,
            for_operation, geometry1, geometry2, strategy);


    // Then sort this range (discarded rows will be ordered first and will be removed in enrich_assign)
    std::sort(begin_cluster, end_cluster,
                sort_in_cluster
                    <
                        TurnPoints,
                        IndexType,
                        Geometry1, Geometry2,
                        RobustPolicy,
                        Reverse1, Reverse2,
                        Strategy
                    >(turn_points, geometry1, geometry2, robust_policy, strategy));

#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_TANGENCIES)
    typedef typename IndexType::type operations_type;
    operations_type const& op = turn_points[begin_cluster->turn_index].operations[begin_cluster->operation_index];
    std::cout << std::endl << "Clustered points on equal distance " << op.fraction << std::endl;

    std::cout << "->Indexes ";
    for (Iterator it = begin_cluster; it != end_cluster; ++it)
    {
        std::cout << " " << it->turn_index;
    }
    std::cout << std::endl << "->Methods: ";
    for (Iterator it = begin_cluster; it != end_cluster; ++it)
    {
        std::cout << " " << method_char(turn_points[it->turn_index].method);
    }
    std::cout << std::endl << "->Operations: ";
    for (Iterator it = begin_cluster; it != end_cluster; ++it)
    {
        std::cout << " " << operation_char(turn_points[it->turn_index].operations[0].operation)
            << operation_char(turn_points[it->turn_index].operations[1].operation);
    }
    std::cout << std::endl << "->Discarded: ";
    for (Iterator it = begin_cluster; it != end_cluster; ++it)
    {
        std::cout << " " << (it->discarded ? "true" : "false");
    }
    std::cout << std::endl;
        //<< "\tOn segments: "    << prev_op.seg_id  << " / "  << prev_op.other_id
        //<< " and "  << op.seg_id << " / " << op.other_id
        //<< geometry::distance(turn_points[prev->turn_index].point, turn_points[it->turn_index].point)
#endif

}


}} // namespace detail::overlay
#endif //DOXYGEN_NO_DETAIL


}} // namespace boost::geometry


#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_TANGENCIES_HPP
