with Floating_Point_Numbers;              use Floating_Point_Numbers;
with Float_Vectors;
with Permutations;                        use Permutations;
with Permute_Operations;                  use Permute_Operations;
with Lists_of_Integer_Vectors;
with Lists_of_Float_Vectors;
with Arrays_of_Integer_Vector_Lists;
with Arrays_of_Float_Vector_Lists;
with Mixed_Volume_Computation;            use Mixed_Volume_Computation;

package body Generating_Mixed_Cells is

-- FIRST TARGET ROUTINE :

  function Permute ( l : Lists_of_Integer_Vectors.List; p : Permutation )
                   return Lists_of_Integer_Vectors.List is

  -- DESCRIPTION :
  --   Applies the permutation p to all elements in the list l.

    use Lists_of_Integer_Vectors;

    tmp,res,res_last : List;

  begin
    tmp := l;
    while not Is_Null(tmp) loop
      declare
        plv,lv : Integer_Vectors.Link_to_Vector;
      begin
        lv := Head_Of(tmp);
        plv := new Integer_Vectors.Vector'(p*lv.all);
        plv(plv'last) := lv(lv'last);  -- same lifting !
        Append(res,res_last,plv.all);
        Integer_Vectors.Clear(plv);
      end;
      tmp := Tail_Of(tmp);
    end loop;
    return res;
  end Permute;

  function Permute ( p : Permutation; mix : Vector;
                     mic : Integer_Mixed_Subdivisions.Mixed_Cell )
                   return Integer_Mixed_Subdivisions.Mixed_Cell is

  -- DESCRIPTION :
  --   Permutes the components of mic.pts according to the permutation p.

    use Lists_of_Integer_Vectors;
    use Arrays_of_Integer_Vector_Lists;
    use Integer_Mixed_Subdivisions;

    res : Mixed_Cell;
    index : natural;
 
  begin
    res.nor := new Integer_Vectors.Vector'(mic.nor.all);
    res.pts := new Array_of_Lists(mic.pts'range);
    for k in res.pts'range loop
      index := Compute_Index(p(k),mix);
      Copy(mic.pts(index),res.pts(k));
    end loop;
    return res;
  end Permute;

  function Permute ( mic : Integer_Mixed_Subdivisions.Mixed_Cell;
                     p : Permutation )
                   return Integer_Mixed_Subdivisions.Mixed_Cell is
 
  -- DESCRIPTION :
  --   Applies permutation p on the mixed cell mic.

    use Arrays_of_Integer_Vector_Lists;
    use Integer_Mixed_Subdivisions;

    res : Mixed_Cell;

  begin
    res.nor := new Integer_Vectors.Vector'(p*mic.nor.all);
    res.nor(res.nor'last) := mic.nor(mic.nor'last);
    res.pts := new Array_of_Lists(mic.pts'range);
    for k in mic.pts'range loop
      res.pts(k) := Permute(mic.pts(k),p);
    end loop;
    return res;
  end Permute;

  procedure Permute_and_Append
               ( v,w : in List_of_Permutations;
                 mic : in Integer_Mixed_Subdivisions.Mixed_Cell;
                 mix : in Vector;
                 mixsub,mixsub_last
                     : in out Integer_Mixed_Subdivisions.Mixed_Subdivision ) is

  -- DESCRIPTION :
  --   Applies all permutations to the mixed cell and appends the results.

    use Integer_Mixed_Subdivisions;

    lv,lw : List_of_Permutations;

  begin
    lv := v; lw := w;
    while not Is_Null(lv) loop
      declare
        vmic,wmic : Mixed_Cell;
      begin
        vmic := Permute(mic,Permutation(Head_Of(lv).all));
        wmic := Permute(Permutation(Head_Of(lw).all),mix,vmic);
        Deep_Clear(vmic);
        if not Is_In(mixsub,wmic.nor.all)
         then Append(mixsub,mixsub_last,wmic);
        end if;
      end;
      lv := Tail_Of(lv);
      lw := Tail_Of(lw);
    end loop;
  end Permute_and_Append;

  function Generating_Cells
              ( v,w : List_of_Permutations; mix : Vector;
                mixsub : Integer_Mixed_Subdivisions.Mixed_Subdivision )
              return Integer_Mixed_Subdivisions.Mixed_Subdivision is

    use Integer_Mixed_Subdivisions;

    tmp,res,res_last,done,done_last : Mixed_Subdivision;

  begin
    tmp := mixsub;
    while not Is_Null(tmp) loop
      declare
        mic : Mixed_Cell := Head_Of(tmp);
      begin
        if not Is_In(done,mic.nor.all)
         then Append(res,res_last,mic);
              Permute_and_Append(v,w,mic,mix,done,done_last);
        end if;
      end;
      tmp := Tail_Of(tmp);
    end loop;
    Clear(done);
    return res;
  end Generating_Cells;

-- SECOND TARGET ROUTINE :

  function Permute ( l : Lists_of_Float_Vectors.List; p : Permutation )
                   return Lists_of_Float_Vectors.List is

  -- DESCRIPTION :
  --   Applies the permutation p to all elements in the list l.

    use Lists_of_Float_Vectors;

    tmp,res,res_last : List;

  begin
    tmp := l;
    while not Is_Null(tmp) loop
      declare
        plv,lv : Float_Vectors.Link_to_Vector;
      begin
        lv := Head_Of(tmp);
        plv := new Float_Vectors.Vector'(p*lv.all);
        plv(plv'last) := lv(lv'last);  -- same lifting !
        Append(res,res_last,plv.all);
        Float_Vectors.Clear(plv);
      end;
      tmp := Tail_Of(tmp);
    end loop;
    return res;
  end Permute;

  function Permute ( p : Permutation; mix : Vector;
                     mic : Float_Mixed_Subdivisions.Mixed_Cell )
                   return Float_Mixed_Subdivisions.Mixed_Cell is

  -- DESCRIPTION :
  --   Permutes the components of mic.pts according to the permutation p.

    use Lists_of_Float_Vectors;
    use Arrays_of_Float_Vector_Lists;
    use Float_Mixed_Subdivisions;

    res : Mixed_Cell;
    index : natural;
 
  begin
    res.nor := new Float_Vectors.Vector'(mic.nor.all);
    res.pts := new Array_of_Lists(mic.pts'range);
    for k in res.pts'range loop
      index := Compute_Index(p(k),mix);
      Copy(mic.pts(index),res.pts(k));
    end loop;
    return res;
  end Permute;

  function Permute ( mic : Float_Mixed_Subdivisions.Mixed_Cell;
                     p : Permutation )
                   return Float_Mixed_Subdivisions.Mixed_Cell is
 
  -- DESCRIPTION :
  --   Applies permutation p on the mixed cell mic.

    use Arrays_of_Float_Vector_Lists;
    use Float_Mixed_Subdivisions;

    res : Mixed_Cell;

  begin
    res.nor := new Float_Vectors.Vector'(p*mic.nor.all);
    res.nor(res.nor'last) := mic.nor(mic.nor'last);
    res.pts := new Array_of_Lists(mic.pts'range);
    for k in mic.pts'range loop
      res.pts(k) := Permute(mic.pts(k),p);
    end loop;
    return res;
  end Permute;

  procedure Permute_and_Append
               ( v,w : in List_of_Permutations;
                 mic : in Float_Mixed_Subdivisions.Mixed_Cell;
                 mix : in Vector;
                 mixsub,mixsub_last
                     : in out Float_Mixed_Subdivisions.Mixed_Subdivision ) is

  -- DESCRIPTION :
  --   Applies all permutations to the mixed cell and appends the results.

    use Float_Mixed_Subdivisions;

    lv,lw : List_of_Permutations;

  begin
    lv := v; lw := w;
    while not Is_Null(lv) loop
      declare
        vmic,wmic : Mixed_Cell;
      begin
        vmic := Permute(mic,Permutation(Head_Of(lv).all));
        wmic := Permute(Permutation(Head_Of(lw).all),mix,vmic);
        Deep_Clear(vmic);
        if not Is_In(mixsub,wmic.nor.all)
         then Append(mixsub,mixsub_last,wmic);
        end if;
      end;
      lv := Tail_Of(lv);
      lw := Tail_Of(lw);
    end loop;
  end Permute_and_Append;

  function Generating_Cells
              ( v,w : List_of_Permutations; mix : Vector;
                mixsub : Float_Mixed_Subdivisions.Mixed_Subdivision )
              return Float_Mixed_Subdivisions.Mixed_Subdivision is

    use Float_Mixed_Subdivisions;

    tmp,res,res_last,done,done_last : Mixed_Subdivision;

  begin
    tmp := mixsub;
    while not Is_Null(tmp) loop
      declare
        mic : Mixed_Cell := Head_Of(tmp);
      begin
        if not Is_In(done,mic.nor.all)
         then Append(res,res_last,mic);
              Permute_and_Append(v,w,mic,mix,done,done_last);
        end if;
      end;
      tmp := Tail_Of(tmp);
    end loop;
    Clear(done);
    return res;
  end Generating_Cells;

-- THIRD TARGET ROUTINE :

  function Generate_Cells
              ( v,w : List_of_Permutations; mix : Vector;
                mixsub : Integer_Mixed_Subdivisions.Mixed_Subdivision )
              return Integer_Mixed_Subdivisions.Mixed_Subdivision is

    use Integer_Mixed_Subdivisions;

    tmp,res,res_last : Mixed_Subdivision;

  begin
    tmp := mixsub;
    while not Is_Null(tmp) loop
      declare
        mic : Mixed_Cell := Head_Of(tmp);
      begin
        Permute_and_Append(v,w,mic,mix,res,res_last);
      end;
      tmp := Tail_Of(tmp);
    end loop;
    return res;
  end Generate_Cells;

  function Exists_Permutation
               ( v1,v2 : Integer_Vectors.Link_to_Vector ) return boolean is

  -- DESCRIPTION :
  --   Returns true if there exists a permutation p: v1 = p*v1.

  begin
    if v1(v1'last) /= v2(v2'last)
     then return false;  -- they must have the same lifted component !
     else return Permutable(v1(v1'first..v1'last-1),v2(v2'first..v2'last-1));
    end if;
  end Exists_Permutation;

  function Permutable ( mic : Integer_Mixed_Subdivisions.Mixed_Cell;
                        mixsub : Integer_Mixed_Subdivisions.Mixed_Subdivision )
                      return boolean is

    use Integer_Mixed_Subdivisions;
    tmp : Mixed_Subdivision := mixsub;
    mic2 : Mixed_Cell;

  begin
    while not Is_Null(tmp) loop
      mic2 := Head_Of(tmp);
      if Exists_Permutation(mic.nor,mic2.nor)
       then return true;
       else tmp := Tail_Of(tmp);
      end if;
    end loop;
    return false;
  end Permutable;

  function Generating_Cells
             ( mixsub : Integer_Mixed_Subdivisions.Mixed_Subdivision )
             return Integer_Mixed_Subdivisions.Mixed_Subdivision is

    use Integer_Mixed_Subdivisions;

    tmp,res,res_last : Mixed_Subdivision;
    mic : Mixed_Cell;
  
  begin
    tmp := mixsub;
    while not Is_Null(tmp) loop
      mic := Head_Of(tmp);
      if not Permutable(mic,res)
       then Append(res,res_last,mic);
      end if;
      tmp := Tail_Of(tmp);
    end loop;
    return res;
  end Generating_Cells;

-- FOURTH TARGET ROUTINE :

  function Exists_Permutation
              ( v1,v2 : Float_Vectors.Link_to_Vector ) return boolean is

  -- DESCRIPTION :
  --   Returns true if there exists a permutation p: v1 = p*v1.

  begin
    if v1(v1'last) /= v2(v2'last)
     then return false;  -- they must have the same lifted component !
     else return Permutable(v1(v1'first..v1'last-1),v2(v2'first..v2'last-1));
    end if;
  end Exists_Permutation;

  function Permutable ( mic : Float_Mixed_Subdivisions.Mixed_Cell;
                        mixsub : Float_Mixed_Subdivisions.Mixed_Subdivision )
                      return boolean is

    use Float_Mixed_Subdivisions;

    tmp : Mixed_Subdivision := mixsub;
    mic2 : Mixed_Cell;

  begin
    while not Is_Null(tmp) loop
      mic2 := Head_Of(tmp);
      if Exists_Permutation(mic.nor,mic2.nor)
       then return true;
       else tmp := Tail_Of(tmp);
      end if;
    end loop;
    return false;
  end Permutable;

  function Generating_Cells
             ( mixsub : Float_Mixed_Subdivisions.Mixed_Subdivision )
             return Float_Mixed_Subdivisions.Mixed_Subdivision is

    use Float_Mixed_Subdivisions;

    tmp,res,res_last : Mixed_Subdivision;
    mic : Mixed_Cell;
  
  begin
    tmp := mixsub;
    while not Is_Null(tmp) loop
      mic := Head_Of(tmp);
      if not Permutable(mic,res)
       then Append(res,res_last,mic);
      end if;
      tmp := Tail_Of(tmp);
    end loop;
    return res;
  end Generating_Cells;

end Generating_Mixed_Cells;
