with Float_Vectors;
with Integer_Vectors_of_Vectors;
with Transforming_Integer_Vector_Lists;  use Transforming_Integer_Vector_Lists;
with Initial_Mixed_Cell;
with Vertices,Simplices;                 use Vertices,Simplices;
with Global_Dynamic_Triangulation;       use Global_Dynamic_Triangulation;
with Integer_Lifting_Utilities;          use Integer_Lifting_Utilities;
with Dynamic_Lifting_Functions;          use Dynamic_Lifting_Functions;
with Flatten_Mixed_Subdivisions;         use Flatten_Mixed_Subdivisions;
with Enumerate_Faces_of_Polytope;        use Enumerate_Faces_of_Polytope;
with Common_Faces_of_Polytope;           use Common_Faces_of_Polytope;
with Integer_Pruning_Methods;            use Integer_Pruning_Methods;
with Mixed_Volume_Computation;           use Mixed_Volume_Computation;
with Contributions_to_Mixed_Volume;      use Contributions_to_Mixed_Volume;

--with text_io,integer_io;             use text_io,integer_io;
--with Integer_Vectors_io;             use Integer_Vectors_io;
--with Lists_of_Integer_Vectors_io;    use Lists_of_Integer_Vectors_io;
--with Faces_of_Polytope_io;           use Faces_of_Polytope_io;
--with Mixed_Subdivisions_io;          use Mixed_Subdivisions_io;
--with Triangulations_io;              use Triangulations_io;

package body Dynamic_Mixed_Subdivisions is

-- UTILITIES :

  function Is_Empty ( points : Array_of_Lists ) return boolean is
  begin
    for i in points'range loop
      if not Is_Null(points(i))
       then return false;
      end if;
    end loop;
    return true;
  end Is_Empty;

  function First_Non_Empty ( points : Array_of_Lists ) return integer is

  -- DESCRIPTION :
  --   Returns the index of the first non empty set in the points.

    res : integer := points'first - 1;

  begin
    for i in points'range loop
      if not Is_Null(points(i))
       then res := i;
      end if;
      exit when res >= points'first;
    end loop;
    return res;
  end First_Non_Empty;

  function Is_In ( fs : Face_Structure; point : vector ) return boolean is
  begin
    if Is_Null(fs.t)
     then return Is_In(fs.l,point);
     else return Is_In(fs.t,point);
    end if;
  end Is_In;

--  procedure put ( n : in natural; fs : in Face_Structure ) is
--  begin
--    put_line("The list of lifted points : "); put(fs.l);
--    put_line("The list of k-faces : "); put(fs.f);
--    put_line("The triangulation : "); put(n,fs.t);
--  end put;

--  procedure put ( n : in natural; fs : in Face_Structures ) is
--  begin
--    for i in fs'range loop
--      put("face structure for component "); put(i,1);
--      put_line(" :");
--      put(n,fs(i));
--    end loop;
--  end put;

-- FLATTENING :

  procedure Flatten ( points : in out Integer_Vectors_of_Vectors.Vector ) is

    pt : Link_to_Vector;

  begin
    for i in points'range loop
      pt := points(i);
      if pt(pt'last) /= 0
       then pt(pt'last) := 0;
      end if;
    end loop;
  end Flatten;

  procedure Flatten ( f : in out Face ) is
  begin
    Flatten(f.all);
  end Flatten;

  procedure Flatten ( fs : in out Faces ) is

    tmp : Faces := fs;
    f : Face;

  begin
    while not Is_Null(tmp) loop
      f := Head_Of(tmp);
      Flatten(f);
      Set_Head(tmp,f);
      tmp := Tail_Of(tmp);
    end loop;
  end Flatten;

  procedure Flatten ( fs : in out Face_Structure ) is
  begin
    Flatten(fs.l); Flatten(fs.t); Flatten(fs.f);
  end Flatten;

  procedure Flatten ( fs : in out Face_Structures ) is
  begin
    for i in fs'range loop
      Flatten(fs(i));
    end loop;
  end Flatten;

-- ZERO CONTRIBUTION :

  function Extract_Facet ( n : natural; s : Simplex ) return Face is

    ver : constant Integer_Vectors_of_Vectors.Vector := Simplices.Vertices(s);
    res : Face := new Integer_Vectors_of_Vectors.Vector(ver'range);

  begin
    for i in res'range loop
      res(i) := new Integer_Vectors.Vector'(ver(i)(1..n));
    end loop;
    return res;
  end Extract_Facet;

  function Extract_Facets ( n : natural; t : Triangulation; x : Vector )
                          return Faces is

  -- DESCRIPTION :
  --   Returns the list of facets in t that all contain x.
  --   The facets are given in their original coordinates.

  -- REQUIRED : x is lifted, i.e., x'length = n+1.

    tmp : Triangulation := t;
    res,res_last : Faces;

  begin
    while not Is_Null(tmp) loop
      declare
        s : Simplex := Head_Of(tmp);
      begin
        if Is_Vertex(s,x)
         then Append(res,res_last,Extract_Facet(n,s));
        end if;
      end;
      tmp := Tail_Of(tmp);
    end loop;
    return res;
  end Extract_Facets;

  function Zero_Contribution ( n : natural; fs : Face_Structures;
                               x : Vector; i : natural ) return boolean is

  -- DESCRIPTION :
  --   Returns true if the point x does not contribute to the mixed volume
  --   when considered to the ith component of the already lifted points.

  -- REQUIRED : x'length = n+1, n = dimension before lifting.

    res : boolean := false;
    supp : Array_of_Lists(fs'range);

  begin
    for j in supp'range loop
      supp(j) := Reduce(fs(j).l,n+1);
    end loop;
    if Length_Of(fs(i).t) > 1
     then declare
            facets : Faces := Extract_Facets(n,fs(i).t,x);
          begin
            res := Simple_Zero_Contribution(supp,facets,x(1..n),i);
          end;
     else res := Simple_Zero_Contribution(supp,x(1..n),i);
    end if;
    Deep_Clear(supp);
    return res;
  end Zero_Contribution;

-- INITIALIZATION :

  procedure Initialize 
               ( n : in natural; mix : in Vector; points : in Array_of_Lists;
                 mixsub : in out Mixed_Subdivision;
                 fs : in out Face_Structures; rest : in out Array_of_Lists;
                 done : in out boolean ) is

  -- DESCRIPTION :
  --   Performs initialization of the dynamic lifting algorithm.

  -- ON ENTRY :
  --   n         length of the vectors in points;
  --   mix       type of mixture;
  --   mixsub    mixed subdivision of the lifted points;
  --   fs        face structures of the lifted points.

  -- ON RETURN :
  --   mixsub    if empty on entry, then it contains the initial mixed
  --             cell, in case the problem is nondegenerate;
  --   rest      rest of point list to be processed;
  --   done      if true, then either the mixed volume equals zero,
  --             or there are no more points to process.

    null_points : Array_of_Lists(points'range);

  begin
    if Is_Null(mixsub)
     then -- compute an initial mixed cell
       declare
         mic : Mixed_Cell;
       begin
         Initial_Mixed_Cell(n,mix,points,mic,rest);
         if Mixed_Volume(n,mix,mic) > 0 -- check on degeneracy
          then
           -- initialize the mixed subdivision :
            Construct(mic,mixsub);
           -- initialize the face structures :
            for i in mic.pts'range loop
              declare
                tmp : List := mic.pts(i);
                fc : Face;
              begin
                while not Is_Null(tmp) loop
                  Append(fs(i).l,fs(i).last,Head_Of(tmp).all);
                  tmp := Tail_of(tmp);
                end loop;
                fc := new Integer_Vectors_of_Vectors.Vector'
                                    (Shallow_Create(mic.pts(i)));
                Construct(fc,fs(i).f);
              end;
            end loop;
            if mix'last = mix'first
             then declare
                    v : constant Integer_Vectors_of_Vectors.Vector
                          := Shallow_Create(fs(fs'first).l);
                    s : Simplex := Create(v);
                  begin
                    fs(fs'first).t := Create(s);
                  end;
            end if;
            done := Is_Empty(rest);
          else -- degenerate problem => mixed volume = 0
            rest := null_points; done := true;
         end if;
       end;
     else
       rest := points;
       done := Is_Empty(rest);
    end if;
  end Initialize;

  function Initial_Triangulation
                ( n : in natural; l : in List; point : in Link_to_Vector )
                return Triangulation is

  -- DESCRIPTION :
  --   Given a list of lifted points with Length_Of(l) >= n and another
  --   lifted point, a nonempty triangulation will be returned, when the
  --   volume of \conv(l U {point}) > 0.

    res : Triangulation;
    del,span : List;
    delpoint : Link_to_Vector := new vector(1..n);

    function Deflate ( lifted : in List ) return List is

    -- DESCRIPTION :
    --   Discards the lifting value of the points in the list l.

      res,res_last : List;
      tmp : List := lifted;
      pt : Link_to_Vector;

    begin
      while not Is_Null(tmp) loop
        pt := Head_Of(tmp);
        declare
          dpt : Link_to_Vector := new vector(1..n);
        begin
          dpt.all := pt(dpt'range);
          Append(res,res_last,dpt);
        end;
        tmp := Tail_Of(tmp);
      end loop;
      return res;
    end Deflate;

    function Lift ( pt,liftpt : Link_to_Vector; lifted : List )
                  return Link_to_Vector is

    -- DESCRIPTION :
    --   Compares the value of pt with that of liftpt and
    --   looks up the lifting value of the point pt in the list lifted.
    --   The lifted point will be returned.

    -- REQUIRED :
    --   Either the point pt should be equal to Deflate(liftpt)
    --   or it should belong to Deflate(lifted).

      tmp : List := lifted;
      res : Link_to_Vector;

    begin
      if pt.all = liftpt(pt'range)
       then return liftpt;
       else while not Is_Null(tmp) loop
              res := Head_Of(tmp);
              if pt.all = res(pt'range)
               then return res;
               else tmp := Tail_Of(tmp);
              end if;
            end loop;
            return res;
      end if;
    end Lift;

  begin
    del := Deflate(l);
    delpoint.all := point(delpoint'range);
    Construct(delpoint,del);
    span := Extremal_Points(n,del);
    if Length_Of(span) = n+1
     then
       declare
         verpts : Integer_Vectors_of_Vectors.Vector(1..n+1);
         tmp : List := span;
         pt : Link_to_Vector;
         s : Simplex;
       begin
         for i in verpts'range loop
           verpts(i) := Lift(Head_Of(tmp),point,l);
           tmp := Tail_Of(tmp);
         end loop;
         s := Create(verpts);
         res := Create(s);
         tmp := l;
         while not Is_Null(tmp) loop
           pt := Head_Of(tmp);
           if not Is_In(span,pt)
            then Update(res,pt);
           end if;
           tmp := Tail_Of(tmp);
         end loop;
       end;
    end if;
    Clear(del); Clear(span); Clear(delpoint);
    return res;
  end Initial_Triangulation;

-- CHOOSE NEXT POINT :

  procedure Next_Point
                ( n : in natural; points : in out Array_of_Lists;
                  order : in boolean;
                  index : in out integer; point : out Link_to_Vector ) is

  -- DESCRIPTION :
  --   Chooses a point out of the lists of points.

  -- ON ENTRY :
  --   n          length of the elements in the point lists;
  --   points     nonempty lists of points;
  --   order      if true, then the first element out of the next first
  --              nonempty list will be chosen;
  --   index      indicates component where not Is_Null(points(index)).

  -- ON RETURN :
  --   points     lists of points with the point removed;
  --   index      points to the next nonempty list,
  --              if index < points'first, then Is_Empty(points);
  --   point      the next point to be processed.

    newindex : integer := points'first-1;
    pt : Link_to_Vector;

  begin
    if order
     then pt := Head_Of(points(index));
     else pt := Max_Extreme(points(index),n,-3,3);
          Swap_to_Front(points(index),pt);
    end if;
    points(index) := Tail_Of(points(index));
    for i in (index+1)..points'last loop
      if not Is_Null(points(i))
       then newindex := i;
      end if;
      exit when newindex >= points'first;
    end loop;
    if newindex < points'first
     then for i in points'first..index loop
            if not Is_Null(points(i))
             then newindex := i;
            end if;
            exit when newindex >= points'first;
          end loop;
    end if;
    index := newindex;
    point := pt;
  end Next_Point;

-- UPDATE ROUTINES :

  procedure Merge ( mic : in out Mixed_Cell;
                    mixsub : in out Mixed_Subdivision ) is

    tmp : Mixed_Subdivision := mixsub;
    done : boolean := false;

  begin
    while not Is_Null(tmp) loop
      declare
        mic1 : Mixed_Cell := Head_Of(tmp);
        last : List;
      begin
        if Equal(mic.nor,mic1.nor)
         then for k in mic1.pts'range loop
                last := mic1.pts(k);
                while not Is_Null(Tail_Of(last)) loop
                  last := Tail_Of(last);
                end loop;
                Deep_Concat_Diff(mic.pts(k),mic1.pts(k),last);
              end loop;
              done := true;
         else tmp := Tail_Of(tmp);
        end if;
      end;
      exit when done;
    end loop;
    if done
     then Deep_Clear(mic);
     else Construct(mic,mixsub);
    end if;
  end Merge;

  procedure Merge ( cells : in Mixed_Subdivision;
                    mixsub : in out Mixed_Subdivision ) is

    tmp : Mixed_Subdivision := cells;

  begin
    while not Is_Null(tmp) loop
      declare
        mic : Mixed_Cell := Head_Of(tmp);
      begin
        Merge(mic,mixsub);
      end;
      tmp := Tail_Of(tmp);
    end loop;
  end Merge;

  procedure Compute_New_Faces
                 ( fs : in out Face_Structure; k,n : in natural;
                   point : in out Link_to_Vector; newfs : out Faces ) is

  -- DESCRIPTION :
  --   Given a point, the new faces will be computed and returned.

  -- ON ENTRY :
  --   fs          a face structure;
  --   k           dimension of the faces to generate;
  --   n           dimension without the lifting;
  --   point       point that has to be in all the faces.

  -- ON RETURN :
  --   fs          face structure with updated triangulation,
  --               the new faces are not added, also the list is not updated;
  --   point       lifted conservatively w.r.t. fs.t;
  --   newfs       k-faces, which all contain the given point.

    res,res_last : Faces;

    procedure Append ( fc : in Integer_Vectors_of_Vectors.Vector ) is
      f : Face;
    begin
      f := new Integer_Vectors_of_Vectors.Vector'(fc);
      Append(res,res_last,f);
    end Append;
    procedure EnumLis is new Enumerate_Lower_Faces_in_List(Append);
    procedure EnumTri is new Enumerate_Faces_in_Triangulation(Append);

  begin
   -- COMPUTE THE NEW FACES AND UPDATE fs :
    if Is_Null(fs.t)
     then
       if Length_Of(fs.l) >= n
        then
          fs.t := Initial_Triangulation(n,fs.l,point);
          if Is_Null(fs.t)
           then EnumLis(fs.l,point,k);
           else EnumTri(fs.t,point,k);
          end if;
        else 
          EnumLis(fs.l,point,k);
       end if;
     else 
       declare
         newt : Triangulation;
       begin
         point(point'last) := Lift_to_Place(fs.t,point.all);
         Update(fs.t,point,newt);
         Enumtri(newt,point,k);
       end;
    end if;
    Append(fs.l,fs.last,point);
    newfs := res;
  end Compute_New_Faces;

  procedure Swap ( index : in natural; points : in out Array_of_Lists ) is

  -- DESCRIPTION :
  --   Swaps the first component of points with the component index.

    tmp : List := points(index);

  begin
    points(index) := points(points'first);
    points(points'first) := tmp;
  end Swap;

  procedure Swap ( index : in natural; mixsub : in out Mixed_Subdivision ) is

  -- DESCRIPTION :
  --   Swaps the first component of each cell with the component index.

    tmp : Mixed_Subdivision := mixsub;

  begin
    while not Is_Null(tmp) loop
      declare
        mic : Mixed_Cell := Head_Of(tmp);
      begin
        Swap(index,mic.pts.all);
       -- Set_Head(tmp,mic);
      end;
      tmp := Tail_Of(tmp);
    end loop;
  end Swap;

  procedure Create_Cells
                ( index,n : in natural; mix : in Vector;
                  afs : in Array_of_Faces; lif : in Array_of_Lists;
                  nbsucc,nbfail : in out Float_Vectors.Vector;
                  mixsub : out Mixed_Subdivision ) is

  -- DESCRIPTION :
  --   Creates a mixed subdivision by considering the faces 
  --   of component index first.

    res : Mixed_Subdivision;
    wrkmix : Vector(mix'range) := mix;
    wrkafs : Array_of_Faces(afs'range) := afs;
    wrklif : Array_of_Lists(lif'range) := lif;

  begin
    wrkmix(wrkmix'first) := mix(index); wrkmix(index) := mix(mix'first);
    wrkafs(wrkafs'first) := afs(index); wrkafs(index) := afs(afs'first);
    wrklif(wrklif'first) := lif(index); wrklif(index) := lif(lif'first);
    Create_CS(n,wrkmix,wrkafs,wrklif,nbsucc,nbfail,res);
    Swap(index,res);
    mixsub := res;
  end Create_Cells;

  procedure Compute_New_Cells 
                ( n : in natural; mix : in Vector; mic : in Mixed_Cell;
                  afs : in Array_of_Faces; index : in integer;
                  lifted : in Array_of_Lists; mixsub : out Mixed_Subdivision;
                  nbsucc,nbfail : in out Float_Vectors.Vector ) is

  -- DESCRIPTION :
  --   Computes the new cells by considering a given mixed cell with
  --   a tuple of faces.

  -- ON ENTRY :
  --   n          dimension before lifting;
  --   mix        type of mixture;
  --   mic        a given mixed cell;
  --   afs        type of faces;
  --   index      component where new faces are computed for;
  --   lifted     tuple of lifted points;
  --   nbsucc     number of succesful tests of face-face combinations;
  --   nbfail     number of unsuccesful tests of face-face combinations.

  -- ON RETURN :
  --   mixsub     the new mixed cells;
  --   nbsucc     updated number of succesful tests;
  --   nbfail     updated number of unsuccesful tests;

    res,res2 : Mixed_Subdivision;
    neiafs : Array_of_Faces(afs'range);

  begin
    neiafs(index) := Neighboring_Faces(mic,afs(index),index);
    if not Is_Null(neiafs(index))
     then
       for i in neiafs'range loop
         if i /= index
          then neiafs(i) := Neighboring_Faces(mic,afs(i),i);
         end if;
       end loop;
       if index /= 1
        then Create_Cells(index,n,mix,neiafs,lifted,nbsucc,nbfail,res);
        else Create_CS(n,mix,neiafs,lifted,nbsucc,nbfail,res);
       end if;
       for i in neiafs'range loop
         if i /= index
          then Shallow_Clear(neiafs(i));
         end if;
       end loop;
    end if;
    Shallow_Clear(neiafs(index));
    res2 := Create(lifted,res); -- test on normals
    Shallow_Clear(res);
    mixsub := res2;
  end Compute_New_Cells;

  procedure Compute_New_Cells
               ( n : in natural; mix : in Vector;
                 mixsub : in Mixed_Subdivision; index : in integer;
                 newfaces : in Faces; fs : in Face_Structures;
                 newcells : out Mixed_Subdivision; 
                 nbsucc,nbfail : in out Float_Vectors.Vector ) is

    res : Mixed_Subdivision;
    afs : Array_of_Faces(fs'range);
    lifted : Array_of_Lists(fs'range);

  begin
   -- CREATE NEW CELLS ONLY WITH THE NEW FACES :
    for i in afs'range loop
      if i = index
       then afs(index) := newfaces;
       else afs(i) := fs(i).f;
      end if;
      lifted(i) := fs(i).l;
    end loop;
    if not Is_Null(fs(index).t)
     then 
      -- ENUMERATE ALL CELLS IN mixsub :
       declare
         tmp : Mixed_Subdivision := mixsub;
       begin
         while not Is_Null(tmp) loop
           declare
             newcells2 : Mixed_Subdivision;
           begin
             Compute_New_Cells(n,mix,Head_Of(tmp),afs,index,lifted,
                               newcells2,nbsucc,nbfail);
             Merge(newcells2,res); Shallow_Clear(newcells2);
           end;
           tmp := Tail_Of(tmp);
         end loop;
       end;
     else
      -- COMPUTE ALL NEW CELLS AT ONCE :
       declare
         res2 : Mixed_Subdivision;
       begin
         if index /= 1
          then Create_Cells(index,n,mix,afs,lifted,nbsucc,nbfail,res2);
          else Create_CS(n,mix,afs,lifted,nbsucc,nbfail,res2);
         end if;
         res := Create(lifted,res2);
         Shallow_Clear(res2);
       end;
    end if;
    newcells := res;
  end Compute_New_Cells;

-- BASIC VERSION : WITHOUT OUTPUT GENERICS :

  procedure Dynamic_Lifting
                ( n : in natural; mix : in Vector;
                  points : in Array_of_Lists; 
                  order,inter,conmv : in boolean; maxli : in natural;
                  mixsub : in out Mixed_Subdivision;
                  fs : in out Face_Structures;
                  nbsucc,nbfail : in out Float_Vectors.Vector ) is

    rest,inner : Array_of_Lists(points'range);
    index,newindex : integer;
    finished : boolean := false;
    pt : Link_to_Vector;
    nexli : natural := 1;

  begin
    Initialize(n,mix,points,mixsub,fs,rest,finished);
    if not finished
     then 
       index := First_Non_Empty(rest); newindex := index;
       while not finished loop  -- ITERATE UNTIL NO MORE POINTS :
         Next_Point(n,rest,order,newindex,pt);
         declare
           point : Link_to_Vector := new vector(pt'first..pt'last+1);
           newfa : Faces;
           newcells : Mixed_Subdivision;
         begin -- LIFT THE POINT CONSERVATIVELY :
           point(pt'range) := pt.all;
           point(point'last) := 1;
           if inter and then Is_In(fs(index),point.all)
            then
              Clear(point); Construct(pt,inner(index));
            else
              nexli := Conservative_Lifting(mixsub,index,point.all);
              if (maxli > 0) and then nexli > maxli
               then Flatten(mixsub); Flatten(fs);
                    nexli := 1;
              end if;
              point(point'last) := nexli;
             -- COMPUTE NEW FACES AND NEW CELLS :
              Compute_New_Faces(fs(index),mix(index),n,point,newfa);
              if not conmv or else not Zero_Contribution(n,fs,point.all,index)
               then Compute_New_Cells(n,mix,mixsub,index,newfa,fs,
                                      newcells,nbsucc,nbfail);
             -- UPDATE THE MIXED SUBDIVISION AND THE FACE STRUCTURES :
                    Construct(newcells,mixsub);   Shallow_Clear(newcells);
              end if;
              Construct(fs(index).f,newfa); Shallow_Clear(newfa);
           end if;
         end;
         index := newindex;
         finished := (index < rest'first);
       end loop;
    end if;
    if inter  -- lift out the interior points
     then for i in inner'range loop
            Constant_Lifting(inner(i),nexli,fs(i).l,fs(i).last);
            Shallow_Clear(inner(i));
          end loop;
    end if;
  exception
    when numeric_error    -- probably due to a too high lifting
       => Flatten(mixsub); Flatten(fs);
          Dynamic_Lifting(n,mix,rest,order,inter,conmv,maxli,mixsub,fs,
                          nbsucc,nbfail);
  end Dynamic_Lifting;

-- EXTENDED VERSIONS : WITH OUTPUT GENERICS :

  procedure Dynamic_Lifting_with_Flat
                ( n : in natural; mix : in Vector; points : in Array_of_Lists; 
                  order,inter,conmv : in boolean; maxli : in natural;
                  mixsub : in out Mixed_Subdivision;
                  fs : in out Face_Structures;
                  nbsucc,nbfail : in out Float_Vectors.Vector ) is

    rest,inner : Array_of_Lists(points'range);
    index,newindex : integer;
    finished : boolean := false;
    pt : Link_to_Vector;
    nexli : natural := 1;

  begin
    Initialize(n,mix,points,mixsub,fs,rest,finished);
    if not finished
     then
       index := First_Non_Empty(rest); newindex := index;
       while not finished loop -- ITERATE UNTIL NO MORE POINTS :
         Next_Point(n,rest,order,newindex,pt);
         declare
           point : Link_to_Vector := new vector(pt'first..pt'last+1);
           newfa : Faces;
           newcells : Mixed_Subdivision;
         begin -- LIFT THE POINT CONSERVATIVELY :
           point(pt'range) := pt.all;
           point(point'last) := 1;
           if inter and then Is_In(fs(index),point.all)
            then
              Clear(point); Construct(pt,inner(index));
            else
              nexli := Conservative_Lifting(mixsub,index,point.all);
              if (maxli > 0) and then nexli > maxli
               then Before_Flattening(mixsub,fs); 
                    Flatten(mixsub); Flatten(fs);
                    nexli := 1;
              end if;
              point(point'last) := nexli;
             -- COMPUTE NEW FACES AND NEW CELLS :
              Compute_New_Faces(fs(index),mix(index),n,point,newfa);
              if not conmv or else not Zero_Contribution(n,fs,point.all,index)
               then Compute_New_Cells(n,mix,mixsub,index,newfa,fs,
                                      newcells,nbsucc,nbfail);
             -- UPDATE THE MIXED SUBDIVISION AND THE FACE STRUCTURES :
                    Construct(newcells,mixsub);   Shallow_Clear(newcells);
              end if;
              Construct(fs(index).f,newfa); Shallow_Clear(newfa);
           end if;
         end;
         index := newindex;
         finished := (index < rest'first);
       end loop;
    end if;
    if inter  -- lift out the interior points
     then for i in inner'range loop
            Constant_Lifting(inner(i),nexli,fs(i).l,fs(i).last);
            Shallow_Clear(inner(i));
          end loop;
    end if;
  exception
    when numeric_error    -- probably due to a too high lifting
       => Before_Flattening(mixsub,fs);
          Flatten(mixsub); Flatten(fs);
          Dynamic_Lifting_with_Flat(n,mix,rest,order,inter,conmv,maxli,mixsub,
                                    fs,nbsucc,nbfail);
  end Dynamic_Lifting_with_Flat;

  procedure Dynamic_Lifting_with_New
                ( n : in natural; mix : in Vector; points : in Array_of_Lists; 
                  order,inter,conmv : in boolean; maxli : in natural;
                  mixsub : in out Mixed_Subdivision;
                  fs : in out Face_Structures;
                  nbsucc,nbfail : in out Float_Vectors.Vector ) is

    rest,inner : Array_of_Lists(points'range);
    index,newindex : integer;
    finished : boolean := false;
    pt : Link_to_Vector;
    nexli : natural := 1;

  begin
    Initialize(n,mix,points,mixsub,fs,rest,finished);
    Process_New_Cells(mixsub,0,Head_Of(fs(fs'last).last).all);
    if not finished
     then
       index := First_Non_Empty(rest); newindex := index;
       while not finished loop -- ITERATE UNTIL NO MORE POINTS :
         Next_Point(n,rest,order,newindex,pt);
         declare
           point : Link_to_Vector := new vector(pt'first..pt'last+1);
           newfa : Faces;
           newcells : Mixed_Subdivision;
         begin -- LIFT THE POINT CONSERVATIVELY :
           point(pt'range) := pt.all;
           point(point'last) := 1;
           if inter and then Is_In(fs(index),point.all)
            then
              Clear(point); Construct(pt,inner(index));
            else
              nexli := Conservative_Lifting(mixsub,index,point.all);
              if (maxli > 0) and then nexli > maxli
               then Flatten(mixsub); Flatten(fs);
                    nexli := 1;
              end if;
              point(point'last) := nexli;
             -- COMPUTE NEW FACES AND NEW CELLS :
              Compute_New_Faces(fs(index),mix(index),n,point,newfa);
              if not conmv or else not Zero_Contribution(n,fs,point.all,index)
               then Compute_New_Cells(n,mix,mixsub,index,newfa,fs,newcells,
                                      nbsucc,nbfail);
                    Process_New_Cells(newcells,index,point.all);
             -- UPDATE THE MIXED SUBDIVISION AND THE FACE STRUCTURES :
                    Construct(newcells,mixsub);   Shallow_Clear(newcells);
              end if;
              Construct(fs(index).f,newfa); Shallow_Clear(newfa);
           end if;
         end;
         index := newindex;
         finished := (index < rest'first);
       end loop;
    end if;
    if inter  -- lift out the interior points
     then for i in inner'range loop
            Constant_Lifting(inner(i),nexli,fs(i).l,fs(i).last);
            Shallow_Clear(inner(i));
          end loop;
    end if;
  exception
    when numeric_error    -- probably due to a too high lifting
       => Flatten(mixsub); Flatten(fs);
          Dynamic_Lifting_with_New(n,mix,rest,order,inter,conmv,maxli,mixsub,fs,
                                   nbsucc,nbfail);
  end Dynamic_Lifting_with_New;

  procedure Dynamic_Lifting_with_Flat_and_New
                ( n : in natural; mix : in Vector; points : in Array_of_Lists; 
                  order,inter,conmv : in boolean; maxli : in natural;
                  mixsub : in out Mixed_Subdivision;
                  fs : in out Face_Structures;
                  nbsucc,nbfail : in out Float_Vectors.Vector ) is

    rest,inner : Array_of_Lists(points'range);
    index,newindex : integer;
    finished : boolean := false;
    pt : Link_to_Vector;
    nexli : natural := 1;

  begin
    Initialize(n,mix,points,mixsub,fs,rest,finished);
    Process_New_Cells(mixsub,0,Head_Of(fs(fs'last).last).all);
    if not finished
     then 
       index := First_Non_Empty(rest); newindex := index;
       while not finished loop -- ITERATE UNTIL NO MORE POINTS 
         Next_Point(n,rest,order,newindex,pt);
         declare
           point : Link_to_Vector := new vector(pt'first..pt'last+1);
           newfa : Faces;
           newcells : Mixed_Subdivision;
         begin -- LIFT THE POINT CONSERVATIVELY :
           point(pt'range) := pt.all;
           point(point'last) := 1;
           if inter and then Is_In(fs(index),point.all)
            then
              Clear(point); Construct(pt,inner(index));
            else
              nexli := Conservative_Lifting(mixsub,index,point.all);
              if (maxli > 0) and then nexli > maxli
               then Before_Flattening(mixsub,fs);
                    Flatten(mixsub); Flatten(fs);
                    nexli := 1;
              end if;
              point(point'last) := nexli;
             -- COMPUTE NEW FACES AND NEW CELLS :
              Compute_New_Faces(fs(index),mix(index),n,point,newfa);
              if not conmv or else not Zero_Contribution(n,fs,point.all,index)
               then Compute_New_Cells(n,mix,mixsub,index,newfa,fs,newcells,
                                      nbsucc,nbfail);
                    Process_New_Cells(newcells,index,point.all);
             -- UPDATE THE MIXED SUBDIVISION AND THE FACE STRUCTURES :
                    Construct(newcells,mixsub);   Shallow_Clear(newcells);
              end if;
              Construct(fs(index).f,newfa); Shallow_Clear(newfa);
           end if;
         end;
         index := newindex;
         finished := (index < rest'first);
       end loop;
    end if;
    if inter  -- lift out the interior points
     then for i in inner'range loop
            Constant_Lifting(inner(i),nexli,fs(i).l,fs(i).last);
            Shallow_Clear(inner(i));
          end loop;
    end if;
  exception
    when numeric_error    -- probably due to a too high lifting
       => Before_Flattening(mixsub,fs);
          Flatten(mixsub); Flatten(fs);
          Dynamic_Lifting_with_Flat_and_New
              (n,mix,rest,order,inter,conmv,maxli,mixsub,fs,nbsucc,nbfail);
  end Dynamic_Lifting_with_Flat_and_New;

end Dynamic_Mixed_Subdivisions;
