// Copyright (c) 1997-2002,2005 Max-Planck-Institute Saarbruecken (Germany).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL: https://github.com/CGAL/cgal/blob/v6.0.3/Nef_3/include/CGAL/Nef_polyhedron_3.h $
// $Id: include/CGAL/Nef_polyhedron_3.h cefe3007d59 $
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
//
// Author(s)     : Michael Seel    <seel@mpi-sb.mpg.de>
//                 Miguel Granados <granados@mpi-sb.mpg.de>
//                 Susan Hert      <hert@mpi-sb.mpg.de>
//                 Lutz Kettner    <kettner@mpi-sb.mpg.de>
//                 Ralf Osbild     <osbild@mpi-sb.mpg.de>
//                 Peter Hachenberger <hachenberger@mpi-sb.mpg.de>
#ifndef CGAL_NEF_POLYHEDRON_3_H
#define CGAL_NEF_POLYHEDRON_3_H

#include <CGAL/license/Nef_3.h>

#include <CGAL/disable_warnings.h>

#include <CGAL/basic.h>
#include <CGAL/Handle_for.h>
#include <CGAL/Nef_3/Default_items.h>
#include <CGAL/Nef_3/SNC_structure.h>
#include <CGAL/Nef_3/SNC_decorator.h>
#include <CGAL/Nef_3/SNC_const_decorator.h>
#include <CGAL/Nef_3/SNC_constructor.h>
#include <CGAL/Nef_3/SNC_external_structure.h>
#include <CGAL/Nef_3/Combine_with_halfspace.h>
#ifdef CGAL_NEF_VISUAL_HULL
#include <CGAL/Nef_3/Binary_operation_vs.h>
#else
#include <CGAL/Nef_3/Binary_operation.h>
#endif
#include <CGAL/Nef_S2/SM_decorator.h>
#include <CGAL/Nef_S2/SM_const_decorator.h>
#include <CGAL/Nef_3/SNC_SM_overlayer.h>
#include <CGAL/Nef_S2/SM_point_locator.h>
#include <CGAL/Nef_3/SNC_SM_explorer.h>
#include <CGAL/Nef_polyhedron_S2.h>
#include <CGAL/Modifier_base.h>
#include <CGAL/Nef_3/Mark_bounded_volumes.h>

#include <CGAL/IO/Verbose_ostream.h>
#include <CGAL/Nef_3/polygon_mesh_to_nef_3.h>
#include <CGAL/Nef_3/shell_to_nef_3.h>
#include <CGAL/Polyhedron_incremental_builder_3.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/boost/graph/graph_traits_Polyhedron_3.h>
#include <CGAL/Nef_3/SNC_point_locator.h>
#include <CGAL/assertions.h>

#include <CGAL/Constrained_triangulation_2.h>
#include <CGAL/Triangulation_data_structure_2.h>
#include <CGAL/Projection_traits_xy_3.h>
#include <CGAL/Projection_traits_yz_3.h>
#include <CGAL/Projection_traits_xz_3.h>
#include <CGAL/Constrained_triangulation_face_base_2.h>
#include <list>
#include <type_traits>

// RO: includes for "vertex cycle to Nef" constructor
#include <CGAL/Nef_3/vertex_cycle_to_nef_3.h>
#include <CGAL/Vector_3.h>
#include <CGAL/normal_vector_newell_3.h>

#ifdef CGAL_NEF_VISUAL_HULL
#include <CGAL/Nef_3/Modifying_binary_operation_vs.h>
#endif

#undef CGAL_NEF_DEBUG
#define CGAL_NEF_DEBUG 11
#include <CGAL/Nef_2/debug.h>

namespace CGAL {

template <typename K, typename I, typename M> class Nef_polyhedron_3;
template <typename K, typename I, typename M> class Nef_polyhedron_3_rep;

template <typename K, typename I, typename M>
std::ostream& operator<<(std::ostream& os, Nef_polyhedron_3<K,I,M>& NP);

template <typename K, typename I, typename M>
std::istream& operator>>(std::istream& os, Nef_polyhedron_3<K,I,M>& NP);


template <typename K, typename I, typename M>
class Nef_polyhedron_3_rep
{
  typedef Nef_polyhedron_3_rep<K,I,M>                  Self;
  friend class Nef_polyhedron_3<K,I,M>;
 public:
  typedef CGAL::SNC_structure<K,I,M>                      SNC_structure;
  typedef CGAL::SNC_decorator<SNC_structure>              SNC_decorator;
  typedef CGAL::SNC_const_decorator<SNC_structure>        SNC_const_decorator;
  typedef CGAL::Binary_operation<SNC_structure>           Binary_operation;
  typedef CGAL::SNC_constructor<I, SNC_structure>         SNC_constructor;
  typedef CGAL::SNC_external_structure<I, SNC_structure>  SNC_external_structure;
  typedef CGAL::SNC_point_locator<SNC_decorator> SNC_point_locator;
  typedef CGAL::SNC_simplify<I, SNC_structure>            SNC_simplify;
  typedef CGAL::SNC_point_locator_by_spatial_subdivision<SNC_decorator> SNC_point_locator_default;

  typedef typename SNC_structure::Sphere_map       Sphere_map;
  typedef CGAL::SM_decorator<Sphere_map>           SM_decorator;
  typedef CGAL::SM_const_decorator<Sphere_map>     SM_const_decorator;
  typedef CGAL::SNC_SM_overlayer<I, SM_decorator>  SM_overlayer;
  typedef CGAL::SM_point_locator<SNC_structure>    SM_point_locator;

 private:
  SNC_structure snc_;
  SNC_point_locator* pl_;

 public:
  Nef_polyhedron_3_rep() : snc_(), pl_(nullptr) {}
  ~Nef_polyhedron_3_rep() {
    CGAL_NEF_TRACEN( "Nef_polyhedron_3_rep: destroying SNC structure "<<&snc_<<
            ", point locator "<<pl_);
    snc_.clear();
    delete pl_;
  }
};

/*{\Manpage {Nef_polyhedron_3} {T} {Nef Polyhedra in Space}{N}}*/

/*{\Mdefinition
An instance of data type |\Mname| is a subset of 3-space which is the
result of forming complements and intersections starting from a set |H| of
halfspaces. |\Mtype| is closed under all binary set operations |intersection|,
|union|, |difference|, |complement| and under the topological operations
|boundary|, |closure|, and |interior|.}*/

template <typename Kernel_, typename Items_ = typename CGAL::Default_items<Kernel_>::Items, typename Mark_ = bool>
class Nef_polyhedron_3 : public CGAL::Handle_for< Nef_polyhedron_3_rep<Kernel_, Items_, Mark_> >,
                         public SNC_const_decorator<SNC_structure<Kernel_,Items_,Mark_> >
{
 public:
  /*{\Mtypes 7}*/

  typedef Kernel_                                     Kernel;
  typedef Kernel_                                     Traits;
  typedef Items_                                      Items;
  typedef Mark_                                       Mark;
  typedef Nef_polyhedron_3<Kernel, Items, Mark>       Self;
  typedef Nef_polyhedron_3<Kernel, Items, Mark>       Nef_polyhedron;
  typedef Handle_for< Nef_polyhedron_3_rep<Kernel, Items, Mark> >   Base;
  typedef typename Kernel::Point_3                    Point_3;
  typedef typename Kernel::Plane_3                    Plane_3;
  typedef typename Kernel::Vector_3                   Vector_3;
  typedef typename Kernel::Segment_3                  Segment_3;
  typedef typename Kernel::Aff_transformation_3       Aff_transformation_3;

#if (! defined _MSC_VER) || (_MSC_VER >= 1900)
  // VC++ < 2017 has a problem to digest the following typedef,
  // and does not need the using statements -- AF
  // The left and right part of these typedefs have the same name. It is
  // very important to qualify the left part with the CGAL:: namespace, no
  // to confuse g++. -- Laurent Rineau, 2010/09/13
  typedef CGAL::SNC_structure<Kernel,Items,Mark> SNC_structure;
  typedef CGAL::SNC_const_decorator<SNC_structure> SNC_const_decorator;
  using SNC_const_decorator::set_snc;
  using SNC_const_decorator::is_standard;
  using SNC_const_decorator::is_bounded;
#endif

  struct Polylines_tag {};
  struct Points_tag {};

  enum Boundary { EXCLUDED=0, INCLUDED=1 };
  /*{\Menum construction selection.}*/

  typedef enum { EMPTY=0, COMPLETE=1 } Content;
  /*{\Menum construction selection}*/

  typedef enum { DEFAULT, NAIVE, WALKING, SPATIAL_SUBDIVISION  } Location_mode;
  /*{\Menum selection flag for the point location mode.}*/

protected:
  struct AND {
    Mark operator()(const Mark& b1, const Mark& b2, bool /* inverted */ =false)const
    { return b1&&b2; }
  };

  struct OR {
    Mark operator()(const Mark& b1, const Mark& b2, bool /* inverted */ =false) const
    { return b1||b2; }
  };

  struct DIFF {
    Mark operator()(const Mark& b1, const Mark& b2, bool inverted=false) const
    { return (inverted) ? !b1&&b2  :  b1&&!b2 ; }
  };

  struct XOR {
    Mark operator()(const Mark& b1, const Mark& b2, bool /* inverted */ =false) const
    { return (b1&&!b2)||(!b1&&b2); }
  };

 public:
  typedef Nef_polyhedron_3_rep<Kernel,Items, Mark>    Nef_rep;

  typedef typename Nef_rep::SM_decorator        SM_decorator;
  typedef typename Nef_rep::SM_const_decorator  SM_const_decorator;
 protected:
  typedef typename Nef_rep::SNC_decorator       SNC_decorator;
  typedef typename Nef_rep::SNC_constructor     SNC_constructor;
  typedef typename Nef_rep::SNC_external_structure SNC_external_structure;
  typedef typename Nef_rep::Binary_operation    Binary_operation;
  typedef typename Nef_rep::SNC_point_locator   SNC_point_locator;
  typedef typename Nef_rep::SNC_point_locator_default
    SNC_point_locator_default;
  typedef CGAL::Combine_with_halfspace<SNC_structure, SNC_point_locator>
          Combine_with_halfspace;
public:
 enum Intersection_mode {
         CLOSED_HALFSPACE = Combine_with_halfspace::CLOSED_HALFSPACE,
     OPEN_HALFSPACE = Combine_with_halfspace::OPEN_HALFSPACE,
     PLANE_ONLY = Combine_with_halfspace::PLANE_ONLY};

protected:
  typedef typename Nef_rep::SM_overlayer        SM_overlayer;
  typedef typename Nef_rep::SM_point_locator    SM_point_locator;
  typedef typename Nef_rep::SNC_simplify        SNC_simplify;

 typedef typename Nef_rep::Sphere_map                Sphere_map;
 public:
 typedef CGAL::Nef_polyhedron_S2<Kernel,Items,Mark,Sphere_map> Nef_polyhedron_S2;
 protected:

  SNC_structure& snc() { return this->ptr()->snc_; }
  const SNC_structure& snc() const { return this->ptr()->snc_; }

  SNC_point_locator*& pl() { return this->ptr()->pl_; }
  const SNC_point_locator* pl() const { return this->ptr()->pl_; }

  friend std::ostream& operator<< <>
      (std::ostream& os, Nef_polyhedron_3<Kernel,Items, Mark>& NP);
  friend std::istream& operator>> <>
      (std::istream& is, Nef_polyhedron_3<Kernel,Items, Mark>& NP);

  typedef typename SNC_decorator::Vertex_handle    Vertex_handle;
  typedef typename SNC_decorator::Halfedge_handle  Halfedge_handle;
  typedef typename SNC_decorator::Halffacet_handle
                                                   Halffacet_handle;
  typedef typename SNC_decorator::Volume_handle    Volume_handle;

 public:
  typedef typename SNC_structure::Sphere_point                 Sphere_point;
  typedef typename SNC_structure::Sphere_segment               Sphere_segment;
  typedef typename SNC_structure::Sphere_circle                Sphere_circle;
  typedef typename SNC_structure::Vertex_base                  Vertex;
  typedef typename SNC_structure::Halfedge_base                Halfedge;
  typedef typename SNC_structure::Halffacet_base               Halffacet;
  typedef typename SNC_structure::Volume_base                  Volume;
  typedef typename SNC_structure::Vertex_const_handle          Vertex_const_handle;
  typedef typename SNC_structure::Halfedge_const_handle        Halfedge_const_handle;
  typedef typename SNC_structure::Halffacet_const_handle       Halffacet_const_handle;
  typedef typename SNC_structure::Volume_const_handle          Volume_const_handle;
  typedef typename SNC_structure::SHalfedge_around_svertex_circulator
                                  SHalfedge_around_svertex_circulator;
  typedef typename SNC_structure::SHalfedge_around_svertex_const_circulator
                                  SHalfedge_around_svertex_const_circulator;
  typedef typename SNC_structure::SHalfedge_around_facet_circulator
                                  SHalfedge_around_facet_circulator;
  typedef typename SNC_structure::SHalfedge_around_facet_const_circulator
                                  SHalfedge_around_facet_const_circulator;
  typedef typename SNC_structure::SHalfedge_around_sface_const_circulator
                                  SHalfedge_around_sface_const_circulator;
  typedef typename SNC_structure::Halffacet_cycle_const_iterator
                                  Halffacet_cycle_const_iterator;
  typedef typename SNC_structure::Halffacet_cycle_iterator
                                  Halffacet_cycle_iterator;
  typedef typename SNC_structure::Infi_box                     Infi_box;
  typedef typename SNC_structure::Size_type Size_type;
  typedef Size_type                         size_type;

  typedef typename Kernel::RT                       RT;

 public:
  typedef typename SM_decorator::SVertex_handle    SVertex_handle;
  typedef typename SM_decorator::SHalfedge_handle  SHalfedge_handle;
  typedef typename SM_decorator::SFace_handle      SFace_handle;
  typedef typename SM_decorator::SVertex_const_handle
                                                   SVertex_const_handle;
  typedef typename SM_decorator::SHalfedge_const_handle
                                                   SHalfedge_const_handle;
  typedef typename SM_decorator::SHalfloop_const_handle
                                                   SHalfloop_const_handle;
  typedef typename SM_decorator::SFace_const_handle
                                                   SFace_const_handle;
  typedef typename SNC_decorator::Vertex_iterator  Vertex_iterator;
  typedef typename SNC_decorator::Halfedge_iterator
                                                   Halfedge_iterator;
  typedef typename SNC_decorator::Halffacet_iterator
                                                   Halffacet_iterator;
  typedef typename SNC_structure::Shell_entry_iterator
                                                   Shell_entry_iterator;
  typedef typename SNC_decorator::Volume_iterator  Volume_iterator;
  typedef typename SNC_structure::Vertex_const_iterator
                                                    Vertex_const_iterator;
  typedef typename SNC_structure::Halfedge_const_iterator
                                                   Halfedge_const_iterator;
  typedef typename SNC_structure::Halffacet_const_iterator
                                                   Halffacet_const_iterator;
  typedef typename SNC_structure::Volume_const_iterator
                                                   Volume_const_iterator;
  typedef typename SNC_structure::Shell_entry_const_iterator
                                                   Shell_entry_const_iterator;
  typedef typename SM_decorator::SVertex_iterator  SVertex_iterator;
  typedef typename SM_decorator::SHalfedge_iterator
                                                   SHalfedge_iterator;
  typedef typename SM_decorator::SHalfloop_iterator
                                                   SHalfloop_iterator;
  typedef typename SM_decorator::SFace_iterator    SFace_iterator;
  typedef typename SM_decorator::SVertex_const_iterator
                                                   SVertex_const_iterator;
  typedef typename SM_decorator::SHalfedge_const_iterator
                                                   SHalfedge_const_iterator;
  typedef typename SM_decorator::SHalfloop_const_iterator
                                                   SHalfloop_const_iterator;
  typedef typename SM_decorator::SFace_const_iterator
                                                   SFace_const_iterator;
  typedef typename SNC_decorator::SFace_cycle_const_iterator
                                                   SFace_cycle_const_iterator;

  typedef typename SNC_decorator::Association  Association;


 protected:
  void initialize_infibox_vertices(Content space) {
    SNC_constructor C(snc());
    Infi_box::initialize_infibox_vertices(C, space == COMPLETE);
  }

  void check_h_for_intersection_of_12_cube_edges_and_add_vertices
  (const Plane_3& p);
  void create_intersection_vertex_of_h_and_e();
  void init_cube_vertices_depending_on_h(const Plane_3& p);
  void add_h_to_local_view_of_v();

 public:
  void build_external_structure() {
    SNC_external_structure es(snc(), pl());
    es.build_external_structure();
  }

 private:
  void mark_bounded_volumes() {
    CGAL::Mark_bounded_volumes<Nef_polyhedron_3> mbv(true);
    delegate(mbv, /*compute_external*/ false, /*simplify*/ false);
  }

  void reserve_for_vertices(Size_type n) {
    snc().reserve_sm_boundary_items(n);
  }

  struct Private_tag {};
  Nef_polyhedron_3(Private_tag) {
    pl() = new SNC_point_locator_default;
    set_snc(snc());
  }

 public:
  /*{\Mcreation 3}*/

  Nef_polyhedron_3( Content space = EMPTY);

  /*{\Mcreate creates an instance |\Mvar| of type |\Mname|
  and initializes it to the empty set if |space == EMPTY|
  and to the whole space if |space == COMPLETE|.}*/

  explicit Nef_polyhedron_3(const Plane_3& p, Boundary b = INCLUDED);
  /*{\Mcreate creates a Nef polyhedron |\Mvar| containing the
  halfspace on the negative side of |p| including |p| if |b==INCLUDED|,
  excluding |p| if |b==EXCLUDED|.}*/

  Nef_polyhedron_3(const Nef_polyhedron_3<Kernel,Items, Mark>& N1)
 : Base(N1) , SNC_const_decorator() {
    set_snc(snc());
  }

  Nef_polyhedron_3& operator=(const Nef_polyhedron_3<Kernel,Items, Mark>& N1) {
    Base::operator=(N1); // copy the handle
    set_snc(snc());
    return (*this);
  }

  Nef_polyhedron_3& operator=(Nef_polyhedron_3<Kernel,Items, Mark>&& N1) noexcept {
    N1.set_snc(snc()); // N1.set_snc sets N1.sncp_ not N1.snc_
    Base::operator=(std::move(N1)); // swap the handles
    set_snc(snc()); // snc() will return N1.snc_
    return (*this);
  }

  ~Nef_polyhedron_3() {
    CGAL_NEF_TRACEN("~Nef_polyhedron_3: destructor called for snc "<<&snc()<<
           ", pl "<<pl());
  }

   // RO: "vertex cycle to Nef" constructor (main part)
   // II input iterator; KN kernel of normal (may differ from Nef kernel)
   template <class II, class KN>
   Nef_polyhedron_3 (II v_first, II v_last,
                    const CGAL::Vector_3<KN> &normal, bool verb = false)
   {  CGAL_NEF_TRACEN("construction from vertex cycle (main part)");

      // project and triangulate vertices,
      // convert result to Nef_polyhedron
      CGAL_precondition (!CGAL::is_empty_range (v_first, v_last));
      bool is_nef = vertex_cycle_to_nef_3<Nef_polyhedron> (snc(),
         v_first, v_last, normal, verb);
      if (is_nef)
      {
         // TO DO:
         // Wie kann der eigene point_locator pl() eingebunden werden?
         // Wie kann der Konstruktor umgangen werden?
         typedef CGAL::SNC_point_locator_by_spatial_subdivision
            <CGAL::SNC_decorator<SNC_structure> >    Point_locator;

         Point_locator Pl;
         SNC_external_structure es(snc(), &Pl);
         es.build_external_structure();
         *this = Nef_polyhedron(snc(), &Pl);
      }
      else
      {  *this = Nef_polyhedron();
      }
      set_snc (snc());
      CGAL_expensive_postcondition (is_valid());
   }

   // RO: "vertex cycle to Nef" constructor (normal computation)
   template <class II>
   Nef_polyhedron_3 (II v_first, II v_last, bool verb = false)
   {  CGAL_NEF_TRACEN("construction from vertex cycle (normal computation)");

      // compute normal vector
      CGAL_precondition (!CGAL::is_empty_range (v_first, v_last));
      CGAL::Vector_3<typename II::value_type::R> normal;
      normal_vector_newell_3 (v_first, v_last, normal);

      // call "main" constructor
      *this = Nef_polyhedron_3 (v_first, v_last, normal, verb);
      set_snc (snc());
   }

 template<typename Items, typename SNC_structure>
 class Sphere_map_creator {
   typedef typename SNC_structure::SM_decorator     SM_decorator;
   typedef typename SNC_structure::Vertex_handle    Vertex_handle;
   typedef typename SNC_structure::SVertex_handle   SVertex_handle;
   typedef typename SNC_structure::SFace_handle     SFace_handle;
   typedef typename SNC_structure::Sphere_point     Sphere_point;

   public:
   Sphere_map_creator() {}

   template<typename point_iterator>
   void create_end_sphere_map(SNC_structure& snc,
                              point_iterator cur,
                              point_iterator prev) {
     Vertex_handle v(snc.new_vertex(*cur, true));
     SM_decorator SM(&*v);
     SVertex_handle sv(v->new_svertex(Sphere_point(ORIGIN+(*prev-*cur)),
                                      true));
     SFace_handle sf(v->new_sface());
     SM.link_as_isolated_vertex(sv,sf);
   }

   template<typename point_iterator>
   void create_sphere_map(SNC_structure& snc,
                          point_iterator cur,
                          point_iterator prev,
                          point_iterator next) {
     Vertex_handle v(snc.new_vertex(*cur, true));
     SM_decorator SM(&*v);
     SVertex_handle sv1(v->new_svertex(Sphere_point(ORIGIN+(*prev-*cur)),
                                       true));
     SVertex_handle sv2(v->new_svertex(Sphere_point(ORIGIN+(*next-*cur)),
                                       true));
     SFace_handle sf(v->new_sface());
     SM.link_as_isolated_vertex(sv1,sf);
     SM.link_as_isolated_vertex(sv2,sf);
   }
 };

 template<typename SNC_structure>
 class Sphere_map_creator<CGAL::SNC_indexed_items, SNC_structure> {
   typedef typename SNC_structure::SM_decorator     SM_decorator;
   typedef typename SNC_structure::Vertex_handle    Vertex_handle;
   typedef typename SNC_structure::SVertex_handle   SVertex_handle;
   typedef typename SNC_structure::SFace_handle     SFace_handle;
   typedef typename SNC_structure::Sphere_point     Sphere_point;

   bool first;
   int index;
 public:
   Sphere_map_creator() : first(true), index(0) {}

     template<typename point_iterator>
       void create_end_sphere_map(SNC_structure& snc,
                                  point_iterator cur,
                                  point_iterator prev) {
       Vertex_handle v(snc.new_vertex(*cur, true));
       SM_decorator SM(&*v);
       SVertex_handle sv(v->new_svertex(Sphere_point(ORIGIN+(*prev-*cur)),
                                        true));
       SFace_handle sf(v->new_sface());
       SM.link_as_isolated_vertex(sv,sf);
       if(first) {
         index = sv->new_index();
         first = false;
       } else
         sv->set_index(index);
     }

     template<typename point_iterator>
       void create_sphere_map(SNC_structure& snc,
                              point_iterator cur,
                              point_iterator prev,
                              point_iterator next) {
       Vertex_handle v(snc.new_vertex(*cur, true));
       SM_decorator SM(&*v);
       SVertex_handle sv1(v->new_svertex(Sphere_point(ORIGIN+(*prev-*cur)),
                                         true));
       SVertex_handle sv2(v->new_svertex(Sphere_point(ORIGIN+(*next-*cur)),
                                         true));
       SFace_handle sf(v->new_sface());
       SM.link_as_isolated_vertex(sv1,sf);
       SM.link_as_isolated_vertex(sv2,sf);
       sv1->set_index(index);
       index = sv2->new_index();
     }
 };

 template <typename InputIterator>
 Nef_polyhedron_3(InputIterator begin, InputIterator end, Polylines_tag)
  : Nef_polyhedron_3(Private_tag{})
 {
   typedef typename std::iterator_traits<InputIterator>::value_type
     point_iterator_pair;
   typedef typename point_iterator_pair::first_type
     point_iterator;

   initialize_infibox_vertices(EMPTY);

   point_iterator pbegin, pend, pnext, pprev;
   Sphere_map_creator<Items, SNC_structure> smc;
   for(;begin != end; ++begin) {
     pend = begin->second;
     pprev = pnext = pbegin = begin->first;
     ++pnext;
     CGAL_assertion(pnext != pend);
     smc.create_end_sphere_map(snc(),pbegin,pnext);
     for(++pbegin,++pnext; pnext!=pend; ++pbegin,++pprev,++pnext)
       smc.create_sphere_map(snc(),pbegin,pprev,pnext);
     smc.create_end_sphere_map(snc(),pbegin,pprev);
   }
   build_external_structure();
   simplify();
 }

 explicit
 Nef_polyhedron_3(const Segment_3& s)
  : Nef_polyhedron_3(Private_tag{})
 {
   initialize_infibox_vertices(EMPTY);

   Sphere_map_creator<Items, SNC_structure> smc;
   std::vector<Point_3> endpoints(2);
   endpoints[0]=s.source();
   endpoints[1]=s.target();
   smc.create_end_sphere_map(snc(),&endpoints[0],&endpoints[1]);
   smc.create_end_sphere_map(snc(),&endpoints[1],&endpoints[0]);

   build_external_structure();
   simplify();
 }

 template <typename InputIterator>
 Nef_polyhedron_3(InputIterator begin, InputIterator end, Points_tag)
   : Nef_polyhedron_3(Private_tag{})
 {
   initialize_infibox_vertices(EMPTY);

   for(InputIterator it=begin; it!=end;++it)
   {
     Vertex_handle v(snc().new_vertex(*it, true));
     SM_decorator SM(&*v);
     v->new_sface();
   }
   build_external_structure();
   simplify();
 }

 explicit
 Nef_polyhedron_3(const Point_3& p)
   : Nef_polyhedron_3(Private_tag{})
 {
   initialize_infibox_vertices(EMPTY);

   Vertex_handle v(snc().new_vertex(p, true));
   SM_decorator SM(&*v);
   v->new_sface();

   build_external_structure();
   simplify();
 }

 template <class PolygonMesh>
 explicit Nef_polyhedron_3(const PolygonMesh& pm)
   : Nef_polyhedron_3(Private_tag{})
 {
    CGAL_NEF_TRACEN("construction from PolygonMesh with internal index maps");
    reserve_for_vertices(num_vertices(pm));
    initialize_infibox_vertices(EMPTY);
    polygon_mesh_to_nef_3<PolygonMesh, SNC_structure>(pm, snc());
    build_external_structure();
    mark_bounded_volumes();
    simplify();
  }

 template <class PolygonMesh, class HalfedgeIndexMap, class FaceIndexMap>
 explicit Nef_polyhedron_3(const PolygonMesh& pm,
                           const HalfedgeIndexMap& him,
                           const FaceIndexMap& fim,
                           typename std::enable_if <
                              !std::is_same<FaceIndexMap, bool>::value
                           >* = nullptr // disambiguate with another constructor
  ) : Nef_polyhedron_3(Private_tag{})
  {
    CGAL_NEF_TRACEN("construction from PolygonMesh");
    reserve_for_vertices(num_vertices(pm));
    initialize_infibox_vertices(EMPTY);
    polygon_mesh_to_nef_3<PolygonMesh, SNC_structure>(pm, snc(), fim, him);
    build_external_structure();
    mark_bounded_volumes();
    simplify();
  }

 Nef_polyhedron_3(const Nef_polyhedron& N,
                  SFace_const_iterator sf)
   : Nef_polyhedron_3(Private_tag{})
 {
   initialize_infibox_vertices(EMPTY);
   shell_to_nef_3(N, sf, snc());
   build_external_structure();
   mark_bounded_volumes();
   simplify();
 }


 protected:

  template<typename Kernel>
  class Triangulation_handler2 {

    typedef typename CGAL::Triangulation_vertex_base_2<Kernel>               Vb;
    typedef typename CGAL::Constrained_triangulation_face_base_2<Kernel>     Fb;
    typedef typename CGAL::Triangulation_data_structure_2<Vb,Fb>             TDS;
    typedef typename CGAL::Constrained_triangulation_2<Kernel,TDS>           CT;

    typedef typename CT::Face_handle           Face_handle;
    typedef typename CT::Vertex_handle         CTVertex_handle;
    typedef typename CT::Finite_faces_iterator Finite_face_iterator;
    typedef typename CT::Edge                  Edge;

    CT ct;
    CGAL::Unique_hash_map<Face_handle, bool> visited;
    CGAL::Unique_hash_map<CTVertex_handle, Vertex_const_handle> ctv2v;
    Finite_face_iterator fi;
    Plane_3 supporting_plane;

  public:
    Triangulation_handler2(Halffacet_const_handle f) :
      visited(false), supporting_plane(f->plane()) {

      Halffacet_cycle_const_iterator fci;
      for(fci=f->facet_cycles_begin(); fci!=f->facet_cycles_end(); ++fci) {
        if(fci.is_shalfedge()) {
          SHalfedge_around_facet_const_circulator sfc(fci), send(sfc);
          CGAL_For_all(sfc,send) {
            CGAL_NEF_TRACEN("  insert point" << sfc->source()->source()->point());
            CTVertex_handle ctv = ct.insert(sfc->source()->source()->point());
            ctv2v[ctv] = sfc->source()->source();
          }
        }
      }

      for(fci=f->facet_cycles_begin(); fci!=f->facet_cycles_end(); ++fci) {
        if(fci.is_shalfedge()) {
          SHalfedge_around_facet_const_circulator sfc(fci), send(sfc);
          CGAL_For_all(sfc,send) {
            CGAL_NEF_TRACEN("  insert constraint" << sfc->source()->source()->point()
                             << "->" << sfc->source()->twin()->source()->point());
            ct.insert_constraint(sfc->source()->source()->point(),
                                 sfc->source()->twin()->source()->point());
          }
        }
      }
      CGAL_assertion(ct.is_valid());

      CGAL_NEF_TRACEN("number of finite triangles " << ct.number_of_faces());

      typename CT::Face_handle infinite = ct.infinite_face();
      typename CT::Vertex_handle ctv = infinite->vertex(1);
      if(ct.is_infinite(ctv)) ctv = infinite->vertex(2);
      CGAL_assertion(!ct.is_infinite(ctv));

      typename CT::Face_handle opposite;
      typename CT::Face_circulator vc(ctv,infinite);
      do { opposite = vc++;
      } while(!ct.is_constrained(typename CT::Edge(vc,vc->index(opposite))));
      typename CT::Face_handle first = vc;

      CGAL_assertion(!ct.is_infinite(first));
      traverse_triangulation(first, first->index(opposite));

      fi = ct.finite_faces_begin();
    }

    void traverse_triangulation(Face_handle f, int parent) {
      visited[f] = true;
      if(!ct.is_constrained(Edge(f,ct.cw(parent))) && !visited[f->neighbor(ct.cw(parent))]) {
        Face_handle child(f->neighbor(ct.cw(parent)));
        traverse_triangulation(child, child->index(f));
      }
      if(!ct.is_constrained(Edge(f,ct.ccw(parent))) && !visited[f->neighbor(ct.ccw(parent))]) {
        Face_handle child(f->neighbor(ct.ccw(parent)));
        traverse_triangulation(child, child->index(f));
      }
    }

    template<typename Triangle_3>
    bool get_next_triangle(Triangle_3& tr) {
      while(fi != ct.finite_faces_end() && visited[fi] == false) ++fi;
      if(fi == ct.finite_faces_end()) return false;
      tr = Triangle_3(fi->vertex(0)->point(), fi->vertex(1)->point(), fi->vertex(2)->point());
      ++fi;
      return true;
    }

    bool same_orientation(Plane_3 p1) const {
      if(p1.a() != 0)
        return CGAL::sign(p1.a()) == CGAL::sign(supporting_plane.a());
      if(p1.b() != 0)
        return CGAL::sign(p1.b()) == CGAL::sign(supporting_plane.b());
      return CGAL::sign(p1.c()) == CGAL::sign(supporting_plane.c());
    }

    template<typename PIB, typename Index>
    void handle_triangles(PIB& pib, Index& VI) {
      while(fi != ct.finite_faces_end() && visited[fi] == false) ++fi;
      while(fi != ct.finite_faces_end()) {
        Plane_3 plane(fi->vertex(0)->point(),
                      fi->vertex(1)->point(),
                      fi->vertex(2)->point());
        pib.begin_facet();
        if(same_orientation(plane)) {
          pib.add_vertex_to_facet(VI[ctv2v[fi->vertex(0)]]);
          pib.add_vertex_to_facet(VI[ctv2v[fi->vertex(1)]]);
          pib.add_vertex_to_facet(VI[ctv2v[fi->vertex(2)]]);
        } else {
          pib.add_vertex_to_facet(VI[ctv2v[fi->vertex(0)]]);
          pib.add_vertex_to_facet(VI[ctv2v[fi->vertex(2)]]);
          pib.add_vertex_to_facet(VI[ctv2v[fi->vertex(1)]]);
        }
        pib.end_facet();
        do {
          ++fi;
        } while(fi != ct.finite_faces_end() && visited[fi] == false);
      }
    }
  };

  template <class HDS>
  class Build_polyhedron : public CGAL::Modifier_base<HDS> {

    class Visitor {

      typedef typename CGAL::Projection_traits_xy_3<Kernel>       XY;
      typedef typename CGAL::Projection_traits_yz_3<Kernel>       YZ;
      typedef typename CGAL::Projection_traits_xz_3<Kernel>       XZ;

      const Object_index<Vertex_const_iterator>& VI;
      Polyhedron_incremental_builder_3<HDS>& B;
      const SNC_const_decorator& D;

    public:
      Visitor(Polyhedron_incremental_builder_3<HDS>& BB,
              const SNC_const_decorator& sd,
              Object_index<Vertex_const_iterator>& vi) : VI(vi), B(BB), D(sd){}

      void visit(Halffacet_const_handle opposite_facet) {

        CGAL_NEF_TRACEN("Build_polyhedron: visit facet " << opposite_facet->plane());

        CGAL_assertion(Infi_box::is_standard(opposite_facet->plane()));

        SHalfedge_const_handle se;
        Halffacet_cycle_const_iterator fc;

        Halffacet_const_handle f = opposite_facet->twin();

        SHalfedge_around_facet_const_circulator
          sfc1(f->facet_cycles_begin()), sfc2(sfc1);

        if(++f->facet_cycles_begin() != f->facet_cycles_end() ||
           ++(++(++sfc1)) != sfc2) {
          Vector_3 orth = f->plane().orthogonal_vector();
          int c = CGAL::abs(orth[0]) > CGAL::abs(orth[1]) ? 0 : 1;
          c = CGAL::abs(orth[2]) > CGAL::abs(orth[c]) ? 2 : c;

          if(c == 0) {
            Triangulation_handler2<YZ> th(f);
            th.handle_triangles(B, VI);
          } else if(c == 1) {
            Triangulation_handler2<XZ> th(f);
            th.handle_triangles(B, VI);
          } else if(c == 2) {
            Triangulation_handler2<XY> th(f);
            th.handle_triangles(B, VI);
          } else
            CGAL_error_msg( "wrong value");

        } else {

          B.begin_facet();
          fc = f->facet_cycles_begin();
          se = SHalfedge_const_handle(fc);
          CGAL_assertion(se!=0);
          SHalfedge_around_facet_const_circulator hc_start(se);
          SHalfedge_around_facet_const_circulator hc_end(hc_start);
          CGAL_For_all(hc_start,hc_end) {
            CGAL_NEF_TRACEN("   add vertex " << hc_start->source()->center_vertex()->point());
            B.add_vertex_to_facet(VI[hc_start->source()->center_vertex()]);
          }
          B.end_facet();
        }
      }

      void visit(SFace_const_handle) {}
      void visit(Halfedge_const_handle) {}
      void visit(Vertex_const_handle) {}
      void visit(SHalfedge_const_handle) {}
      void visit(SHalfloop_const_handle) {}
    };

  public:

    const SNC_const_decorator& scd;
    Object_index<Vertex_const_iterator> VI;

    Build_polyhedron(const SNC_const_decorator& s) :
      scd(s), VI(s.vertices_begin(),s.vertices_end(),'V') {}

    void operator()( HDS& hds) {

      Polyhedron_incremental_builder_3<HDS> B(hds, true);

      int skip_volumes;
      if(Infi_box::extended_kernel()) {
        B.begin_surface(scd.number_of_vertices()-8,
                        scd.number_of_facets()-6,
                        scd.number_of_edges()-12);
        skip_volumes = 2;
      }
      else {
        B.begin_surface(scd.number_of_vertices(),
                        2*scd.number_of_vertices()-4,
                        3*scd.number_of_vertices()-6);
        skip_volumes = 1;
      }

      int vertex_index = 0;
      Vertex_const_iterator v;
      CGAL_forall_vertices(v,scd) {
        if(Infi_box::is_standard(v->point())) {
          VI[v]=vertex_index++;
          B.add_vertex(v->point());
        }
      }

      Visitor V(B,scd,VI);
      Volume_const_handle c;
      CGAL_forall_volumes(c,scd)
        if(skip_volumes-- <= 0)
          scd.visit_shell_objects(SFace_const_handle(c->shells_begin()),V);
      B.end_surface();
      if ( B.error() ) B.rollback();
    }

  };

  template <class HDS>
  class Build_polyhedron2 : public CGAL::Modifier_base<HDS> {

    class Find_holes {

      Unique_hash_map<Vertex_const_handle, bool>& omit_vertex;
      int nov, nof;
      bool hh;

    public:
      Find_holes(Unique_hash_map<Vertex_const_handle, bool>& omit_vertex_)
        : omit_vertex(omit_vertex_), nov(0), nof(0), hh(false) {}

      void visit(Halffacet_const_handle f) {
        ++nof;
        Halffacet_cycle_const_iterator fc = f->facet_cycles_begin();
        for(++fc; fc != f->facet_cycles_end(); ++fc) {
          if(fc.is_shalfedge()) {
            --nof;
            SHalfedge_around_facet_const_circulator
              sfc(fc), send(sfc);
            CGAL_For_all(sfc, send) {
              omit_vertex[sfc->source()->source()] = true;
              --nov;
            hh=true;
            }
          } else if(fc.is_shalfloop()) {
            SHalfloop_const_handle sl(fc);
            omit_vertex[sl->incident_sface()->center_vertex()] = true;
            --nov;
          } else
            CGAL_error_msg( "wrong handle type");
        }
      }

      void visit(Vertex_const_handle) { ++nov; }
      void visit(SFace_const_handle) {}
      void visit(Halfedge_const_handle) {}
      void visit(SHalfedge_const_handle) {}
      void visit(SHalfloop_const_handle) {}

      int number_of_vertices() const {
        return nov;
      }

      int number_of_facets() const {
        return nof;
      }

      bool holes_detected() const {
        return hh;
      }
    };

    class Nested_holes {

      Unique_hash_map<Vertex_const_handle, bool>& omit_vertex;
      int norv, nof;

    public:
      Nested_holes(Unique_hash_map<Vertex_const_handle, bool>& omit_vertex_)
        : omit_vertex(omit_vertex_), norv(0), nof(0) {}

      void visit(Halffacet_const_handle f) {
        Halffacet_cycle_const_iterator fc = f->facet_cycles_begin();
        CGAL_assertion(fc.is_shalfedge());

        SHalfedge_around_facet_const_circulator sfc(fc), send(sfc);
        bool all_in=true;
        bool all_out=true;
        CGAL_For_all(sfc, send) {
          if (omit_vertex[sfc->source()->source()])
            all_in=false;
          else
            all_out=false;
        }
        if (!all_in && !all_out)
        {
          SHalfedge_around_facet_const_circulator sfc(fc), send(sfc);
          ++nof;
          CGAL_For_all(sfc, send) {
            if (!omit_vertex[sfc->source()->source()])
            {
              omit_vertex[sfc->source()->source()]=true;
              ++norv;
            }
          }
        }
        if (all_in)
          ++nof;
      }

      void visit(Vertex_const_handle) {}
      void visit(SFace_const_handle) {}
      void visit(Halfedge_const_handle) {}
      void visit(SHalfedge_const_handle) {}
      void visit(SHalfloop_const_handle) {}

      int number_of_removed_vertices() const {
        return norv;
      }

      int number_of_facets() const {
        return nof;
      }
    };

    class Add_vertices {

      Polyhedron_incremental_builder_3<HDS>& B;
      Unique_hash_map<Vertex_const_handle, bool>& omit_vertex;
      Object_index<Vertex_const_iterator>& VI;
      int vertex_index;

    public:
      Add_vertices(Polyhedron_incremental_builder_3<HDS>& B_,
                   Unique_hash_map<Vertex_const_handle, bool>& omit_vertex_,
                   Object_index<Vertex_const_iterator>& VI_)
        : B(B_), omit_vertex(omit_vertex_), VI(VI_), vertex_index(0) {}

      void visit(Vertex_const_handle v) {
        if(omit_vertex[v]) return;
        VI[v]=vertex_index++;
        B.add_vertex(v->point());
      }

      void visit(Halffacet_const_handle) {}
      void visit(SFace_const_handle) {}
      void visit(Halfedge_const_handle) {}
      void visit(SHalfedge_const_handle) {}
      void visit(SHalfloop_const_handle) {}

    };

    class Visitor {

      const Object_index<Vertex_const_iterator>& VI;
      Polyhedron_incremental_builder_3<HDS>& B;
      const Unique_hash_map<Vertex_const_handle, bool>& omit_vertex;
      SNC_const_decorator& D;

    public:
      Visitor(Polyhedron_incremental_builder_3<HDS>& BB,
              const Unique_hash_map<Vertex_const_handle, bool>& omit_vertex_,
              SNC_const_decorator& sd,
              Object_index<Vertex_const_iterator>& vi)
        : VI(vi), B(BB), omit_vertex(omit_vertex_), D(sd){}

      void visit(Halffacet_const_handle opposite_facet) {

        CGAL_NEF_TRACEN("Build_polyhedron: visit facet " << opposite_facet->plane());

        CGAL_assertion(Infi_box::is_standard(opposite_facet->plane()));

        SHalfedge_const_handle se;
        Halffacet_cycle_const_iterator fc;

        Halffacet_const_handle f = opposite_facet->twin();

        fc = f->facet_cycles_begin();
        se = SHalfedge_const_handle(fc);
        CGAL_assertion(se!=0);
        if(omit_vertex[se->source()->source()]) return;

        SHalfedge_around_facet_const_circulator hc_start(se);
        SHalfedge_around_facet_const_circulator hc_end(hc_start);
        std::vector<std::size_t> vids;
        CGAL_For_all(hc_start,hc_end) {
          if (omit_vertex[hc_start->source()->center_vertex()])
          {
            std::cout << "issue with " << se->source()->source()->point() << "\n";
            return;
          }
          vids.push_back(VI[hc_start->source()->center_vertex()]);
        }
        B.add_facet (vids.begin(), vids.end());
      }

      void visit(SFace_const_handle) {}
      void visit(Halfedge_const_handle) {}
      void visit(Vertex_const_handle) {}
      void visit(SHalfedge_const_handle) {}
      void visit(SHalfloop_const_handle) {}
    };

  public:

    SFace_const_handle sf;
    SNC_const_decorator& scd;
    Object_index<Vertex_const_iterator> VI;
    Unique_hash_map<Vertex_const_handle, bool> omit_vertex;

    Build_polyhedron2(SFace_const_handle sf_, SNC_const_decorator& s) :
      sf(sf_), scd(s), VI(s.vertices_begin(),s.vertices_end(),'V'),
      omit_vertex(false) {}

      void operator()(HDS& hds) {

      Polyhedron_incremental_builder_3<HDS> B(hds, true);

      // first mark vertices of holes of each halffacet as omitted.
      Find_holes F(omit_vertex);
      scd.visit_shell_objects(sf, F);
      std::size_t nb_v = F.number_of_vertices();
      std::size_t nb_f = F.number_of_facets();

      // then if a halffacet contains a vertex marked as omitted, all its vertices
      // must be marked as such
      if (F.holes_detected())
      {
        while(true)
        {
          Nested_holes F2(omit_vertex);
          scd.visit_shell_objects(sf, F2);
          if (F2.number_of_removed_vertices()==0) break;
          nb_v-=F2.number_of_removed_vertices();
          nb_f=F2.number_of_facets();
        }
      }


      B.begin_surface(nb_v,
                      nb_f,
                      F.number_of_vertices()+F.number_of_facets()-2);

      Add_vertices A(B,omit_vertex, VI);
      scd.visit_shell_objects(sf, A);

      Visitor V(B,omit_vertex, scd,VI);
      scd.visit_shell_objects(sf, V);
      B.end_surface();
      if ( B.error() ) B.rollback();
    }

  };


 public:
 void delegate( Modifier_base<SNC_structure>& modifier,
                bool compute_external = false,
                bool do_simplify = true) {

   // calls the `operator()' of the `modifier'. Precondition: The
   // `modifier' returns a consistent representation.
   if( this->is_shared()) clone_rep();
   modifier(snc());
   if(compute_external) {
     SNC_external_structure es(snc());
     es.clear_external_structure();

     build_external_structure();
   }
   if(do_simplify)
     simplify();
   CGAL_expensive_postcondition( is_valid());
 }

 struct SNC_and_PL {
   SNC_structure* sncp;
   SNC_point_locator* pl;

   SNC_and_PL(SNC_structure* s, SNC_point_locator* p) : sncp(s), pl(p) {}
 };

 void delegate( Modifier_base<SNC_and_PL>& modifier,
                bool compute_external = false,
                bool do_simplify = false) {
   // calls the `operator()' of the `modifier'. Precondition: The
   // `modifier' returns a consistent representation.
   if( this->is_shared()) clone_rep();
   SNC_and_PL sncpl(&snc(),pl());
   modifier(sncpl);
   pl() = sncpl.pl;
   if(compute_external) {
     SNC_external_structure es(snc());
     es.clear_external_structure();
     build_external_structure();
   }
   if(do_simplify)
     simplify();
   CGAL_expensive_postcondition( is_valid());
 }

 public:

 template<typename Polyhedron>
 void convert_to_Polyhedron(Polyhedron& P) const {
   convert_to_polyhedron(P);
 }

 template<typename Polyhedron>
 void convert_to_polyhedron(Polyhedron& P) const {
   typedef typename Polyhedron::HalfedgeDS HalfedgeDS;
   CGAL_precondition(is_simple());
   P.clear();
   Build_polyhedron<HalfedgeDS> bp(*this);
   P.delegate(bp);
 }

 template<typename Polyhedron>
 void convert_inner_shell_to_polyhedron(SFace_const_iterator sf, Polyhedron& P) {
   typedef typename Polyhedron::HalfedgeDS HalfedgeDS;
   P.clear();
   Build_polyhedron2<HalfedgeDS> bp(sf, *this);
   P.delegate(bp);
 }

  bool is_valid( bool verb = false, int level = 0) {
    // checks the combinatorial consistency.
    Verbose_ostream verr(verb);
    verr << "begin CGAL::Nef_polyhedron_3<...>::is_valid( verb=true, "
      "level = " << level << "):" << std::endl;

    SNC_decorator D(snc());
    bool valid = D.is_valid(verb, level);
    verr << "end of CGAL::Nef_polyhedron_3<...>::is_valid(): structure is "
         << ( valid ? "valid." : "NOT VALID.") << std::endl;
    return valid;
  }

  bool is_simple() const {

    Halfedge_const_iterator e;
    CGAL_forall_edges(e,snc())
      if(!is_edge_2manifold(e))
        return false;

    CGAL_NEF_TRACEN("there is no edge with non-manifold situation");

    Vertex_const_iterator v;
    CGAL_forall_vertices(v,snc())
      if(!is_vertex_2manifold(v))
        return false;

    CGAL_NEF_TRACEN("there is no vertex with non-manifold situation");
/*
    Halffacet_iterator f;
    CGAL_forall_halffacets(f,snc())
      if(!is_facet_simple(f))
        return false;

    CGAL_NEF_TRACEN("there are no holes");
*/
    return true;
  }

 bool is_convex() const {

   Vertex_const_iterator v;
   CGAL_forall_vertices(v, *this) {

     SM_const_decorator SD(&*v);
     if(std::distance(SD.sfaces_begin(),SD.sfaces_end())!=2)
       return false;

     if(!Infi_box::is_standard(v->point())) continue;

     SFace_const_iterator sf;
     CGAL_forall_sfaces(sf,SD) {
       if(sf->volume() == Infi_box::getNirvana(snc())) continue;
       if(std::distance(sf->sface_cycles_begin(),sf->sface_cycles_end())!=1)
         return false;
       SFace_cycle_const_iterator sfi(sf->sface_cycles_begin());
       if(!sfi.is_shalfedge())
         return false;
       SHalfedge_const_handle se(sf->sface_cycles_begin());
       SHalfedge_around_sface_const_circulator sec(se),send(sec);
       CGAL_For_all(sec,send)
         if(spherical_orientation(sec->source()->point(),
                                  sec->snext()->source()->point(),
                                  sec->snext()->snext()->source()->point())<0) {
           std::cerr << "vertex at " << v->point() << " is not convex" << std::endl;
           return false;
         }
     }
   }
   return true;
 }

 private:
  bool is_edge_2manifold(const Halfedge_const_handle& e) const {

    SM_decorator SD;
    SHalfedge_around_svertex_const_circulator c(SD.first_out_edge(e)), c2(c);

    if(c == 0) {
      CGAL_assertion(circulator_size(c) !=2);
      return false;
    }

    if(++c == c2){
      CGAL_assertion(circulator_size(c) !=2);
      return false;
    }

    if(++c != c2) {
      CGAL_assertion(circulator_size(c) !=2);
      return false;
    }

    CGAL_assertion(circulator_size(c) == 2);
    return true;
  }

  bool is_vertex_2manifold(const Vertex_const_handle& v) const {

    SFace_const_iterator sfi(v->sfaces_begin());
    if (++sfi != v->sfaces_last())
      return false;

    return true;
  }

  bool is_facet_simple(const Halffacet_const_handle& f) const {

    bool found_first = false;
    Halffacet_cycle_const_iterator it;
    CGAL_forall_facet_cycles_of(it,f)
      if (found_first || !it.is_shalfedge())
        return false;
      else
        found_first = true;

    return true;
  }

 public:

  void clear(Content space = EMPTY)
    { *this = Nef_polyhedron_3(space); }
  /*{\Mop makes |\Mvar| the empty set if |space == EMPTY| and the
  full space if |space == COMPLETE|.}*/

 bool is_empty() const {
   /*{\Mop returns true if |\Mvar| is empty, false otherwise.}*/
   if(Infi_box::extended_kernel())
     return this->number_of_vertices() == 8 &&
            this->number_of_edges() == 12 &&
            this->number_of_facets() == 6 &&
            this->number_of_volumes() == 2 &&
            (++this->volumes_begin())->mark() == false;

   else
     return this->number_of_vertices() == 0 &&
            this->number_of_edges() == 0 &&
            this->number_of_facets() == 0 &&
            this->number_of_volumes() == 1 &&
            (this->volumes_begin())->mark() == false;
  }

 bool is_space() const {
  /*{\Mop returns true if |\Mvar| is the whole space, false otherwise.}*/
   if(Infi_box::extended_kernel())
     return this->number_of_vertices() == 8 &&
            this->number_of_edges() == 12 &&
            this->number_of_facets() == 6 &&
            this->number_of_volumes() == 2 &&
            (++this->volumes_begin())->mark() == true;

   else
     return this->number_of_vertices() == 0 &&
            this->number_of_edges() == 0 &&
            this->number_of_facets() == 0 &&
            this->number_of_volumes() == 1 &&
            (this->volumes_begin())->mark() == true;
  }

  /*{\Xtext \headerline{Destructive Operations}}*/

 protected:
  void clone_rep() { *this = Nef_polyhedron_3<Kernel,Items, Mark>(snc(), pl()); }
  void empty_rep() {
    SNC_structure rsnc;
    delete pl();
    *this = Nef_polyhedron_3<Kernel,Items, Mark>(rsnc, new SNC_point_locator_default,false);
  }

 public:
  Nef_polyhedron_3( const SNC_structure& W,
                    SNC_point_locator* _pl = new SNC_point_locator_default,
                    bool clone_pl = true,
                    bool clone_snc = true);

  /*{\Xcreate makes |\Mvar| a new object.  If |cloneit==true| then the
  underlying structure of |W| is copied into |\Mvar|.}*/
  // TODO: granados: define behavior when clone=false

  /*{\Moperations 4 3 }*/

  void simplify() {
    SNC_simplify simp(snc());
    bool simplified = simp.simplify();
    CGAL_NEF_TRACEN( "simplify(): structure simplified? "<<simplified);

    if( simplified) {
#ifdef CGAL_NEF3_UPDATE_K3TREE_AFTER_SIMPLIFICATION
      /*debug*/ snc().print_statistics();
      Unique_hash_map<Vertex_handle, bool>
        V(false, snc().number_of_vertices());
      Unique_hash_map<Halfedge_handle, bool>
        E(false, snc().number_of_halfedges());
      Unique_hash_map<Halffacet_handle, bool>
        F(false, snc().number_of_halffacets());
      Vertex_iterator v;
      Halfedge_iterator e;
      Halffacet_iterator f;
      CGAL_forall_vertices( v, snc()) V[Vertex_handle(v)] = true;
      CGAL_forall_halfedges( e, snc()) E[Halfedge_handle(e)] = true;
      CGAL_forall_halffacets( f, snc()) F[Halffacet_handle(f)] = true;
      bool updated = pl()->update( V, E, F);
      CGAL_NEF_TRACEN("simplify(): point locator structure updated? " << updated);
#else
      SNC_point_locator* old_pl = pl();
      pl() = pl()->clone();
      pl()->initialize(&snc());
      delete old_pl;
#endif
    }
  }

 public:
  Nef_polyhedron_S2 get_sphere_map(Vertex_const_handle v) const {
    return Nef_polyhedron_S2(*v);
  }

  void extract_complement();
  /*{\Xop converts |\Mvar| to its complement. }*/
  void extract_interior();
  /*{\Xop converts |\Mvar| to its interior. }*/
  void extract_boundary();
  /*{\Xop converts |\Mvar| to its boundary. }*/

  void extract_closure()
  /*{\Xop converts |\Mvar| to its closure. }*/
  { CGAL_NEF_TRACEN("extract closure");
    if( this->is_shared()) clone_rep();
    extract_complement();
    extract_interior();
    extract_complement();
  }

  void extract_regularization()
  /*{\Xop converts |\Mvar| to its regularization. }*/
  { CGAL_NEF_TRACEN("extract regularization");
    if( this->is_shared()) clone_rep();
    extract_interior();
    extract_closure();
  }

  /*{\Mtext \headerline{Constructive Operations}}*/

  Nef_polyhedron_3<Kernel,Items, Mark> complement() const
  /*{\Mop returns the complement of |\Mvar| in the plane. }*/
  { Nef_polyhedron_3<Kernel,Items, Mark> res = *this;
    res.extract_complement();
    return res;
  }

  Nef_polyhedron_3<Kernel,Items, Mark> interior() const
  /*{\Mop    returns the interior of |\Mvar|. }*/
  { Nef_polyhedron_3<Kernel,Items, Mark> res = *this;
    res.extract_interior();
    return res;
  }

  Nef_polyhedron_3<Kernel,Items, Mark> closure() const
  /*{\Mop returns the closure of |\Mvar|. }*/
  { Nef_polyhedron_3<Kernel,Items, Mark> res = *this;
    res.extract_closure();
    return res;
  }

  Nef_polyhedron_3<Kernel,Items, Mark> boundary() const
  /*{\Mop returns the boundary of |\Mvar|. }*/
  { Nef_polyhedron_3<Kernel,Items, Mark> res = *this;
    res.extract_boundary();
    return res;
  }

  Nef_polyhedron_3<Kernel,Items, Mark> regularization() const
  /*{\Mop    returns the regularized polyhedron (closure of
    the interior).}*/
  { Nef_polyhedron_3<Kernel,Items, Mark> res = *this;
    res.extract_regularization();
    return res;
  }

  Nef_polyhedron_3<Kernel,Items, Mark>
  intersection(const Nef_polyhedron_3<Kernel,Items, Mark>& N1) const
    /*{\Mop returns |\Mvar| $\cap$ |N1|. }*/ {
    CGAL_NEF_TRACEN(" intersection between nef3 "<<&*this<<" and "<<&N1);
    if (is_empty()) return *this;
    if (N1.is_empty()) return N1;
    if (is_space()) return N1;
    if (N1.is_space()) return *this;
    AND _and;
    Nef_polyhedron_3<Kernel,Items, Mark> res(Private_tag{});
    Binary_operation bo( res.snc());
    bo(res.pl(), snc(), pl(), N1.snc(), N1.pl(), _and);
    return res;
  }

  Nef_polyhedron_3<Kernel,Items, Mark>
   intersection(const Plane_3& plane,
                Intersection_mode im) const {
    AND _and;
    Nef_polyhedron_3<Kernel,Items, Mark> res(Private_tag{});
    Combine_with_halfspace cwh(res.snc(), res.pl());
    cwh.combine_with_halfspace(snc(), plane, _and,
                                                           static_cast<typename Combine_with_halfspace::Intersection_mode>(im));
    return res;
  }

  Nef_polyhedron_3<Kernel,Items, Mark>
  join(const Nef_polyhedron_3<Kernel,Items, Mark>& N1) const
  /*{\Mop returns |\Mvar| $\cup$ |N1|. }*/ {
    CGAL_NEF_TRACEN(" join between nef3 "<<&*this<<" and "<<&N1);
    if (is_empty()) return N1;
    if (N1.is_empty()) return *this;
    if (is_space()) return *this;
    if (N1.is_space()) return N1;
    OR _or;
    //CGAL::binop_intersection_tests_allpairs<SNC_decorator, OR> tests_impl;
    Nef_polyhedron_3<Kernel,Items, Mark> res(Private_tag{});
    Binary_operation bo(res.snc());
    bo(res.pl(), snc(), pl(), N1.snc(), N1.pl(), _or);
    return res;
  }

  Nef_polyhedron_3<Kernel,Items, Mark>
  difference(const Nef_polyhedron_3<Kernel,Items, Mark>& N1) const
  /*{\Mop returns |\Mvar| $-$ |N1|. }*/ {
    CGAL_NEF_TRACEN(" difference between nef3 "<<&*this<<" and "<<&N1);
    if (is_empty()) return *this;
    if (N1.is_empty()) return *this;
    if (is_space()) return N1.complement();
    if (N1.is_space()) return Nef_polyhedron_3(EMPTY);
    DIFF _diff;
    //CGAL::binop_intersection_tests_allpairs<SNC_decorator, DIFF> tests_impl;
    Nef_polyhedron_3<Kernel,Items, Mark> res(Private_tag{});
    Binary_operation bo(res.snc());
    bo(res.pl(), snc(), pl(), N1.snc(), N1.pl(), _diff);
    return res;
  }

  Nef_polyhedron_3<Kernel,Items, Mark>
  symmetric_difference(const Nef_polyhedron_3<Kernel,Items, Mark>& N1) const
  /*{\Mop returns the symmectric difference |\Mvar - T| $\cup$
          |T - \Mvar|. }*/ {
    CGAL_NEF_TRACEN(" symmetric difference between nef3 "<<&*this<<" and "<<&N1);
    if (is_empty()) return N1;
    if (N1.is_empty()) return *this;
    if (is_space()) return Nef_polyhedron_3(EMPTY);
    if (N1.is_space()) return Nef_polyhedron_3(EMPTY);
    XOR _xor;
    //CGAL::binop_intersection_tests_allpairs<SNC_decorator, XOR> tests_impl;
    Nef_polyhedron_3<Kernel,Items, Mark> res(Private_tag{});
    Binary_operation bo(res.snc());
    bo(res.pl(), snc(), pl(), N1.snc(), N1.pl(), _xor);
    return res;
  }


  /*{\Mtext Additionally there are operators |*,+,-,^,!| which
  implement the binary operations \emph{intersection}, \emph{union},
  \emph{difference}, \emph{symmetric difference}, and the unary
  operation \emph{complement}. There are also the corresponding
  modification operations |*=,+=,-=,^=|.}*/

  Nef_polyhedron_3<Kernel,Items, Mark>  operator*(const Nef_polyhedron_3<Kernel,Items, Mark>& N1) const
  { return intersection(N1); }

  Nef_polyhedron_3<Kernel,Items, Mark>  operator+(const Nef_polyhedron_3<Kernel,Items, Mark>& N1) const
  { return join(N1); }

  Nef_polyhedron_3<Kernel,Items, Mark>  operator-(const Nef_polyhedron_3<Kernel,Items, Mark>& N1) const
  { return difference(N1); }

  Nef_polyhedron_3<Kernel,Items, Mark>  operator^(const Nef_polyhedron_3<Kernel,Items, Mark>& N1) const
  { return symmetric_difference(N1); }

  Nef_polyhedron_3<Kernel,Items, Mark>  operator!() const
  { return complement(); }

  Nef_polyhedron_3<Kernel,Items, Mark>& operator*=(const Nef_polyhedron_3<Kernel,Items, Mark>& N1)
  {
#ifdef CGAL_NEF_VISUAL_HULL
    CGAL_NEF_SETDTHREAD(19*43*71);
    std::cerr << "visual hull code " << std::endl;
    std::cerr << *this << std::endl;
    std::cerr << const_cast<Nef_polyhedron&>(N1) << std::endl;
    AND _and;
    typename CGAL::Modifying_binary_operation<SNC_structure>
      mbo(this->snc());
    mbo(const_cast<SNC_structure&>(N1.snc()), N1.pl(), pl(), _and);
    return *this;
#else
    *this = intersection(N1); return *this;
#endif
  }

  Nef_polyhedron_3<Kernel,Items, Mark>& operator+=(const Nef_polyhedron_3<Kernel,Items, Mark>& N1)
  { *this = join(N1); return *this; }

  Nef_polyhedron_3<Kernel,Items, Mark>& operator-=(const Nef_polyhedron_3<Kernel,Items, Mark>& N1)
  { *this = difference(N1); return *this; }

  Nef_polyhedron_3<Kernel,Items, Mark>& operator^=(const Nef_polyhedron_3<Kernel,Items, Mark>& N1)
  { *this = symmetric_difference(N1); return *this; }

  /*{\Mtext There are also comparison operations like |<,<=,>,>=,==,!=|
  which implement the relations subset, subset or equal, superset, superset
  or equal, equality, inequality.}*/

  bool operator==(const Nef_polyhedron_3<Kernel,Items, Mark>& N1) const
  { CGAL_NEF_TRACEN(" equality comparison between nef3 "<<&*this<<" and "<<&N1);
    return symmetric_difference(N1).is_empty(); }

  bool operator!=(const Nef_polyhedron_3<Kernel,Items, Mark>& N1) const
  { CGAL_NEF_TRACEN(" inequality comparison between nef3 "<<&*this<<" and "<<&N1);
    return !operator==(N1); }

  bool operator<(const Nef_polyhedron_3<Kernel,Items, Mark>& N1) const
  { return !N1.difference(*this).is_empty() && difference(N1).is_empty(); }

  bool operator>(const Nef_polyhedron_3<Kernel,Items, Mark>& N1) const
  { return N1.difference(*this).is_empty() && !difference(N1).is_empty(); }

  bool operator<=(const Nef_polyhedron_3<Kernel,Items, Mark>& N1) const
  { return difference(N1).is_empty(); }

  bool operator>=(const Nef_polyhedron_3<Kernel,Items, Mark>& N1) const
  { return N1.difference(*this).is_empty(); }


 bool is_90degree_rotation(const Aff_transformation_3& aff) const {
   if(aff.hm(0,3) != 0) return false;
   if(aff.hm(1,3) != 0) return false;
   if(aff.hm(2,3) != 0) return false;
   if(CGAL_NTS abs(aff.hm(0,0)) +
      CGAL_NTS abs(aff.hm(0,1)) +
      CGAL_NTS abs(aff.hm(0,2)) != aff.hm(3,3)) return false;
   if(CGAL_NTS abs(aff.hm(1,0)) +
      CGAL_NTS abs(aff.hm(1,1)) +
      CGAL_NTS abs(aff.hm(1,2)) != aff.hm(3,3)) return false;
   if(CGAL_NTS abs(aff.hm(2,0)) +
      CGAL_NTS abs(aff.hm(2,1)) +
      CGAL_NTS abs(aff.hm(2,2)) != aff.hm(3,3)) return false;
   if(CGAL_NTS abs(aff.hm(0,0)) +
      CGAL_NTS abs(aff.hm(1,0)) +
      CGAL_NTS abs(aff.hm(2,0)) != aff.hm(3,3)) return false;
   if(CGAL_NTS abs(aff.hm(0,1)) +
      CGAL_NTS abs(aff.hm(1,1)) +
      CGAL_NTS abs(aff.hm(2,1)) != aff.hm(3,3)) return false;
   if(CGAL_NTS abs(aff.hm(0,2)) +
      CGAL_NTS abs(aff.hm(1,2)) +
      CGAL_NTS abs(aff.hm(2,2)) != aff.hm(3,3)) return false;
   return true;
 }

 bool is_scaling(const Aff_transformation_3& aff) const {
   if(aff.hm(0,3) != 0) return false;
   if(aff.hm(1,3) != 0) return false;
   if(aff.hm(2,3) != 0) return false;
   if(aff.hm(0,1) != 0) return false;
   if(aff.hm(0,2) != 0) return false;
   if(aff.hm(1,0) != 0) return false;
   if(aff.hm(1,2) != 0) return false;
   if(aff.hm(2,0) != 0) return false;
   if(aff.hm(2,1) != 0) return false;
   if(aff.hm(0,0) != aff.hm(1,1)) return false;
   if(aff.hm(0,0) != aff.hm(2,2)) return false;
   return true;
 }

 void change_orientation(bool full = false) {

   Halffacet_handle ftemp;
   Volume_handle vtemp;
   SVertex_handle svtemp;
   SHalfedge_handle setemp;
   SFace_handle sftemp;

   SVertex_iterator sv;
   CGAL_forall_svertices(sv, snc()) {
     if (!sv->is_isolated())
      sv->out_sedge() = sv->out_sedge()->twin();
   }

   SHalfedge_iterator se;
   CGAL_forall_shalfedges(se, snc()) {
     if(se->is_twin()) {
       svtemp = se->source();
       se->source() = se->twin()->source();
       se->twin()->source() = svtemp;

       if(full) {
         ftemp = se->facet();
         se->facet() = se->twin()->facet();
         se->twin()->facet() = ftemp;
       }
       //       sftemp = se->incident_sface();
       //       se->incident_sface() = se->twin()->incident_sface();
       //       se->twin()->incident_sface() = sftemp;
     }

     setemp = se->sprev();
     se->sprev() = se->snext();
     se->snext() = setemp;

     se->circle() = se->circle().opposite();

     if(full) {
       setemp = se->prev();
       se->prev() = se->next();
       se->next() = setemp;
     }
   }

   if(full) {
     Halffacet_iterator f;
     CGAL_forall_facets(f, snc()) {
       vtemp  = f->incident_volume();
       f->incident_volume() = f->twin()->incident_volume();
       f->twin()->incident_volume() = vtemp;
       Halffacet_cycle_iterator fc(f->facet_cycles_begin()),
         fct(f->twin()->facet_cycles_begin());
       while(fc!=f->facet_cycles_end()) {
         CGAL_assertion(fct!=f->twin()->facet_cycles_end());
         if(fc.is_shalfedge()) {
           CGAL_assertion(fct.is_shalfedge());
           setemp = fc;
           *fc = *fct;
           *fct = make_object(setemp);
         }
         ++fc;
         ++fct;
       }
     }

     CGAL_forall_halffacets(f, snc()) {
       Halffacet_cycle_iterator fc(f->facet_cycles_begin());
       for(;fc!=f->facet_cycles_end();++fc) {
         if(fc.is_shalfedge()) {
           setemp = fc;
           SHalfedge_around_facet_circulator hfc(setemp),hend(hfc);
           ++hfc;
           CGAL_For_all(hfc,hend) {
             if ( CGAL::lexicographically_xyz_smaller(hfc->source()->source()->point(),
                                                      setemp->source()->source()->point()))
               setemp = hfc;
           }
           *fc = make_object(setemp);
         }
       }
     }
   }
 }

  void transform( const Aff_transformation_3& aff) {

    if( this->is_shared())
      clone_rep();
    // only linear transform for the origin-centered sphere maps
    Aff_transformation_3 linear( aff.hm(0,0), aff.hm(0,1), aff.hm(0,2),
                                 aff.hm(1,0), aff.hm(1,1), aff.hm(1,2),
                                 aff.hm(2,0), aff.hm(2,1), aff.hm(2,2),
                                 aff.hm(3,3));

    SNC_constructor cstr(snc());

    std::list<Vertex_handle> vertex_list;
    std::list<Vertex_handle> corner_list;
    std::list<Vertex_handle> delete_list;
    typename std::list<Vertex_handle>::iterator li;
    typename std::list<Vertex_handle>::iterator li2;

    bool ninety = is_90degree_rotation(aff);
    bool scale = is_scaling(aff);

    bool translate = aff.is_translation();

    Vertex_iterator vi;
    CGAL_forall_vertices( vi, snc()) {

      CGAL_NEF_TRACEN("transform vertex ");
      if(scale) {
        if(is_standard(vi))
          vi->point() = vi->point().transform( aff);
        else if(!Infi_box::is_infibox_corner(vi->point())) {
          vi->point() = normalized(Infi_box::normalize_transformed_vertex(vi->point().transform(aff)));
        }
      } else if (!is_standard(vi) && !ninety) {
        if(Infi_box::is_infibox_corner(vi->point()))
          corner_list.push_back(vi);
        vertex_list.push_back(vi);
      } else {
        vi->point() = vi->point().transform( aff);
        if(! translate){
          SM_decorator sdeco(&*vi);
          sdeco.transform( linear);
        }
      }
    }

    if(!this->is_bounded() && !ninety && !scale) {
      Halffacet_iterator fi;
      CGAL_forall_facets(fi, snc()) {
        if(!is_standard(fi) || is_bounded(fi)) continue;
        Plane_3 pt = fi->plane();
        pt = pt.transform(aff);
        std::list<Point_3> points(Infi_box::find_points_of_box_with_plane(cstr,pt));
        std::list<Vertex_handle> newVertices;
        newVertices = Infi_box::create_vertices_on_infibox(cstr,
                                                           pt, points, fi->mark(),
                                                           fi->twin()->incident_volume()->mark(),
                                                           fi->incident_volume()->mark());

        for(li = newVertices.begin(); li != newVertices.end(); ++li) {
          if(Infi_box::is_infibox_corner((*li)->point())) {
            li2 = corner_list.begin();
            while(li2 != corner_list.end() && (*li2)->point() != (*li)->point()) ++li2;
            CGAL_assertion(li2 != corner_list.end());
            delete_list.push_back(*li2);
            *li2 = *li;
          }
        }
      }

      for(li = vertex_list.begin(); li != vertex_list.end();++li) {
        SM_decorator SD(&**li);
        if(Infi_box::is_complex_facet_infibox_intersection(**li)) {
          Halffacet_handle hf[2];
          int i=0;
          SHalfedge_iterator sei;
          CGAL_forall_sedges(sei,SD) {
            if(!Infi_box::is_sedge_on_infibox(sei)) {
              hf[i] = sei->facet();
              if(hf[i]->is_twin()) hf[i] = hf[i]->twin();
              ++i;
            }
            if(i>1)
              break;
          }
        }
      }

      Association A;
      SNC_external_structure es(snc());
      es.clear_external_structure();
      for(li = vertex_list.begin(); li != vertex_list.end();++li){
        if(Infi_box::is_complex_facet_infibox_intersection(**li)) {
          Vertex_handle v2;
          Vertex_handle v1 = cstr.create_for_infibox_overlay(*li);
          v1->point() = normalized(Infi_box::normalize_transformed_vertex((*li)->point().transform(aff)));
          SM_decorator sdeco(&*v1);
          sdeco.transform(linear);
          switch(Infi_box::type_of_infibox_point(v1->point())) {
          case 1:
            v2 = cstr.create_from_point_on_infibox_facet(v1->point());
            break;
          case 2:
            v2 = cstr.create_from_point_on_infibox_edge(v1->point());
            break;
          case 3:
            v2 = cstr.create_from_point_on_infibox_vertex(v1->point());
            li2 = corner_list.begin();
            while(li2 != corner_list.end() && (*li2)->point() != v2->point()) ++li2;
            if(li2 != corner_list.end())
              delete_list.push_back(*li2);
            break;
          default: CGAL_error_msg( "wrong value");
          }
          Vertex_handle v = snc().new_vertex(v1->point(), (*li)->mark());
          SM_overlayer O(&*v);
          O.subdivide(&*v1, &*v2, A);
          AND _and;
          O.select(_and);
          O.simplify(A);
          snc().delete_vertex(v1);
          snc().delete_vertex(v2);
        }

        if(Infi_box::is_infibox_corner((*li)->point())) {
          SM_decorator SD(&**li);
          if(SD.number_of_svertices() < 4)
            continue;
          li2 = corner_list.begin();
          while(li2 != corner_list.end() && (*li2)->point() != (*li)->point()) ++li2;
          CGAL_assertion(li2 != corner_list.end());
          if(*li == *li2) {
            delete_list.push_back(*li2);
            *li2 = cstr.create_from_point_on_infibox_vertex((*li)->point());
          }
        } else
          snc().delete_vertex(*li);
      }

      for(li = delete_list.begin(); li != delete_list.end(); ++li)
        snc().delete_vertex(*li);

      if(!aff.is_even())
        change_orientation();

      while(cstr.erase_redundant_vertices()) ;
      cstr.correct_infibox_sedge_marks();

      build_external_structure();
      cstr.correct_infibox_sface_marks();

      // are the upcoming lines necessary?
      SNC_point_locator* old_pl = pl();
      pl() = pl()->clone();
      pl()->initialize(&snc());
      delete old_pl;

    } else {
      Halffacet_iterator fi;
      CGAL_forall_halffacets(fi,snc()) {
        if(is_standard(fi) || ninety) {
          fi->plane() = fi->plane().transform( aff);
        }
      }

      if(!aff.is_even())
        change_orientation(true);

      if(aff.homogeneous(0,1) != 0 ||
         aff.homogeneous(0,2) != 0 ||
         aff.homogeneous(1,0) != 0 ||
         aff.homogeneous(1,2) != 0 ||
         aff.homogeneous(2,0) != 0 ||
         aff.homogeneous(2,1) != 0 ||
         aff.homogeneous(0,0) != aff.homogeneous(1,1) ||
         aff.homogeneous(0,0) != aff.homogeneous(2,2) ||
         !this->is_bounded()) {
           SNC_point_locator* old_pl = pl();
           pl() = pl()->clone();
           pl()->initialize(&snc());
           delete old_pl;
         }
      else pl()->transform(aff);
    }

    SNC_constructor C(snc());
    C.assign_indices();
  }

  /*{\Mtext \headerline{Exploration}
  As Nef polyhedra are the result of forming complements
  and intersections starting from a set |H| of halfspaces which are
  defined by oriented planes in three space. The corresponding
  structure is represented by an extended wuerzburg structure
  $W = (V,E,F,C)$. For topological queries within |W| the following
  types and operations allow exploration access to this structure.}*/

  /*{\Mtypes 3}*/

    typedef CGAL::SNC_SM_explorer<SM_const_decorator>  SM_explorer;

    SM_explorer SMexplorer(Vertex_const_handle v) const {
      SM_const_decorator SMCD(&*v);
      return SM_explorer(SMCD);
    }

  typedef typename SNC_structure::Object_list Object_list;
  typedef typename SNC_structure::Object_handle Object_handle;
  /*{\Mtypemember a generic handle to an object of the underlying
  plane map. The kind of object |(vertex, halfedge, face)| can
  be determined and the object can be assigned to a corresponding
  handle by the three functions:\\
  |bool assign(Vertex_const_handle& h, Object_handle)|\\
  |bool assign(Edge_const_handle& h, Object_handle)|\\
  |bool assign(Facet_const_handle& h, Object_handle)|\\
  |bool assign(Volume_const_handle& h, Object_handle)|\\
  where each function returns |true| iff the assignment to
  |h| was done.}*/

  /*{\Moperations 3 1 }*/

  bool contains(Object_handle /*h*/) const
  /*{\Mop  returns true iff the object |h| is contained in the set
  represented by |\Mvar|.}*/
    // { SNC_point_locator PL(snc()); return PL.mark(h);}
    { CGAL_error_msg( "not implemented."); return false;}

  bool contained_in_boundary(Object_handle h) const
  /*{\Mop  returns true iff the object |h| is contained in the $2$-skeleton
  of |\Mvar|.}*/
  { Vertex_const_handle v;
    Halfedge_const_handle e;
    Halffacet_const_handle f;
    return  ( assign(v,h) || assign(e,h) || assign(f,h) );
  }

  Object_handle locate(const Point_3& p) const
  /*{\Mop  returns a generic handle |h| to an object (vertex, edge, facet,
  volume) of the underlying SNC which contains the point |p| in its relative
  interior. The point |p| is contained in the set represented by |\Mvar| if
  |\Mvar.contains(h)| is true.}*/ {
    CGAL_NEF_TRACEN( "locating point...");
    CGAL_assertion( pl() != nullptr);

    Object_handle o = pl()->locate(p);

    Vertex_handle v;
    Halfedge_handle e;
    Halffacet_handle f;
    Volume_handle c;
    if(assign(v,o)) return make_object(Vertex_const_handle(v));
    if(assign(e,o)) return make_object(Halfedge_const_handle(e));
    if(assign(f,o)) return make_object(Halffacet_const_handle(f));
    if(assign(c,o)) return make_object(Volume_const_handle(c));

    return Object_handle();
  }

  /*{\Mimplementation Nef polyhedra are implemented on top of an
  extended Wuerzburg structure data structure (EWS) and use linear
  space in the number of vertices, edges and facets.  Operations like
  empty take constant time. The operations clear, complement, interior,
  closure, boundary, regularization, input and output take linear
  time. All binary set operations and comparison operations take time
  $O(N^2)$ where $N$ is the size of the output plus the size of the
  input.

  The point location operations run in linear query time without any
  preprocessing.}*/

  /*{\Mexample Nef polyhedra are parameterized by a so called extended
  geometric kernel. There's currently only one such kernel based on a
  homogeneous representation of extended points called
  |Extended_homogeneous<NT>|.  The kernel is parameterized by a
  multiprecision integer type. The member types of |Nef_polyhedron_3<
  Extended_homogeneous_3<NT> >| map to corresponding types of the CGAL
  geometry kernel (e.g. |Nef_polyhedron::Plane_3| equals
  |CGAL::Homogeneous<leda_integer>::Plane_3| in the example below).
  \begin{Mverb}
  #include <CGAL/basic.h>
  #include <CGAL/leda_integer.h>
  #include <CGAL/Extended_homogeneous.h>
  #include <CGAL/Nef_polyhedron_3.h>

  using namespace CGAL;
  typedef  Extended_homogeneous<leda_integer>   Extended_kernel;
  typedef  Nef_polyhedron_3<Extended_kernel>    Nef_polyhedron;
  typedef  Nef_polyhedron::Plane_3              Plane_3;

  int main()
  {
    Nef_polyhedron N1(Plane_3(1,0,0,0));
    Nef_polyhedron N2(Plane_3(0,1,0,0), Nef_polyhedron::EXCLUDED);
    Nef_polyhedron N3 = N1 * N2; // line (*)
    return 0;
  }
  \end{Mverb}
  After line (*) |N3| is the intersection of |N1| and |N2|.}*/

  std::size_t bytes() {
    // bytes used for the Nef_polyhedron_3.
    return sizeof(Self) + (snc().bytes() - sizeof(SNC_structure));
  }

  std::size_t bytes_reduced() {
    // bytes used for the Nef_polyhedron_3.
    std::cout << sizeof(Self) + (snc().bytes_reduced2() - sizeof(SNC_structure)) << std::endl;
    return sizeof(Self) + (snc().bytes_reduced() - sizeof(SNC_structure));
  }

}; // end of Nef_polyhedron_3

template <typename Kernel, typename Items, typename Mark>
Nef_polyhedron_3<Kernel,Items, Mark>::
Nef_polyhedron_3( Content space)
  : Nef_polyhedron_3(Private_tag{})
{
  CGAL_NEF_TRACEN("construction from empty or space.");
  if(Infi_box::extended_kernel()) {
    initialize_infibox_vertices(space);
    build_external_structure();
  } else {
    build_external_structure();
    snc().volumes_begin()->mark() = (space == COMPLETE) ? 1 : 0;
  }
}

template <typename Kernel, typename Items, typename Mark>
Nef_polyhedron_3<Kernel,Items, Mark>::
Nef_polyhedron_3(const Plane_3& h, Boundary b)
  : Nef_polyhedron_3(Private_tag{})
{
  CGAL_NEF_TRACEN("construction from plane "<<h);
  SNC_constructor C(snc());
  Infi_box::create_vertices_of_box_with_plane(C,h,(b==INCLUDED));
  build_external_structure();
  /*
  if(Infi_box::extended_kernel()) {
    SNC_structure snc1, snc2;
    SNC_point_locator* pl1 = new SNC_point_locator_default;
    SNC_point_locator* pl2 = new SNC_point_locator_default;

    SNC_constructor c1(snc1);
    Infi_box::initialize_infibox_vertices(c1, true);
    SNC_external_structure es1(snc1, pl1);
    es1.build_external_structure();

    SNC_constructor c2(snc2);
    c2.create_vertices_for_halfspace(h, b);
    SNC_external_structure es2(snc2, pl2);
    es2.pair_up_halfedges();
    es2.link_shalfedges_to_facet_cycles();
    c2.create_facets_and_volumes_of_halfspace(h);
    pl2->initialize(&snc2);

    AND _and;
    Binary_operation bo(snc());
    bo(pl(), snc1, pl1, snc2, pl2, _and);

    delete pl1;
    delete pl2;
  } else
    CGAL_error_msg
      ("Constructor is only available with extended kernels");
  */
}

template <typename Kernel, typename Items, typename Mark>
Nef_polyhedron_3<Kernel,Items, Mark>::
Nef_polyhedron_3( const SNC_structure& W, SNC_point_locator* _pl,
                  bool clone_pl,
                  bool clone_snc) {
  CGAL_assertion( clone_snc == true || clone_pl == false);
  // TODO: granados: define behavior when clone=false
  //  CGAL_NEF_TRACEN("construction from an existing SNC structure (clone="<<clone<<")");

  this->copy_on_write();
  if(clone_snc) {
    snc() = W;
    set_snc(snc());
  }
  if(clone_pl) {
    pl() = _pl->clone();
    pl()->initialize(&snc());
  }
  else
    pl() = _pl;
}

template <typename Kernel, typename Items, typename Mark>
void
Nef_polyhedron_3<Kernel,Items, Mark>::
extract_complement() {
  CGAL_NEF_TRACEN("extract complement");
  if( this->is_shared()) clone_rep();
  SNC_decorator D(snc());
  Vertex_iterator v;
  CGAL_forall_vertices(v,D){
    v->mark() = !v->mark();
    SM_decorator SM(&*v);
    SM.extract_complement();
  }

  Halffacet_iterator f;
  CGAL_forall_halffacets(f,D) f->mark() = !f->mark();

  Volume_iterator c;
  CGAL_forall_volumes(c,D)
    //    if(!(Infi_box::extended_kernel && c==D.volumes_begin()))
      c->mark() = !c->mark();
}

template <typename Kernel, typename Items, typename Mark>
void
Nef_polyhedron_3<Kernel,Items, Mark>::
extract_interior() {
  CGAL_NEF_TRACEN("extract interior");
  if (this->is_shared()) clone_rep();
  SNC_decorator D(snc());
  Vertex_iterator v;
  CGAL_forall_vertices(v,D){
    v->mark() = false;
    SM_decorator SM(&*v);
    SM.extract_interior();
  }
  Halffacet_iterator f;
  CGAL_forall_halffacets(f,D) f->mark() = false;

  simplify();
}

template <typename Kernel, typename Items, typename Mark>
void
Nef_polyhedron_3<Kernel,Items, Mark>::
extract_boundary() {
  CGAL_NEF_TRACEN("extract boundary");
  if (this->is_shared()) clone_rep();
  SNC_decorator D(snc());
  Vertex_iterator v;
  CGAL_forall_vertices(v,D) {
    v->mark() = true;
    SM_decorator SM(&*v);
    SM.extract_boundary();
  }
  Halffacet_iterator f;
  CGAL_forall_halffacets(f,D) f->mark() = true;
  Volume_iterator c;
  CGAL_forall_volumes(c,D) c->mark() = false;
  simplify();
}

} //namespace CGAL

#include <CGAL/enable_warnings.h>

#endif //CGAL_NEF_POLYHEDRON_3_H
