with integer_io,Numbers_io;               use integer_io,Numbers_io;
with Communications_with_User;            use Communications_with_User;
with Timing_Package;                      use Timing_Package;
with Natural_Vectors;
with Complex_Multivariate_Polynomials;    use Complex_Multivariate_Polynomials;
with Complex_Polynomial_Systems_io;       use Complex_Polynomial_Systems_io;
with Reduction_of_Polynomial_Systems;     use Reduction_of_Polynomial_Systems;
with Reduction_of_Overconstrained_Systems;
 use Reduction_of_Overconstrained_Systems;

package body Drivers_for_Reduction is

  procedure Display_Info is

    i : array(1..12)of string(1..65);

  begin
    i( 1):="The goal of reduction is to rewrite the system into an equivalent";
    i( 2):="one  (i.e.:  the  same  finite  solutions) that has a lower total";
    i( 3):="degree, so  that  fewer  solution  paths  need  to  be  followed.";
    i( 4):="Sometimes  reduction  can  already detect whether a system has no";
    i( 5):="solutions or an infinite number of solutions.                    ";
    i( 6):="  We distinguish between linear  and  nonlinear  reduction.   The";
    i( 7):="first  type  performs  row-reduction on the coefficient matrix of";
    i( 8):="the system.  By nonlinear reduction, highest-degree monomials are";
    i( 9):="eliminated   by  replacing  a  polynomial  in  the  system  by  a";
    i(10):="Subtraction-polynomial.  This second type is more  powerful,  but";
    i(11):="also  more  expensive.   Bounds  have  to  be  set  to  limit the";
    i(12):="combinatorial enumeration.                                       ";
    for k in i'range loop
      put_line(i(k));
    end loop;
  end Display_Info;

  procedure Display_Menu ( exit_opt : in boolean; ans : in out character ) is

    m : array(0..3) of string(1..65);

  begin
    m(0):="  0 : No Reduction            : leave the menu                   ";
    m(1):="  1 : Linear Reduction        : triangulate coefficient matrix   ";
    m(2):="  2 : Sparse Linear Reduction : diagonalize coefficient matrix   ";
    m(3):="  3 : Nonlinear Reduction     : S-polynomial combinations        ";
    loop
      new_line;
      put_line("MENU for Reducing Polynomial Systems :");
      if exit_opt
       then for i in m'range loop
              put_line(m(i));
            end loop;
            put("Type 0, 1, 2, or 3 to select reduction, or i for info : ");
            Ask_Alternative(ans,"0123i");
       else for i in 1..m'last loop
              put_line(m(i));
            end loop;
            put("Type 1 , 2, or 3 to select reduction, or i for info : ");
            Ask_Alternative(ans,"123i");
      end if;
      if ans = 'i'
       then new_line; Display_Info;
      end if;
      exit when ans /= 'i';
    end loop;
  end Display_Menu;

  procedure Rationalize ( p : in out Poly_Sys ) is

  -- DESCRIPTION :
  --   Shortens the exponent vectors when an unknown disappears.

    n : natural := p'length;
    to_rationalize : boolean;

    procedure rationalize (p : in out Poly; k : in natural) is

      procedure rat_term (t : in out Term; cont : out boolean) is
        tmp : Degrees := new Natural_Vectors.Vector'(1..n-1 => 0);
      begin
        for i in t.dg'range loop
          if i < k
           then tmp(i) := t.dg(i);
           elsif i > k
               then tmp(i-1) := t.dg(i);
          end if;
        end loop;
        Natural_Vectors.Clear(Natural_Vectors.Link_to_Vector(t.dg));
        t.dg := tmp;
        cont := true;
      end rat_term;
      procedure rat_terms is new Changing_Iterator(rat_term);

    begin
      rat_terms(p);
    end rationalize;

  begin
    for i in reverse 1..n loop
      to_rationalize := true;   -- suppose the i-th unknown may disappear
      for j in 1..n loop
        if Degree(p(j),i) > 0
         then to_rationalize := false;
        end if;
        exit when not to_rationalize;
      end loop;
      if to_rationalize
       then for j in 1..n loop
              rationalize(p(j),i);
            end loop;
      end if;
    end loop;
  end Rationalize;

  procedure Write_Diagnostics
               ( file : in file_type; p : in Poly_Sys;
                 diagonal,inconsistent,infinite : in boolean;
                 d : out natural ) is

  -- DESCRIPTION :
  --   Writes diagnostics after reduction.

    b : natural;

  begin
    if not diagonal
     then if inconsistent
           then put_line("  Inconsistent system: no solutions.");
                put_line(file,"  Inconsistent system: no solutions.");
           elsif infinite
               then put("  Probably an infinite number");
                    put_line(" of solutions.");
                    put(file,"  Probably an infinite number");
                    put_line(file," of solutions.");
               else put("  The total degree is ");
                    put(file,"  The total degree is ");
                    b := Total_Degree(p);
                    put(b,1); put(file,b,1);
                    put_line("."); put_line(file,".");
                    d := b;
          end if;
     else put_line("  No initial terms could be eliminated.");
          put_line(file,"  No initial terms could be eliminated.");
    end if;
  end Write_Diagnostics;

  procedure Write_Results ( file : in file_type; p : in Poly_Sys;
                            timer : in timing_widget; banner : in string ) is
  begin
    new_line(file);
    put_line(file,"THE REDUCED SYSTEM :");
    put(file,p);
    new_line(file);
    print_times(file,timer,banner);
  end Write_Results;

  procedure Driver_for_Linear_Reduction
              ( file : in file_type; p : in out Poly_Sys; d : out natural ) is

    timer : timing_widget;
    diagonal,inconsistent,infinite : boolean := false;

  begin
    new_line(file);
    put_line(file,"LINEAR REDUCTION : ");
    tstart(timer);
    Reduce(p,diagonal,inconsistent,infinite);
    tstop(timer);
    Write_Diagnostics(file,p,diagonal,inconsistent,infinite,d);
    Write_Results(file,p,timer,"Linear Reduction");
  end Driver_for_Linear_Reduction;

  procedure Driver_for_Sparse_Linear_Reduction
              ( file : in file_type; p : in out Poly_Sys; d : out natural ) is

    timer : timing_widget;
    diagonal,inconsistent,infinite : boolean := false;

  begin
    new_line(file);
    put_line(file,"SPARSE LINEAR REDUCTION : ");
    tstart(timer);
    Sparse_Reduce(p,inconsistent,infinite);
    tstop(timer);
    Write_Diagnostics(file,p,diagonal,inconsistent,infinite,d);
    Write_Results(file,p,timer,"Sparse Reduction");
  end Driver_for_Sparse_Linear_Reduction;

  procedure Driver_for_Nonlinear_Reduction
                ( file : in file_type;
                  p : in out Poly_Sys; d : out natural ) is

    res : Poly_Sys(p'range);
    sparse : boolean;
    cnt_eq,cnt_sp,cnt_rp,max_eq,max_sp,max_rp : natural;

    timer : timing_widget;
    b : natural;

  begin
    Rationalize(p);
    Clear(res); Copy(p,res);

    new_line(file);
    put_line(file,"NONLINEAR REDUCTION :");
    put("  Give the limit on #equal degree replacements : ");
    Read_Natural(max_eq); cnt_eq := 0;
    put("  Give the limit on #computed S-polynomials : ");
    Read_Natural(max_sp); cnt_sp := 0;
    put("  Give the limit on #computed R-polynomials : ");
    Read_Natural(max_rp); cnt_rp := 0;
    put(file,"  The limit on #equal degree replacements : ");
    put(file,max_eq,1); new_line(file);
    put(file,"  The limit on #computed S-polynomials : ");
    put(file,max_sp,1); new_line(file);
    put(file,"  The limit on #computed R-polynomials : ");
    put(file,max_rp,1); new_line(file);

    sparse := false;
    tstart(timer);
    if sparse
     then Sparse_Reduce(p,res,cnt_eq,max_eq);
     else Reduce(p,res,cnt_eq,max_eq,cnt_sp,max_sp,cnt_rp,max_rp);
    end if;
    tstop(timer);
    Clear(p); Copy(res,p); Clear(res);
    b := Total_Degree(p);
    put("The total degree is "); put(b,1);
    put(file,"The total degree is "); put(file,b,1);
    put_line("."); put_line(file,".");
    d := b;

    new_line(file); 
    put_line(file,"Amount of arithmetic work");
    put(file,"   #equal replacements     : ");
    put(file,cnt_eq,4); new_line(file);
    put(file,"   #computed S-polynomials : ");
    put(file,cnt_sp,4); new_line(file);
    put(file,"   #computed R-polynomials : ");
    put(file,cnt_rp,4); new_line(file);
    new_line(file);

    put_line(file,"The reduced system :");
    put(file,p);
    new_line(file);
    print_times(file,timer,"Nonlinear Reduction");
    new_line(file);
  end Driver_for_Nonlinear_Reduction;

  procedure Driver_for_Overconstrained_Reduction
                ( file : in file_type; p : in out Poly_Sys ) is

    ans : character;
    n : constant natural := p'length;
    m : constant natural := Number_of_Unknowns(p(p'first));
    res : Poly_Sys(1..m);

  begin
    new_line;
    put_line("MENU for Overconstrained Reduction :");
    put_line("  0. No reduction : leave the menu.");
    put_line("  1. Add a random combination of the remainder to the first.");
    put_line("  2. Reduce the first equations with the remainder.");
    put("Type 0, 1, or 2 to select reduction : ");
    Ask_Alternative(ans,"012");
    case ans is
      when '1' => res := Random_Square(p);
      when '2' => res := Reduced_Square(p);
      when others => null;
    end case;
    if ans /= '0'
     then for i in 1..m loop
            Copy(res(i),p(i)); Clear(res(i));
          end loop;
          for i in m+1..n loop
            Clear(p(i));
          end loop;
    end if;
  end Driver_for_Overconstrained_Reduction;

  procedure Driver_for_Reduction
               ( file : in file_type; p : in out Poly_Sys; d : out natural;
                 exit_option : in boolean ) is

    n : constant natural := p'length;
    ans : character := '0';

  begin
    Display_Menu(exit_option,ans);
    case ans is
      when '1' => Driver_for_Linear_Reduction(file,p,d);
      when '2' => Driver_for_Sparse_Linear_Reduction(file,p,d);
      when '3' => Driver_for_Sparse_Linear_Reduction(file,p,d);
                  Driver_for_Nonlinear_Reduction(file,p,d);
      when others => null;
    end case;
    if Number_of_Unknowns(p(p'first)) < n
     then Driver_for_Overconstrained_Reduction(file,p);
    end if;
  end Driver_for_Reduction;

end Drivers_for_Reduction;
