// Copyright (c) 1997-2000  ETH Zurich (Switzerland).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL: https://github.com/CGAL/cgal/blob/v6.0.3/Polytope_distance_d/include/CGAL/Width_3.h $
// $Id: include/CGAL/Width_3.h cefe3007d59 $
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
//
// Author(s)     : Thomas Herrmann, Lutz Kettner

#ifndef CGAL_WIDTH_3_H
#define CGAL_WIDTH_3_H

#include <CGAL/license/Polytope_distance_d.h>


#include <CGAL/basic.h>
#include <cstdlib>
#include <iostream>
#include <CGAL/convex_hull_3.h>

#include <CGAL/Polyhedron_3.h>
#include <CGAL/HalfedgeDS_list.h>

#include <CGAL/assertions.h>
#include <CGAL/Width_polyhedron_3.h>
#include <CGAL/width_assertions.h>

namespace CGAL {

template<class Traits_>
class Width_3 {
  // +----------------------------------------------------------------------+
  // | Typedef Area                                                         |
  // +----------------------------------------------------------------------+
 private:
  typedef Traits_                   Traits;
  typedef typename Traits::Point_3  Point_3;
  typedef typename Traits::Vector_3 Vector_3;
  typedef typename Traits::Plane_3  Plane_3;
  typedef typename Traits::RT       RT;

  // +----------------------------------------------------------------------+
  // | Variable Declaration                                                 |
  // +----------------------------------------------------------------------+
 private:
  //the current best plane coefficients: e1:Ax+By+Cz+1=0
  //                                     e2:Ax+By+Cz+D=0
  RT A,B,C,D,K;

  // Width itself
  RT WNum,WDenom;

  // Planes and directions are derived from these variables

  // A list with all quadruples A/K, B/K, C/K, D/K
  std::vector< std::vector<RT> > allsolutions;

  // A list with all quadruples that give an optimal solution
  std::vector< std::vector<RT> > alloptimal;

  //The traits class object
  Traits tco;

  //The new origin to know how to translate back
  Point_3 neworigin;


  // +----------------------------------------------------------------------+
  // | Access to Private Variables                                          |
  // +----------------------------------------------------------------------+
 public:
  void get_width_coefficients(RT& a, RT& b, RT& c, RT& d, RT& k) {
    d=-A*neworigin.hx()-B*neworigin.hy()-C*neworigin.hz()+D*neworigin.hw();
    k=-A*neworigin.hx()-B*neworigin.hy()-C*neworigin.hz()+K*neworigin.hw();
    a=A*neworigin.hw();
    b=B*neworigin.hw();
    c=C*neworigin.hw();
#ifdef GCD_COMPUTATION
    simplify_solution(a,b,c,d,k);
#endif
  }

  void get_squared_width(RT& num, RT& denom) {
    num=WNum;
    denom=WDenom;
  }

  Vector_3 get_build_direction() {
    return tco.make_vector(A,B,C);
  }

  void get_width_planes(Plane_3& e1, Plane_3& e2) {
    RT a,b,c,d,k;
    get_width_coefficients(a,b,c,d,k);
    e1=tco.make_plane(a,b,c,d);
    e2=tco.make_plane(a,b,c,k);
  }

  void get_all_build_directions(std::vector<Vector_3>& alldir) {
    typename std::vector< std::vector<RT> >::iterator it=alloptimal.begin();
    RT a,b,c;
    while(it!=alloptimal.end()) {
      a=(*it)[0];
      b=(*it)[1];
      c=(*it)[2];
#ifdef GCD_COMPUTATION
      RT dummy1=0;
      RT dummy2=0;
      simplify_solution(a,b,c,dummy1,dummy2);
#endif
      Vector_3 dir=tco.make_vector(a,b,c);
      alldir.push_back(dir);
      ++it;
    }
  }

  int get_number_of_optimal_solutions() {
    return int(alloptimal.size());
  }

  int get_number_of_possible_solutions() {
    return int(allsolutions.size());
  }

  void get_all_possible_solutions(std::vector< std::vector<RT> >& allsol) {
    allsol.clear();
    typename std::vector< std::vector<RT> >::iterator it=allsolutions.begin();
    while(it!=allsolutions.end()) {
      RT d=-((*it)[0])*neworigin.hx()-((*it)[1])*neworigin.hy()
        -((*it)[2])*neworigin.hz()+((*it)[3])*neworigin.hw();
      RT k=-((*it)[0])*neworigin.hx()-((*it)[1])*neworigin.hy()
        -((*it)[2])*neworigin.hz()+((*it)[4])*neworigin.hw();
      RT a=((*it)[0])*neworigin.hw();
      RT b=((*it)[1])*neworigin.hw();
      RT c=((*it)[2])*neworigin.hw();
#ifdef GCD_COMPUTATION
      simplify_solution(a,b,c,d,k);
#endif
      std::vector<RT> sol;
      sol.push_back(a);
      sol.push_back(b);
      sol.push_back(c);
      sol.push_back(d);
      sol.push_back(k);
      allsol.push_back(sol);
      ++it;
    }
  }


  // +----------------------------------------------------------------------+
  // | The Con- and Destructors                                             |
  // +----------------------------------------------------------------------+
 public:
  Width_3(): A(0), B(0), C(0), D(2), K(1), WNum(0), WDenom(1) {}

  template<class InputIterator>
  Width_3( InputIterator begin, InputIterator beyond):
    A(0), B(0), C(0), D(2), K(1), WNum(0), WDenom(1) {
    INFOMSG(INFO,"Waiting for new HDS to build class Width_Polyhedron!"
            <<std::endl<<"Working with extern additional data structures.");
    typedef typename Traits::ChullTraits CHT;
    typedef Width_polyhedron_items_3                        Items;
    typedef Polyhedron_3< Traits, Items, HalfedgeDS_list>   LocalPolyhedron;
    LocalPolyhedron P;
    convex_hull_3( begin, beyond, P, CHT());
    width_3_convex(P);
  }

  template <class InputPolyhedron>
  Width_3(InputPolyhedron& Poly):
    A(0), B(0), C(0), D(2), K(1), WNum(0), WDenom(1) {
    // Compute convex hull with new width_polyhedron structure
    INFOMSG(INFO,"Working with extern additional data structures.");
    width_3_convex(Poly);
  }

  ~Width_3() {
    allsolutions.clear();
    alloptimal.clear();
  }

  // +----------------------------------------------------------------------+
  // | Begin of private function area                                       |
  // +----------------------------------------------------------------------+
 private:
  // Just to remember:
  // E1: -axh - byh - czh - kwh <= 0          axh + byh + czh + kwh >= 0
  // E2:  axh + byh + czh + dwh <= 0
  // VF-pair: 3xE1 + 1xE2
  // EE-pair: 2xE1 + 2xE2
  // plane equation in facets: Ax + By + Cz + 1 = 0
  //                           ax + by + cz + k = 0 (A=a/k,...)

  //-----------------------------
  //---Combinatorial functions---
  //-----------------------------

  // *** PREPARATION_CHECK ***
  //---------------------------
  //This function determines the next facet if the halfedge e is a
  //valable halfedge over which we can rotate. If so fnext is returned.
  //PRECONDITION: e is the LAST edge in the go_on or impassable list!
  template <class InputDA, class Halfedge_handle_, class Facet_handle_>
    bool preparation_check(InputDA& dao,
                           Halfedge_handle_& e,
                           Facet_handle_& fnext,
                           std::vector<Halfedge_handle_>& go_on,
                           std::vector<Halfedge_handle_>& imp)
    {
    //If the halfedge flag impassable is set then we can pop e from the stack
    //of the possibale go_on edges
    DEBUGMSG(PREPARATION_CHECK,"\nBegin PREPARATION_CHECK");
    DEBUGENDL(PREPARATION_CHECK,"Edge e: "<<e->opposite()->vertex()->point()
              <<" --> ",e->vertex()->point());
    CGAL_precondition(go_on.back()==e);
    DEBUGMSG(ASSERTION_OUTPUT,"e is last element on stack go_on. "
             <<"ASSERTION OK.");
    if ( dao.is_impassable(e) ) {
      DEBUGMSG(PREPARATION_CHECK," is impassable. Erase from go_on.");
      go_on.pop_back();
      DEBUGMSG(PREPARATION_CHECK,"End PREPARATION_CHECK");
      return false;
    } else {
      //If the opposite halfedge of e is already visited, then we insert
      //e in the impassable list an pop e from the stack of the go_on edges
      typename InputDA::Halfedge_handle h=e->opposite();
      if(dao.is_visited(h)) {
        DEBUGMSG(PREPARATION_CHECK," has a visited opposite edge. Set "
                 <<"impassable flag, push on impassable stack and erase"
                 <<" from go_on");
        imp.push_back(e);
        dao.set_impassable_flag(h,true);
        go_on.pop_back();
        DEBUGMSG(PREPARATION_CHECK,"End PREPARATION_CHECK");
        return false;
      } else {
        DEBUGMSG(PREPARATION_CHECK," is a valable candidate. Compute next "
                 <<"facet and erase from go_on. Set visited flag of all to "
                 <<"fnext incident edges.");
        //e is a valable candidate. Thus set fnext to the next facet we visit
        fnext=h->facet();
        //Delete e from go_on and insert the edges of fnext (except opposite
        //of e) in the go_on list
        go_on.pop_back();
        typename InputDA::Halfedge_handle h0=h;
        h=h->next();
        while ( h!=h0) {
          DEBUGENDL(PREPARATION_CHECK,"Adding edge to go_on stack: ",
                    h->opposite()->vertex()->point()<<"  -->  "
                    <<h->vertex()->point());
          go_on.push_back(h);
          dao.set_visited_flag(h,true);
          h=h->next();
        }
        DEBUGMSG(PREPARATION_CHECK,"End PREPARATION_CHECK");
        return true;
      }
    }
  }

  // *** NEIGHBORS_OF ***
  //----------------------
  //To compute the neighbors of a vertex. The vertex is implicitly given
  //as the vertex the halfedge points to.
  template <class Halfedge_handle_, class Vertex_handle_>
    void neighbors_of(const Halfedge_handle_& h,
                      std::vector<Vertex_handle_>& V) {
    DEBUGMSG(NEIGHBORS_OF,"\nBegin NEIGHBORS_OF");
    DEBUGENDL(NEIGHBORS_OF,"Determining the neighbors of: ",
              h->vertex()->point());
    Halfedge_handle_ e=h;
    Halfedge_handle_ e0=e->opposite();
    V.clear();
    V.push_back(e0->vertex());
    e=e->next();
    //Now go around the vertex and store the neighbor vertices
    while ( e!=e0 ) {
      V.push_back(e->vertex());
      e=e->opposite()->next();
    }
#if NEIGHBORS_OF
    typename std::vector<Vertex_handle_>::iterator vtxit=V.begin();
    while(vtxit!=V.end()) {
      DEBUGENDL(NEIGHBORS_OF,"Neighbor: ",(*vtxit)->point());
      ++vtxit;
    }
#endif
    DEBUGMSG(NEIGHBORS_OF,"End NEIGHBORS_OF");

  }

  //During the algorithm we have to build union and minus set
  //of two sets and check whether two sets are cutting each other or not

  // *** SETMINUS ***
  //------------------
  //Builds the set A\B where the set A is changed
  template <class Vertex_handle_>
    void setminus(std::vector<Vertex_handle_>& res,
                  const std::vector<Vertex_handle_>&
                  without) {
    DEBUGMSG(SETMINUS,"\nBegin SETMINUS");
    typename std::vector<Vertex_handle_>::iterator resit;
    typename std::vector<Vertex_handle_>::const_iterator
      withoutit=without.begin();
    //Scan through all elements of without and check if they are also in res.
    //If so delete the element from res.
    while(withoutit!=without.end()) {
      resit=std::find(res.begin(),res.end(),*withoutit);
      if ( resit!=res.end() ) {
        CGAL_assertion((*resit)->point()==(*withoutit)->point());
        DEBUGMSG(ASSERTION_OUTPUT,"Found an element to erase. ASSERTION OK.");
        DEBUGENDL(SETMINUS,"Erase point: ",(*resit)->point());
        res.erase(resit);
      }
      ++withoutit;
    }
    DEBUGMSG(SETMINUS,"End SETMINUS");
  }

  // *** SETUNION ***
  //------------------
  //Builds the union of two sets A and B. The result is stored in A
  //POSTCONDITION: Every element in A is stored once.
  template <class Vertex_handle_>
    void setunion(std::vector<Vertex_handle_>&res,
                  std::vector<Vertex_handle_>& uni) {
    DEBUGMSG(SETUNION,"\nBegin SETUNION");
    typename std::vector<Vertex_handle_>::iterator
      uniit=uni.begin();
    typename std::vector<Vertex_handle_>::iterator resit;
    //Scan the uni set and add every new element in res
    while(uniit!=uni.end()) {
      resit=std::find(res.begin(),res.end(),*uniit);
      if ( resit==res.end() ) {
        DEBUGENDL(SETUNION,"Insert new point: ",(*uniit)->point());
        res.push_back(*uniit);
      }
      ++uniit;
    }
    DEBUGMSG(SETUNION,"End SETUNION");
  }

  // *** SETCUT ***
  //----------------
  //Checks if two sets are cutting each other or not (the common elements are
  //not determined
  template <class Vertex_handle_>
    bool setcut(std::vector<Vertex_handle_>& AA,
                std::vector<Vertex_handle_>& BB) {
    DEBUGMSG(SETCUT,"\nBegin SETCUT");
    typename std::vector<Vertex_handle_>::iterator
      Ait=AA.begin();
    typename std::vector<Vertex_handle_>::iterator Bfindit;
    while(Ait!=AA.end()) {
      Bfindit=std::find(BB.begin(),BB.end(),*Ait);
      if (Bfindit!=BB.end()) {
        DEBUGMSG(SETCUT,"The sets are cutting each other. Return true.");
        DEBUGMSG(SETCUT,"End SETCUT");
        return true;
      }
      ++Ait;
    }
    DEBUGMSG(SETCUT,"No common element detected. Return false");
    DEBUGMSG(SETCUT,"End SETCUT");
    return false;
  }


  // ---Numerical functions---
  // *************************

  // *** COMPUTE_PLANE_EQUATION ***
  //--------------------------------
  //We don't take the standard plane equation computation from CGAL,
  //because in the context of the width the coefficients have to
  //satisfy a system of linear inequations.
  //PRECONDITION: (0,0,0) is strictly inside the convex hull
  //POSTCONDITION:(0,0,0) is on the positive side of the plane <==> the normal
  //              vector of the plane points to the side where (0,0,0) lies
  template<class InputDA, class Facet_handle_>
    void compute_plane_equation(InputDA,
                                const Facet_handle_& f) {
    DEBUGMSG(COMPUTE_PLANE_EQUATION,"\nBegin COMPUTE_PLANE_EQUATION");
    DEBUGENDL(COMPUTE_PLANE_EQUATION,"Compute plane equations of facet f: ("
              <<f->halfedge()->opposite()->vertex()->point()<<"), (",
              f->halfedge()->vertex()->point()<<"), ("
              <<f->halfedge()->next()->vertex()->point()<<")");
    typename InputDA::Halfedge_handle e = f->halfedge();
    typename InputDA::PolyPoint p,q,r;
    q = e -> opposite() -> vertex() -> point();
    p = e -> vertex() -> point();
    r = e -> next() -> vertex() -> point();
    CGAL_assertion(r!=p && r!=q && p!=q);
    DEBUGMSG(ASSERTION_OUTPUT,"There are 3 different points. ASSERTION OK.");
    RT a,b,c,k;
    solve_3x3(InputDA(),p,q,r,a,b,c,k);
    f->plane()=tco.make_plane(a,b,c,k);
    DEBUGENDL(COMPUTE_PLANE_EQUATION,"Plane Coefficients: ",f->plane());
    DEBUGMSG(COMPUTE_PLANE_EQUATION,"End COMPUTE_PLANE_EQUATION");
  }

  // *** SOLVE_3X3 ***
  //-------------------
  //To solve a special 3x3 system. The rows of the coefficient matrix
  //are the (homogeneous) x,y,z-coordinates of points and the right
  //hand side is the homogeneous part of the point times the provided
  //coefficient. The system is solved with Cramer's Rule. The sign of
  //the coefficients is chosen in a way that (0,0,0) lies on the positive
  //side of the plane.
  template<class InputDA, class PolyPoint_>
    void solve_3x3(InputDA,
                   const PolyPoint_& p,
                   const PolyPoint_& q,
                   const PolyPoint_& r,
                   RT& a, RT& b, RT& c, RT& k) {
    DEBUGMSG(SOLVE_3X3,"\nBegin SOLVE_3X3");
    RT px,py,pz,ph;
    tco.get_point_coordinates(p,px,py,pz,ph);
    RT qx,qy,qz,qh;
    tco.get_point_coordinates(q,qx,qy,qz,qh);
    RT rx,ry,rz,rh;
    tco.get_point_coordinates(r,rx,ry,rz,rh);
    CGAL_assertion(ph>0 && qh>0 && rh>0);
    DEBUGMSG(ASSERTION_OUTPUT,"All homogeneous parts >0. ASSERTION OK.");
    DEBUGMSG(SOLVE_3X3,"Matrix:");
    DEBUGENDL(SOLVE_3X3,"",px<<" "<<py<<" "<<pz<<" : "<<-ph);
    DEBUGENDL(SOLVE_3X3,"",qx<<" "<<qy<<" "<<qz<<" : "<<-qh);
    DEBUGENDL(SOLVE_3X3,"",rx<<" "<<ry<<" "<<rz<<" : "<<-rh);
    k=px*(qy*rz-ry*qz)-qx*(py*rz-ry*pz)+rx*(py*qz-qy*pz);
    RT sig(1);
    if (k<=0) {
      if(k<0) {
        sig=-1;
        k=-k;
      }
      else
        CGAL_assertion_msg(k!=0,"Couldn't solve plane equation system");
    }
    a=sig*(-ph*(qy*rz-ry*qz)+qh*(py*rz-ry*pz)-rh*(py*qz-qy*pz));
    b=sig*(px*(rh*qz-qh*rz)-qx*(rh*pz-ph*rz)+rx*(qh*pz-ph*qz));
    c=sig*(px*(ry*qh-qy*rh)-qx*(ry*ph-py*rh)+rx*(qy*ph-py*qh));
#ifdef GCD_COMPUTATION
    RT dummy=0;
    DEBUGENDL(SOLVE_3X3,"Solution of 3x3 (before GCD computation):\n",a
              <<std::endl
              <<b<<std::endl<<c<<std::endl<<k<<std::endl);
    simplify_solution(a,b,c,k,dummy);
#endif
    DEBUGENDL(SOLVE_3X3,"Solution of 3x3:\n",a<<std::endl
              <<b<<std::endl<<c<<std::endl<<k<<std::endl);
    DEBUGMSG(SOLVE_3X3,"End SOLVE_3X3");
  }

  // *** SOLVE_4X4 ***
  //-------------------
  //To enumerate EE-pairs we need to solve a 4x4 linear equation system
  //The rows of the coefficient matrix
  //are the (homogeneous) x,y,z-coordinates of points and the right
  //hand side is the homogeneous part of the point times the provided
  //coefficient. The system is solved with Cramer's Rule.
  template<class InputDA, class PolyPoint_>
    bool solve_4x4(InputDA,
                   const PolyPoint_& p,
                   const PolyPoint_& q,
                   const PolyPoint_& r,
                   const PolyPoint_& v,
                   RT& a, RT& b, RT& c, RT& d, RT& k) {
    DEBUGMSG(SOLVE_4X4,"\nBegin SOLVE_4X4");
    RT px,py,pz,ph;
    tco.get_point_coordinates(p,px,py,pz,ph);
    RT qx,qy,qz,qh;
    tco.get_point_coordinates(q,qx,qy,qz,qh);
    RT rx,ry,rz,rh;
    tco.get_point_coordinates(r,rx,ry,rz,rh);
    RT vx,vy,vz,vh;
    tco.get_point_coordinates(v,vx,vy,vz,vh);
    CGAL_assertion(ph>0 && qh>0 && vh>0 && rh>0);
    DEBUGMSG(ASSERTION_OUTPUT,"All homogeneous parts >0. ASSERTION OK.");
    DEBUGMSG(SOLVE_4X4,"Matrix: ");
    DEBUGENDL(SOLVE_4X4,"",px<<" "<<py<<" "<<pz<<" 0 : "<<-ph);
    DEBUGENDL(SOLVE_4X4,"",qx<<" "<<qy<<" "<<qz<<" 0 : "<<-qh);
    DEBUGENDL(SOLVE_4X4,"",rx<<" "<<ry<<" "<<rz<<" "<<rh<<" : 0");
    DEBUGENDL(SOLVE_4X4,"",vx<<" "<<vy<<" "<<vz<<" "<<vh<<" : 0");

    k=-rh*(px*(qy*vz-vy*qz)-qx*(py*vz-vy*pz)+vx*(py*qz-qy*pz))
      +vh*(px*(qy*rz-ry*qz)-qx*(py*rz-ry*pz)+rx*(py*qz-qy*pz));
    RT sig(1);
    if (k<=0) {
      if (k<0) {
        sig=-1;
        k=-k;
        DEBUGMSG(SOLVE_4X4,"Sign of k (and of all other coefficients) "
                 <<"changed.");
      } else {
        DEBUGMSG(SOLVE_4X4,"No proper solution.");
        return false;
      }
    }

    a=sig*(-ph*(qy*(rz*vh-vz*rh)-ry*qz*vh+vy*qz*rh)
      +qh*(py*(rz*vh-vz*rh)-ry*pz*vh+vy*pz*rh));
    b=sig*(ph*(qx*(rz*vh-vz*rh)-rx*qz*vh+vx*qz*rh)
      -qh*(px*(rz*vh-vz*rh)-rx*pz*vh+vx*pz*rh));
    c=sig*(-ph*(qx*(ry*vh-vy*rh)-rx*qy*vh+vx*qy*rh)
      +qh*(px*(ry*vh-vy*rh)-rx*py*vh+vx*py*rh));
    d=sig*(ph*(qx*(ry*vz-vy*rz)-rx*(qy*vz-vy*qz)+vx*(qy*rz-ry*qz))
      -qh*(px*(ry*vz-vy*rz)-rx*(py*vz-vy*pz)+vx*(py*rz-ry*pz)));
    if (d>k) {
      DEBUGMSG(SOLVE_4X4,"d>k: Interchange d and k");
      RT tmp=d;
      d=k;
      k=tmp;
      CGAL_assertion(a*px+b*py+c*pz+d*ph==0);
      CGAL_assertion(a*qx+b*qy+c*qz+d*qh==0);
      CGAL_assertion(a*rx+b*ry+c*rz+k*rh==0);
      CGAL_assertion(a*vx+b*vy+c*vz+k*vh==0);
      DEBUGMSG(ASSERTION_OUTPUT,"Interchanged k and d. All Assertions ok.");
    }

    if (a==0 && b==0 && c==0) {
      DEBUGENDL(SOLVE_4X4,"Solution of 4x4:\n ",a<<std::endl<<b<<std::endl<<c
                <<std::endl
                <<d<<std::endl<<k);
      CGAL_assertion(a!=0);
      CGAL_error();
    } else {
#ifdef GCD_COMPUTATION
      DEBUGENDL(SOLVE_4X4,"Unique Solution of 4x4 (before GCD computation):\n",
                a<<std::endl<<b<<std::endl<<c<<std::endl<<d<<std::endl<<k);
      simplify_solution(a,b,c,d,k);
#endif
      DEBUGENDL(SOLVE_4X4,"Unique Solution of 4x4:\n",
                a<<std::endl<<b<<std::endl<<c<<std::endl<<d<<std::endl<<k);
      DEBUGMSG(SOLVE_4X4,"End SOLVE_4X4");
    }
    return true;
  }

  // *** CHECK_FEASIBILITY ***
  //---------------------------
  //This function checks the feasibility of a provided quadruple A/K, B/K,
  //C/K and D/K. Because we do not want to check the feasibility for all
  //the points the list of points is also expected.
  template<class InputDA, class Vertex_handle_>
    bool check_feasibility(InputDA,
                           const RT& a, const RT& b, const RT& c,
                           const RT& d, const RT& k,
                           const std::vector<Vertex_handle_>& V) {
    DEBUGMSG(CHECK_FEASIBILITY,"\nBegin CHECK_FEASIBILITY");
    if (d==k) {
      DEBUGMSG(CHECK_FEASIBILITY,"The planes e1 and e2 are the same. "
               <<"Not a feasible solution.");
      DEBUGMSG(CHECK_FEASIBILITY,"End CHECK_FEASIBILITY");
      return false;
    }

    typename std::vector<Vertex_handle_>::const_iterator
      it=V.begin();
    RT tmp;
    while ( it!=V.end() ) {
      RT px,py,pz,ph;
      tco.get_point_coordinates((*it)->point(),px,py,pz,ph);
      tmp = a*px+b*py+c*pz;
      //Check if the restrictions according to p are satisfied
      if (tmp+k*ph < 0 || tmp + d*ph > 0) {
#if CHECK_FEASIBILITY
        DEBUGENDL(CHECK_FEASIBILITY,"Restriction to point ",
                  (*it)->point()<<" failed.");
        if (tmp+k*ph < 0){
          DEBUGENDL(CHECK_FEASIBILITY,"E1 not satisfied: ",tmp+k*ph);
        } else {
          DEBUGENDL(CHECK_FEASIBILITY,"E2 not satisfied: ",tmp+d*ph);
        }
#endif
        DEBUGMSG(CHECK_FEASIBILITY,"Feasibility Check failed.");
        DEBUGMSG(CHECK_FEASIBILITY,"End CHECK_FEASIBILITY");
        return false;
      }
      ++it;
    }

    //All restrictions are satisfied, thus the check returns true
    DEBUGMSG(CHECK_FEASIBILITY,"Feasibility Check was successful.");
    DEBUGMSG(CHECK_FEASIBILITY,"End CHECK_FEASIBILITY");
    return true;
  }

#if GCD_COMPUTATION
  // *** GCD ***
  //-------------
  //To compute the gcd of 2 integer numbers
  //PRECONDITION: abs(IntNum) must be defined!
  //              %-operator must be defined!
  template<class IntNum>
    IntNum gcd(const IntNum& a, const IntNum& b) {
    DEBUGMSG(GCD_OUTPUT,"\nBegin GCD");
    DEBUGENDL(GCD_OUTPUT,"Compute gcd of ",a<<" and "<<b);
    IntNum r,s,t;
    if (abs(a)<abs(b)) {
      r=abs(b);
      s=abs(a);
    } else {
      r=abs(a);
      s=abs(b);
    }
    if (s==0) {
      DEBUGMSG(GCD_OUTPUT,"End GCD");
      return r;
    }
    t=r%s;
    while(t!=0){
      r=s;
      s=t;
      DEBUGENDL(GCD_OUTPUT,"New r: ",r<<"  and  new s: "<<s);
      t=r%s;
    }
    DEBUGENDL(GCD_OUTPUT,"Return gcd: ",s);
    DEBUGMSG(GCD_OUTPUT,"End GCD");
    return s;
  }

  // *** SIMPLIFY_SOLUTION ***
  //---------------------------
  //To simplify the solutions
  template<class IntNum>
    void simplify_solution(IntNum& a, IntNum& b, IntNum& c, IntNum& d,
                           IntNum& k) {
    DEBUGMSG(SIMPLIFY_SOLUTION,"\nBegin SIMPLIFY_SOLUTION");
    IntNum r=gcd(a,b);
    IntNum s=gcd(c,d);
    IntNum t=gcd(r,s);
    IntNum g=gcd(t,k);
    CGAL_assertion(g*(a/g)==a);
    a=a/g;
    CGAL_assertion(g*(b/g)==b);
    b=b/g;
    CGAL_assertion(g*(c/g)==c);
    c=c/g;
    CGAL_assertion(g*(d/g)==d);
    d=d/g;
    CGAL_assertion(g*(k/g)==k);
    k=k/g;
    DEBUGENDL(SIMPLIFY_SOLUTION,"Simplified solutions: ",a<<" "<<b<<" "<<c
              <<" "<<d<<" "<<k);
    DEBUGMSG(SIMPLIFY_SOLUTION,"End SIMPLIFY_SOLUTION");
  }
#endif

  // ---Width functions---
  // *********************

  // *** INITIAL_VF_PAIR ***
  //-------------------------
  //After the first initialization phase we have to compute an initial
  //Vertex-Facet pair to start with the enumeration
  //PRECONDITION: Normal of initial plane points to the interior of the
  //              convex hull
  template<class InputDA, class Facet_handle_, class Polyhedron_,
           class Halfedge_handle_>
    void initial_VF_pair(InputDA& dao,
                         Facet_handle_& f,
                         Polyhedron_& P,
                         std::vector<Halfedge_handle_>& go_on)
    {
    DEBUGMSG(INITIAL_VF_PAIR,"\nBegin INITIAL_VF_PAIR");
    DEBUGENDL(INITIAL_VF_PAIR,"Compute initial VF-pair with facet f: ("
              <<f->halfedge()->opposite()->vertex()->point()<<"), (",
              f->halfedge()->vertex()->point()<<"), ("
              <<f->halfedge()->next()->vertex()->point()<<")");
    typedef typename InputDA::Vertex_handle Vertex_handle;
    //Compute the facet. ==> e2 is fixed
    tco.get_plane_coefficients(f->plane(),A,B,C,K);
    CGAL_assertion(K>0);
    DEBUGMSG(ASSERTION_OUTPUT,"K greater (strictly) than 0. ASSERTION OK.");

    //Start with an impossible configuration for the still unknown
    //coefficient D=K, ie plane E1 == plane E2
    D=K;
    DEBUGENDL(INITIAL_VF_PAIR,"Starting with values:\nA:",A<<std::endl
              <<"B: "<<B<<std::endl<<"C: "<<C<<std::endl<<"D: "
              <<D<<std::endl<<"K: "<<K);

    std::vector<Vertex_handle> apv;
#if !(defined(CGAL_KERNEL_NO_ASSERTIONS) || defined(CGAL_NO_ASSERTIONS) \
  || (!defined(CGAL_KERNEL_CHECK_EXPENSIVE) && !defined(CGAL_CHECK_EXPENSIVE)))
      typename InputDA::Vertex_iterator vtxitass = P.vertices_begin();
      while(vtxitass!=P.vertices_end()) {
        RT px,py,pz,ph;
        tco.get_point_coordinates((*vtxitass).point(),px,py,pz,ph);
        CGAL_expensive_assertion(ph>0);
        CGAL_expensive_assertion(A*px+B*py+C*pz+K*ph>=0);
        ++vtxitass;
      }
      DEBUGMSG(ASSERTION_OUTPUT,"All points satisfy restriction "
               <<"type E1. ASSERTION OK>");
#endif

    typename InputDA::Vertex_iterator vtxit=P.vertices_begin();
    RT maxdist=0;
    RT hompart=1;
    //Try every point to be an/the antipodal vertex of the facet f. Take the
    //one with the biggest distance from E1
    DEBUGENDL(INITIAL_VF_PAIR,"Plane E1:",f->plane());
    while (vtxit != P.vertices_end() ) {
      RT pix, piy, piz, pih;
      tco.get_point_coordinates((*vtxit).point(),pix,piy,piz,pih);
      DEBUGENDL(INITIAL_VF_PAIR,"Try Point: ",(*vtxit).point());

      //Compute the sign of the distance from pi to the current plane e2
      RT distpie1=A*pix + B*piy + C*piz;
      DEBUGENDL(INITIAL_VF_PAIR,"Distance from p to current plane e1: ",
                distpie1*hompart);
      //If pi is not between e1 and e2, compute a new plane e2 through pi
      //If pi is also ON the current plane e2, then insert pi in the list
      //of current antipodal vertices of the facet f
      if (hompart*distpie1 >= pih*maxdist) {
        DEBUGMSG(INITIAL_VF_PAIR,"Distance of this point is greater (or equal)"
                 <<" than all the distances before."
                 <<"Change plane antipodal vertices.");
        if (hompart*distpie1 > pih*maxdist) {
          DEBUGMSG(INITIAL_VF_PAIR,"Compute new plane e2!");
          apv.clear();
          hompart=pih;
          maxdist=distpie1;
        }
        apv.push_back(vtxit);
      }
      ++vtxit;
    }
    A=A*hompart;
    B=B*hompart;
    C=C*hompart;
    D=-maxdist;
    K=K*hompart;
#ifdef GCD_COMPUTATION
    simplify_solution(A,B,C,D,K);
#endif
    DEBUGENDL(INITIAL_VF_PAIR,"Initial Plane E1: ",A<<" "<<B<<" "<<C<<" "<<K);
    DEBUGENDL(INITIAL_VF_PAIR,"Initial Plane E2: ",A<<" "<<B<<" "<<C<<" "<<D);

#if !(defined(CGAL_KERNEL_NO_ASSERTIONS) || defined(CGAL_NO_ASSERTIONS) \
  || (!defined(CGAL_KERNEL_CHECK_EXPENSIVE) && !defined(CGAL_CHECK_EXPENSIVE)))
      CGAL_expensive_assertion(D!=K && D!=0);
      DEBUGMSG(ASSERTION_OUTPUT,"A real plane E2 has been computed. "
               <<"ASSERTION OK.");
      vtxit=P.vertices_begin();
      while (vtxit != P.vertices_end() ) {
        RT px, py, pz, ph;
        tco.get_point_coordinates((*vtxit).point(),px,py,pz,ph);
        CGAL_expensive_assertion(A*px+B*py+C*pz+K*ph>=0);
        CGAL_expensive_assertion(A*px+B*py+C*pz+D*ph<=0);
        DEBUGENDL(ASSERTION_OUTPUT,"Restriction values: E1:",
                  A*px+B*py+C*pz+K*ph<<"  and E2: "<<A*px+B*py+C*pz+D*ph);
        DEBUGENDL(ASSERTION_OUTPUT,"Restrictions E1 and E2 according to "
                  <<"point "<<(*vtxit).point()
                  <<" are both satisfied.","ASSERTION OK.");
        ++vtxit;
      }
      DEBUGMSG(ASSERTION_OUTPUT,"All restrictions satisfied. "
               <<"ASSERTION OK.");
#endif
    DEBUGENDL(INITIAL_VF_PAIR,"Initial plane E1:",A<<" "<<B<<" "<<C<<" "<<K);
    DEBUGENDL(INITIAL_VF_PAIR,"Initial plane E2:",A<<" "<<B<<" "<<C<<" "<<D);
    //set the list of antipodal vertices of f definitely
    dao.set_antipodal_vertices(f,apv);

    //All solutions
    std::vector <RT> sol;
    sol.push_back(A);
    sol.push_back(B);
    sol.push_back(C);
    sol.push_back(D);
    sol.push_back(K);
    allsolutions.push_back(sol);
    alloptimal.push_back(sol);

    //Compute the squared width with the determined coefficients
    WNum=(K-D)*(K-D);
    WDenom=A*A+B*B+C*C;
    DEBUGENDL(INITIAL_VF_PAIR,"Initial squared width: ",
              WNum<<"/"<<WDenom);

    //Set all halfedges of f to be possible edges for a rotation
    //The set of these edges is used in the third phase of the algorithm
    typename InputDA::Halfedge_handle e = f->halfedge();
    go_on.push_back(e);
    dao.set_visited_flag(e,true);
    typename InputDA::Halfedge_handle e0 = e;
    e = e->next();
    while ( e != e0 ) {
      go_on.push_back(e);
      dao.set_visited_flag(e,true);
      e=e->next();
    }
    DEBUGMSG(INITIAL_VF_PAIR,"End INITIAL_VF_PAIR");
  }

  // *** CHECK_ABOUT_VF_PAIRS ***
  //------------------------------
  //This function checks if a facet and a subset of a given set of vertices
  //build a vertex facet pair
  template<class InputDA, class Facet_handle_, class Vertex_handle_>
    bool
    check_about_VF_pairs(InputDA& dao,
                         Facet_handle_& f,
                         const std::vector<Vertex_handle_>& V)
    {
    DEBUGMSG(CHECK_ABOUT_VF_PAIRS,"\nBegin CHECK_ABOUT_VF_PAIRS");
    DEBUGMSG(CHECK_ABOUT_VF_PAIRS,"Check, if f has antipodal vertices in "
             <<"a set.");
    RT a,b,c,d,k;
    typename std::vector<typename InputDA::Vertex_handle>
      ::const_iterator  vtxit=V.begin();
    std::vector<typename InputDA::Vertex_handle> W;
    typename std::vector<typename InputDA::Vertex_handle>
      ::iterator neighborit;
    bool feasible=false;
    std::vector<typename InputDA::Vertex_handle> apv;
    std::vector<typename InputDA::Vertex_handle> visited_points;
    while (vtxit!=V.end()) {
      RT vx,vy,vz,vh;
      tco.get_point_coordinates((*vtxit)->point(),vx,vy,vz,vh);
      tco.get_plane_coefficients(f->plane(),a,b,c,k);
      //assume plane e2 parallel to e1 through v: e2:axh+byh+czh+d=0
      d = -a*vx - b*vy - c*vz;
      CGAL_assertion(vh > 0);
      DEBUGMSG(ASSERTION_OUTPUT,"vh is greater than zero (strictly). "
               <<"ASSERTION OK.");
      a=tco.get_a(f->plane())*vh;
      b=tco.get_b(f->plane())*vh;
      c=tco.get_c(f->plane())*vh;
      k=tco.get_d(f->plane())*vh;
      CGAL_assertion(a*vx+b*vy+c*vz+k*vh>=0);
      CGAL_assertion(a*vx+b*vy+c*vz+d*vh==0);
      DEBUGMSG(ASSERTION_OUTPUT,"Checked: Point on the right side of e1, and "
               <<"on e2. ASSERTION OK>");

      //If v lies on plane e1 then we can continue (v is not antipodal)
      if (d == k) {
        CGAL_assertion(a*vx+b*vy+c*vz+k*vh==0);
        DEBUGENDL(CHECK_ABOUT_VF_PAIRS,"Point "<<(*vtxit)->point()
                  <<" lies on plane ",f->plane()<<". Continue.");
        ++vtxit;
        continue;
      }
      CGAL_assertion(a*vx+b*vy+c*vz+k*vh>0);
      DEBUGMSG(ASSERTION_OUTPUT,"v not on e1. ASSERTION OK.");
      //Else we look if we can find a witness in the neighborhood of v that
      //shows that v is not an antipodal vertex of f
      neighbors_of((*vtxit)->halfedge(),W);

      //Assume there is no witness for infeasibility
      feasible = true;

      //Scan all possible witnesses
      while(!W.empty()) {
        neighborit=W.begin();
        visited_points.push_back(*neighborit);
        RT nx,ny,nz,nh;
        tco.get_point_coordinates((*neighborit)->point(),nx,ny,nz,nh);

        //Check if n (neighbor of v) satisfies restriction type e2
        //with the presumed plane e2, ie anx+bny+cnz-Dv<=0
        CGAL_assertion(a*nx+b*ny+c*nz+k*nh>=0);
        DEBUGMSG(ASSERTION_OUTPUT,"Restrictions E1 is satisfied. "
                 <<"ASSERTION OK.");
        if ( a*nx+b*ny+c*nz+d*nh >= 0 ) {
          //Could be a violation. Now check if v and n lie on the
          //same plane. If so no violation, otherwise we can break
          if (a*nx+b*ny+c*nz+d*nh == 0 ) {
            DEBUGMSG(CHECK_ABOUT_VF_PAIRS,"Additional Antipodal Vertex "
                     <<"found. Expanding "<<"set of witnesses.");
            //v and n are both (so far) antipodal vertices ==>EF-pair
            apv.push_back(*neighborit);
            //There could now be more witnesses that give violating
            //restrictions. Therefore compute the new neighbors of n
            std::vector<typename InputDA::Vertex_handle> Wnew;
            neighbors_of((*neighborit)->halfedge(),Wnew);

            //Erase v from the vertices to be considered as new witnesses
            typename
              std::vector<typename InputDA::Vertex_handle>
              ::iterator  res= std::find(Wnew.begin(),Wnew.end(),*vtxit);
            if ( res!=Wnew.end() )
              Wnew.erase(res);
            //Erase all the elements we already considered from the new
            //set of witnesses
            setminus(Wnew,visited_points);
            //Erase the neighbor vertex itself from the set of witnesses
            W.erase(neighborit);
            //Compute the new whole set of witnesses, that is add the
            //remaining new ones to the old set of witnesses
            setunion(W,Wnew);
          } else {
            DEBUGENDL(CHECK_ABOUT_VF_PAIRS,"Violation found. Not a feasible "
                     <<"solution. Violated Point:",(*neighborit)->point());
            //there is a violation, so do a break
            feasible = false;
            break;
          }
        } else {
          DEBUGENDL(CHECK_ABOUT_VF_PAIRS,"Restriction E2 also satisfied by "
                   <<"point:",(*neighborit)->point());
          //There is no violating restriction according to the vertex n
          //Erase it from the set of witnesses
          W.erase(neighborit);
        }
      } //end while(!W.empty())

      //Now we can determine if we have a feasible solution or not. Because
      //the feasible flag can only be set to false during the while-loop
      //we can be sure of the feasibility of our solution (no witness found)
      //if feasible is false then we have found a witness that v is not an
      //antipodal vertex. So we go on in the list of possible antipodal
      //vertices otherwise.
      if (feasible == true) {
        DEBUGMSG(CHECK_ABOUT_VF_PAIRS,"All witnesses checked. "
                 <<"Update width and antipodal vertices. Return true");
        apv.push_back(*vtxit);
#ifdef GCD_COMPUTATION
        simplify_solution(a,b,c,d,k);
#endif
        update_width(a,b,c,d,k);
        dao.set_antipodal_vertices(f,apv);
#ifdef VF_PAIR_OUTPUT
        DEBUGENDL(VF_PAIR_OUTPUT,"Antipodal vertices of plane: ",
                  f->plane());
        typename std::vector<Vertex_handle_>::iterator cavfpit=apv.begin();
        while(cavfpit!=apv.end()) {
          DEBUGENDL(VF_PAIR_OUTPUT,"Antipodal Vertex: ",
                    (*cavfpit)->point());
          ++cavfpit;
        }
#endif
        DEBUGMSG(CHECK_ABOUT_VF_PAIRS,"End CHECK_ABOUT_VF_PAIRS");
        return true;
      }
      ++vtxit;
    }//end while(vtxit!=V.end())
    //If we could not return with antipodal vertices we return false
    DEBUGMSG(CHECK_ABOUT_VF_PAIRS,"No new VF-pair found. Return false.");
    DEBUGMSG(CHECK_ABOUT_VF_PAIRS,"End CHECK_ABOUT_VF_PAIRS");
    return false;
  }

  // *** UPDATE_WIDTH ***
  //----------------------
  //This function we use to update the current best width. The old width is
  //compared with a new provided one and the better solution will we taken
  //as the new width. This function also saves all the possible quadruples
  //to be the width of the point set.
  void update_width(RT& a, RT& b, RT& c, RT& d, RT& k) {
    //Update the list of all possible solutions
    DEBUGMSG(UPDATE_WIDTH,"\nBegin UPDATE_WIDTH");
    std::vector<RT> sol;
    sol.push_back(a);
    sol.push_back(b);
    sol.push_back(c);
    sol.push_back(d);
    sol.push_back(k);
    allsolutions.push_back(sol);

    //Compute the squared width provided by the new solution
    RT tocompareNum=(k-d)*(k-d);
    RT tocompareDenom=(a*a+b*b+c*c);
    DEBUGENDL(UPDATE_WIDTH,"New possible width: ",tocompareNum
              <<" / "<<tocompareDenom);
    //Compare with old width
    if (WNum*tocompareDenom >= tocompareNum*WDenom) {
      if (WNum*tocompareDenom > tocompareNum*WDenom){
        DEBUGMSG(UPDATE_WIDTH,"Optimal width changes");
        WNum=tocompareNum;
        WDenom=tocompareDenom;
        alloptimal.clear();
        alloptimal.push_back(sol);
        A=a;
        B=b;
        C=c;
        D=d;
        K=k;
      } else {
        //now we have an additional optimal solution
        alloptimal.push_back(sol);
      }
    }//end if equal or better width
    DEBUGMSG(UPDATE_WIDTH,"End UPDATE_WIDTH");
  }

  // *** EE_COMPUTATION ***
  //------------------------
  //During the 3rd phase of the width-algorithm we have to rotate planes to
  //enumerate all possible edge-edge pairs. This rotating (in primal context)
  //resp. tracking edges (in the dual context) is made by the following
  //function. The edge we rotate about is called e. To ensure only to
  //enumerate a pair once (...only going forward) we need a set of
  //already visited vertices (Visited) and a set of vertices from that we know
  //they are antipodal to the first facet (V). In this function we don't
  //know the antipodal vertices of the second facet.
  template <class InputDA, class Halfedge_handle_, class Vertex_handle_>
    void EE_computation(InputDA,
                        Halfedge_handle_& e,
                        std::vector<Vertex_handle_>& V,
                        std::vector<Vertex_handle_>& Visited,
                        std::vector<Vertex_handle_>& Nnew) {
    DEBUGMSG(EE_COMPUTATION,"\nBegin EE_COMPUTATION");
    //Compute end points of e and two witnesses: Each in one of the two
    //facets participating
    Point_3 p,q;
    p=e->opposite()->vertex()->point();
    q=e->vertex()->point();
    typename InputDA::Vertex_handle w1=e->next()->vertex();
    typename InputDA::Vertex_handle w2=e->opposite()->next()->vertex();

    //prepare for the rotating procedure
    Nnew.clear();
    typename std::vector<typename InputDA::Vertex_handle>
      ::iterator  vtxit=V.begin();

    //Consider all the vertices in V. EE-pairs consist of p,q and the
    //vertex v in V and another neighbor vertex of v
    while(vtxit != V.end() ) {
      std::vector<typename InputDA::Vertex_handle> R;
      neighbors_of((*vtxit)->halfedge(),R);
      std::vector<typename InputDA::Vertex_handle> Witnesses;
      //The set of witnesses are all neighbor vertices of v (=R) and the
      //two vertices "on the other side" that ensure not rotating too far
      Witnesses.push_back(w1);
      Witnesses.push_back(w2);
      setunion(Witnesses,R);
      //The neighbor vertices of v that are also in the basic set V are of no
      //interest, so we exclude them
      setminus(R,V);
      //The set of all vertices we have already visited is also of no interest
      setminus(R,Visited);

      typename std::vector<typename InputDA::Vertex_handle>
        ::iterator rit=R.begin();
      //Now look at the modified set of neighbor vertices. For each neighbor r
      //we assume (p,q) and (v,r) to be an EE-pair and want then to find
      //witnesses that against this quadruple. If no such witness exist
      //(p,q) and (v,r) are a legal EE-pair. In that case we break the
      //quest and update all the sets Visited Nnew and V. If we have considered
      //all vertices v in V and all the respective r in R and if we have
      //not found a legal EE-pair, then an error occurs
      while (rit!=R.end()) {
        RT a,b,c,d,k;
        //It could be that the system is not uniquely solvable we only want to
        //enumerate proper solutions no degenerate ones
        if(solve_4x4(InputDA(),p,q,(*rit)->point(),(*vtxit)->point(),
                              a,b,c,d,k)){
          DEBUGMSG(EE_COMPUTATION,"Now we check if the provided "
                   <<"solution is a feasible one.");
#if !(defined(CGAL_KERNEL_NO_ASSERTIONS) || defined(CGAL_NO_ASSERTIONS) \
  || (!defined(CGAL_KERNEL_CHECK_EXPENSIVE) && !defined(CGAL_CHECK_EXPENSIVE)))
            RT px,py,pz,ph;
            tco.get_point_coordinates(p,px,py,pz,ph);
            CGAL_expensive_assertion(a*px+b*py+c*pz+k*ph>=0);
            CGAL_expensive_assertion(a*px+b*py+c*pz+d*ph<=0);
            tco.get_point_coordinates(q,px,py,pz,ph);
            CGAL_expensive_assertion(a*px+b*py+c*pz+k*ph>=0);
            CGAL_expensive_assertion(a*px+b*py+c*pz+d*ph<=0);
            tco.get_point_coordinates((*rit)->point(),px,py,pz,ph);
            CGAL_expensive_assertion(a*px+b*py+c*pz+k*ph>=0);
            CGAL_expensive_assertion(a*px+b*py+c*pz+d*ph<=0);
            tco.get_point_coordinates((*vtxit)->point(),px,py,pz,ph);
            CGAL_expensive_assertion(a*px+b*py+c*pz+k*ph>=0);
            CGAL_expensive_assertion(a*px+b*py+c*pz+d*ph<=0);
            DEBUGMSG(ASSERTION_OUTPUT,"All restrictions to the 4 points "
                     <<"are satisfied. ASSERTION OK.");
#endif
          if (check_feasibility(InputDA(),a,b,c,d,k,Witnesses)) {
            DEBUGMSG(EE_COMPUTATION,"Update Width and compute all "
                     <<"active restrictions");
            //Therefore we update the width
            update_width(a,b,c,d,k);
            //The next region we consider (because we only go forward)
            //contains (at least) r
            Nnew.push_back(*rit);

            //Now we look if we are in a special case, that is we look if
            //other restrictions according to  neighboring vertices of r
            //are also active. If so Nnew is expanded we them.
            std::vector<typename InputDA::Vertex_handle> S;
            neighbors_of((*rit)->halfedge(),S);
            //Because we only go forward we exclude v from the neighbor set S
            std::vector<typename InputDA::Vertex_handle> vtemp;
            vtemp.push_back(*vtxit);
            setminus(S,vtemp);
            //The check of more than 4 active restrictions begins
            typename
              std::vector<typename InputDA::Vertex_handle>
              ::iterator sit;
            while(!S.empty()) {
              sit=S.begin();
              RT sx,sy,sz,sh;
              tco.get_point_coordinates((*sit)->point(),sx,sy,sz,sh);
              if (a*sx+b*sy+c*sz+d*sh==0) {
                //This special case occurs now. Thus we extend Nnew
                Nnew.push_back(*sit);
                //In the neighborhood of this new active vertex could
                //also be other new active vertices but we are only interested
                //in new ones
                std::vector<typename InputDA::Vertex_handle> T;
                neighbors_of((*sit)->halfedge(),T);
                S.erase(sit);
                setunion(S,T);
                T.clear();
                T.push_back(*vtxit);
                setminus(S,T);
                setminus(S,Nnew);
              } else {
                //s is not active and we can erase it
                S.erase(sit);
              }
            } //end while (!S.empty())
            //Since we have now enumerated all EE-pairs with the active
            //restrictions according to p,q and v we can now leave
            //Nnew contains now all new active restrictions
            DEBUGMSG(EE_COMPUTATION,"End EE_COMPUTATION");
            return;
          } //end if (feasible)
        }//end if(proper)
        //Try next r
        ++rit;
      }//end while(!R.empty())
      //Try new v
      ++vtxit;
    }
    //There must be a new EE-pair. If not, an error occurs
    std::cerr<<"No new EE-pair found!"<<std::endl;
    CGAL_error();
  }


  // *** EE_PAIRS ***
  //------------------------
  //This function is similar to EE_computation. The difference is that now
  //we know the antipodal vertices of BOTH participating facets
  template <class InputDA, class Halfedge_handle_>
    void EE_pairs(InputDA& dao,
                  Halfedge_handle_& e,
                  std::vector<Halfedge_handle_>& impassable) {
    DEBUGMSG(EE_PAIRS,"\nBegin EE_PAIRS");
    Point_3 p,q;
    p=e->opposite()->vertex()->point();
    q=e->vertex()->point();
    typename InputDA::Vertex_handle w1=e->next()->vertex();
    typename InputDA::Vertex_handle w2=e->opposite()->next()->vertex();
    typename InputDA::Facet_handle f1=e->facet();
    typename InputDA::Facet_handle f2=e->opposite()->facet();
    std::vector<typename InputDA::Vertex_handle> V1;
    std::vector<typename InputDA::Vertex_handle> V2;
    dao.get_antipodal_vertices(f1,V1);
    dao.get_antipodal_vertices(f2,V2);
    std::vector<typename InputDA::Vertex_handle> N,V,Visited;
    V=V1;
    bool do_break = false;

    while (!setcut(V,V2)) {
      do_break = false;
      typename std::vector<typename InputDA::Vertex_handle>
        ::iterator  vtxit=V.begin();
      std::vector<typename InputDA::Vertex_handle> R;
      while (vtxit!=V.end()) {
        neighbors_of((*vtxit)->halfedge(),R);
        std::vector<typename InputDA::Vertex_handle> Witnesses;
        Witnesses.push_back(w1);
        Witnesses.push_back(w2);
        setunion(Witnesses,R);
        setminus(R,V);
        setminus(R,Visited);
        typename std::vector<typename InputDA::Vertex_handle>
          ::iterator  rit=R.begin();
        while (rit!=R.end()) {
          RT a,b,c,d,k;
          //It could be that the system is not uniquely solvable we only want
          //to enumerate proper solutions no degenerate ones
          if(solve_4x4(InputDA(),p,q,(*rit)->point(),(*vtxit)->point(),
                                a,b,c,d,k)){
            if (check_feasibility(InputDA(),a,b,c,d,k,Witnesses)) {
              update_width(a,b,c,d,k);
              N.push_back(*rit);
              std::vector<typename InputDA::Vertex_handle> S;
              neighbors_of((*rit)->halfedge(),S);
              setminus(S,V);
              typename
                std::vector<typename InputDA::Vertex_handle>
                ::iterator sit;
              while(!S.empty()) {
                sit=S.begin();
                RT sx,sy,sz,sh;
                tco.get_point_coordinates((*sit)->point(),sx,sy,sz,sh);
                if (a*sx+b*sy+c*sz+d*sh== 0) {
                  N.push_back(*sit);
                  std::vector<typename InputDA::Vertex_handle>
                    T;
                  neighbors_of((*sit)->halfedge(),T);
                  S.erase(sit);
                  setunion(S,T);
                  T.clear();
                  T.push_back(*vtxit);
                  setminus(S,T);
                  setminus(S,N);
                } else {
                  S.erase(sit);
                }
              }//end while (!S.empty())
              do_break=true;
              break;
            }//if (feasible)
          }//if(proper)
          ++rit;
        }//end while(!R.empty())
        if (do_break == true)
          break;
        ++vtxit;
      }//end while(!V.end())
      setunion(Visited,V);
      V=N;
    }
    impassable.pop_back();
    //Go on with next edge
    DEBUGMSG(EE_PAIRS,"End EE_PAIRS");
  }


  // *** ORIGIN_INSIDE_CH ***
  //-------------------------
  // To ensure that zero lies completely inside the convex hull of a point set.
  // Returns true if the point set is not coplanar, false otherwise
  // PRECONDITION: Iterator range has at least 3 points
  template<class InputDA, class Vertex_iterator_>
    bool origin_inside_CH(Vertex_iterator_& start,
                          Vertex_iterator_& beyond,
                          InputDA){
    DEBUGMSG(ORIGIN_INSIDE_CH,"\nBegin ORIGIN_INSIDE_CH");
    typename InputDA::Vertex_iterator first=start;
    //Take 4 points that build a tetrahedron. This tetrahedron is also
    //contained in the convex hull of the points. Thus every point
    //in/on this tetrahedron is a valable point for a new origin
    typename InputDA::PolyPoint p,q,r,s;
    p=(*first).point();
    ++first;
    q=(*first).point();
    ++first;
    r=(*first).point();
    ++first;
    RT px,py,pz,ph,qx,qy,qz,qh,rx,ry,rz,rh;
    tco.get_point_coordinates(p,px,py,pz,ph);
    tco.get_point_coordinates(q,qx,qy,qz,qh);
    tco.get_point_coordinates(r,rx,ry,rz,rh);
    CGAL_assertion(ph>0 && qh>0 && rh>0);
    RT tmpa,tmpb,tmpc,tmpk;
    tmpk=px*(qy*rz-ry*qz)-qx*(py*rz-ry*pz)+rx*(py*qz-qy*pz);
    tmpa=-ph*(qy*rz-ry*qz)+qh*(py*rz-ry*pz)-rh*(py*qz-qy*pz);
    tmpb=px*(rh*qz-qh*rz)-qx*(rh*pz-ph*rz)+rx*(qh*pz-ph*qz);
    tmpc=px*(ry*qh-qy*rh)-qx*(ry*ph-py*rh)+rx*(qy*ph-py*qh);
#ifdef GCD_COMPUTATION
    RT dummy=0;
    DEBUGENDL(ORIGIN_INSIDE_CH,"Solution of 3x3 (before GCD "
              <<"computation):\n",tmpa<<std::endl
              <<tmpb<<std::endl<<tmpc<<std::endl<<tmpk<<std::endl);
    simplify_solution(tmpa,tmpb,tmpc,tmpk,dummy);
#endif
    if (first==beyond) {
      DEBUGMSG(ORIGIN_INSIDE_CH,"3 coplanar Points. Computed plane through "
               <<"these points. Width=0.");
      WNum=0;
      WDenom=1;
      A=tmpa;
      B=tmpb;
      C=tmpc;
      K=tmpk;
      DEBUGENDL(ORIGIN_INSIDE_CH,"Solution of 3x3:\n",A<<std::endl
                <<B<<std::endl<<C<<std::endl<<K<<std::endl);
      D=K;
      std::vector <RT> sol;
      sol.push_back(A);
      sol.push_back(B);
      sol.push_back(C);
      sol.push_back(D);
      sol.push_back(K);
      allsolutions.push_back(sol);
      alloptimal.push_back(sol);
      DEBUGMSG(ORIGIN_INSIDE_CH,"End ORIGIN_INSIDE_CH");
      return false;
    } else {
      s=(*first).point();
      RT sx,sy,sz,sh;
      tco.get_point_coordinates(s,sx,sy,sz,sh);
      //Ensure that the 4 points are not coplanar. If so take another 4th point
      while (tmpa*sx+tmpb*sy+tmpc*sz+tmpk*sh==0 && first!=beyond) {
        s=(*first).point();
        tco.get_point_coordinates(s,sx,sy,sz,sh);
        ++first;
      }
      //If we could not find a valable 4th point, then the set of the points
      //is coplanar. Therefore the width is zero and we can terminate the
      //algorithm
      if (tmpa*sx+tmpb*sy+tmpc*sz+tmpk*sh==0) {
        DEBUGMSG(ORIGIN_INSIDE_CH,"n coplanar Points. Compute plane through "
                 <<"these points. Width=0.");
        WNum=0;
        WDenom=1;
        A=tmpa;
        B=tmpb;
        C=tmpc;
        K=tmpk;
        DEBUGENDL(ORIGIN_INSIDE_CH,"Solution of 3x3:\n",A<<std::endl
                  <<B<<std::endl<<C<<std::endl<<K<<std::endl);
        D=K;
        std::vector <RT> sol;
        sol.push_back(A);
        sol.push_back(B);
        sol.push_back(C);
        sol.push_back(D);
        sol.push_back(K);
        allsolutions.push_back(sol);
        alloptimal.push_back(sol);
        DEBUGMSG(ORIGIN_INSIDE_CH,"End ORIGIN_INSIDE_CH");
        return false;
      } else {
        //Take center of tetrahedron pqrs
        RT ux,uy,uz,uh,vx,vy,vz,vh,nox,noy,noz,noh;
        ux=px*qh+ph*qx;
        vx=rx*sh+rh*sx;
        uy=py*qh+ph*qy;
        vy=ry*sh+rh*sy;
        uz=pz*qh+ph*qz;
        vz=rz*sh+rh*sz;
        uh=RT(2)*ph*qh;
        vh=RT(2)*rh*sh;
        nox=ux*vh+uh*vx;
        noy=uy*vh+uh*vy;
        noz=uz*vh+uh*vz;
        noh=RT(2)*uh*vh;
        neworigin=tco.make_point(nox,noy,noz,noh);
        CGAL_assertion(noh!=0);
        DEBUGENDL(ORIGIN_INSIDE_CH,"New Origin: ",neworigin);
        //Translate all the points
        first=start;
        while(first!=beyond) {
          typename InputDA::PolyPoint tmp=(*first).point();
          RT tmpx,tmpy,tmpz,tmph;
          tco.get_point_coordinates(tmp,tmpx,tmpy,tmpz,tmph);
          RT newx,newy,newz,newh;
          newx=tmpx*noh-tmph*nox;
          newy=tmpy*noh-tmph*noy;
          newz=tmpz*noh-tmph*noz;
          newh=tmph*noh;
          DEBUGENDL(ORIGIN_INSIDE_CH,"Old Point: ",(*first).point());
#ifdef GCD_COMPUTATION
          RT dummy=0;
          simplify_solution(newx,newy,newz,newh,dummy);
#endif
          (*first).point()=tco.make_point(newx,newy,newz,newh);
          DEBUGENDL(ORIGIN_INSIDE_CH,"New Point: ",(*first).point());
          ++first;
        }
        DEBUGMSG(ORIGIN_INSIDE_CH,"Zero now inside polyhedron.");
        DEBUGMSG(ORIGIN_INSIDE_CH,"End ORIGIN_INSIDE_CH");
        return true;
      }
    }
  }


  /* ****************************************************** */
  /* *** --- *** The main enumeration functions *** --- *** */
  /* ****************************************************** */
  template<class InputPolyhedron>
  void width_3_convex(InputPolyhedron &P) {
    DEBUGMSG(WIDTH_3_CONVEX,"\nBegin WIDTH_3_CONVEX");
    typedef CGAL::Width_3_internal::Data_access<InputPolyhedron,Traits> DA;
    typedef typename DA::Facet_handle Facet_handle;
    typedef typename DA::Vertex_handle Vertex_handle;
    typedef typename DA::Halfedge_handle Halfedge_handle;
    typedef typename DA::Vertex_iterator Vertex_iterator;
    //Ensure that Polyhedron has at least one vertex
    CGAL_assertion_msg(P.size_of_vertices()>2,
         "Can not compute width of a 0, 1 or 2-vertex polyhedron");

    Vertex_iterator first=P.vertices_begin();
    Vertex_iterator beyond=P.vertices_end();

    //Begin with Phase 2
    if (origin_inside_CH(first,beyond,DA())) {
      DEBUGMSG(WIDTH_3_CONVEX,"Origin is now Inside the Polyhedron. "
               <<std::endl
               <<"And polyhedron has at least 4 not coplanar vertices");

      DA dao;
      std::vector<Halfedge_handle> go_on;
      std::vector<Halfedge_handle> impassable;

      //Ensure that the plane equations are determined because of the
      //compare operator in DA
      Facet_handle feq=P.facets_begin();
      while(feq!=P.facets_end()) {
        compute_plane_equation(DA(),feq);
        ++feq;
      }
      DEBUGMSG(WIDTH_3_CONVEX,"All plane equations of all facets computed.");

      //ensure all flags are false
#if !(defined(CGAL_KERNEL_NO_ASSERTIONS) || defined(CGAL_NO_ASSERTIONS))
        int halfedgecount=0;
#endif
        Halfedge_handle esf=P.halfedges_begin();
        while(esf!=P.halfedges_end()) {
#if !(defined(CGAL_KERNEL_NO_ASSERTIONS) || defined(CGAL_NO_ASSERTIONS))
          ++halfedgecount;
#endif
          DEBUGENDL(EDGE_INITIALIZING,"Edge e: "
                    <<esf->opposite()->vertex()->point()
                    <<" --> ",esf->vertex()->point());
          dao.set_visited_flag(esf,false);
          dao.set_impassable_flag(esf,false);
          ++esf;
        }

#if !(defined(CGAL_KERNEL_NO_ASSERTIONS) || defined(CGAL_NO_ASSERTIONS))
      CGAL_assertion(int(P.size_of_halfedges())==halfedgecount);
      DEBUGENDL(WIDTH_3_CONVEX,"Visited all ",halfedgecount
                <<" halfedges. ASSERTION OK.");
      CGAL_assertion(dao.size_of_visited()==halfedgecount);
      CGAL_assertion(dao.size_of_impassable()==halfedgecount);
      DEBUGMSG(WIDTH_3_CONVEX,"Map sizes of visited and impassable "
               <<"halfedges are correct. ASSERTION OK.");
#endif
      DEBUGMSG(WIDTH_3_CONVEX,"All flags set to false.");

      //Now begin with the main enumeration
      Facet_handle f = P.facets_begin();
      initial_VF_pair(dao,f,P,go_on);

#if !(defined(CGAL_KERNEL_NO_ASSERTIONS) || defined(CGAL_NO_ASSERTIONS) \
  || (!defined(CGAL_KERNEL_CHECK_EXPENSIVE) && !defined(CGAL_CHECK_EXPENSIVE)))
        Vertex_iterator vtxass=P.vertices_begin();
        while(vtxass!=P.vertices_end()) {
          RT px,py,pz,ph;
          tco.get_point_coordinates(vtxass->point(),px,py,pz,ph);
          CGAL_expensive_assertion(A*px+B*py+C*pz+K*ph>=0);
          CGAL_expensive_assertion(A*px+B*py+C*pz+D*ph<=0);
          ++vtxass;
        }
        //Assert that the initial facet has antipodal vertices
        //and that all incident edges are visited (flag=true) but
        //that the impassable flag is not set yet.
        std::vector<Vertex_handle> avass;
        dao.get_antipodal_vertices(f,avass);
        DEBUGENDL(ASSERTION_OUTPUT,"Size of avass: ",avass.size());
        CGAL_expensive_assertion(avass.size()!=0);
        Halfedge_handle eass=f->halfedge();
        Halfedge_handle eass0=eass;
        CGAL_expensive_assertion(dao.is_visited(eass));
        CGAL_expensive_assertion(!dao.is_impassable(eass));
        eass=eass->next();
        while (eass != eass0) {
          CGAL_expensive_assertion(dao.is_visited(eass));
          CGAL_expensive_assertion(!dao.is_impassable(eass));
          eass=eass->next();
        }
        DEBUGMSG(ASSERTION_OUTPUT,"All edges of the first initial facet "
                 <<"has a visited flag.");
#endif
      // Begin Phase 3
      Facet_handle fnext;
      Halfedge_handle e;
      std::vector<Vertex_handle> Visited;
      std::vector<Vertex_handle> N;
      std::vector<Vertex_handle> Nnew;

      //While there still exist an edge we can rotate an incident facet with
      //known antipodal vertices in the other facet with unknown antipodal
      //vertices then do this rotation
      while ( !go_on.empty()) {
        DEBUGENDL(WIDTH_3_CONVEX,"Size of go_on: ",go_on.size());
#ifdef GO_ON_OUTPUT
        DEBUGMSG(GO_ON_OUTPUT,"Edges on stack go_on:");
        typename std::vector<Halfedge_handle>::iterator
          goonit=go_on.begin();
        while(goonit!=go_on.end()) {
          DEBUGENDL(GO_ON_OUTPUT,"Edge: ",
                    (*goonit)->opposite()->vertex()->point()<<" --> "
                    <<(*goonit)->vertex()->point());
          ++goonit;
        }
#endif
        //Take last edge on stack go_on
        e=go_on.back();
        //Check if e is a proper edge or not. If so determine fnext
        if (preparation_check(dao,e,fnext,go_on,impassable)) {
          DEBUGMSG(WIDTH_3_CONVEX,"Preparation Check successful");
          //f is the facet of which we know the antipodal vertices
          f=e->facet();
          Visited.clear();
          dao.get_antipodal_vertices(f,N);
          CGAL_assertion (!N.empty());
          DEBUGMSG(ASSERTION_OUTPUT,"f has some antipodal vertices. Assertion "
                   <<"successful.");
          while(!check_about_VF_pairs(dao,fnext,N)) {
            DEBUGMSG(WIDTH_3_CONVEX,"No new VF-pair. Continue (Begin) "
                     <<"rotation of the planes.");
            EE_computation(DA(),e,N,Visited,Nnew);
            DEBUGMSG(WIDTH_3_CONVEX,"Planes have been rotated. Check now "
                     <<"for a new VF-pair");
            setunion(Visited,N);
            N=Nnew;
          }
        }
      }
#if !(defined(CGAL_KERNEL_NO_ASSERTIONS) || defined(CGAL_NO_ASSERTIONS) \
  || (!defined(CGAL_KERNEL_CHECK_EXPENSIVE) && !defined(CGAL_CHECK_EXPENSIVE)))
      Facet_handle fass=P.facets_begin();
      while(fass!=P.facets_end()) {
        std::vector<Vertex_handle> apvass;
        dao.get_antipodal_vertices(fass,apvass);
        DEBUGENDL(ASSERTION,"Current checking facet: ",fass->plane());
        CGAL_assertion(!apvass.empty());
        ++fass;
      }
      DEBUGMSG(ASSERTION,"All facets have antipodal vertices. "
               <<"ASSERTION OK.");
      Facet_handle fec = P.facets_begin();
      std::vector<Halfedge_handle> fakego_on;
      DA daoec;
      while(fec!=P.facets_end()) {
        std::vector<Vertex_handle> avec;
        std::vector<Vertex_handle> avivf;
        initial_VF_pair(daoec,fec,P,fakego_on);
        daoec.get_antipodal_vertices(fec,avec);
        dao.get_antipodal_vertices(fec,avivf);
        CGAL_assertion(int(avivf.size())==int(avec.size()));
        CGAL_assertion(int(avec.size())>0);
        DEBUGENDL(EXPENSIVE_CHECKS_OUTPUT,"Antipodal vertices of facet: ("
                  <<fec->halfedge()->opposite()->vertex()->point()
                  <<"), (",fec->halfedge()->vertex()->point()<<"), ("
                  <<fec->halfedge()->next()->vertex()->point()<<")");
        std::vector<Vertex_handle>::iterator vtxit=avec.begin();
        while(vtxit!=avec.end()) {
          std::vector<Vertex_handle>::iterator it;
          it=std::find(avivf.begin(),avivf.end(),*vtxit);
          CGAL_assertion(it!=avivf.end());
          DEBUGENDL(EXPENSIVE_CHECKS_OUTPUT,"Antipodal vertex: ",
                    (*vtxit)->point());
          ++vtxit;
        }
        ++fec;
      }
      DEBUGMSG(EXPENSIVE_CHECKS_OUTPUT,"All VF-pairs verified. "
               <<"Expensive Check successful.");
//
//    This assertion should not currently be true since the convex hull
//    polyhedron is triangulated;  no postprocessing is done to merge coplanar
//    neighboring facets.
//
//    CGAL_assertion(dao.size_of_antipodal_vertices()
//                    ==int(P.size_of_facets()));
#endif
      //Begin with phase 4. As long as the set of impassable edges is not empty
      //rotate one of the planes into the other sharing the impassable edge
      while(!impassable.empty()) {
        //Take top edge on stack impassable
        e=impassable.back();
        EE_pairs(dao,e,impassable);
        //In EE_pairs the top element will be removed
      }
    }
    DEBUGMSG(WIDTH_3_CONVEX,"Width computed.");
  }
};

} //namespace CGAL

#endif
