File : taylors2.adb


with Ints, Ints.IO, Reps.IO, Messages;
use Ints, Ints.IO, Reps.IO, Messages;

pragma Elaborate (Ints);

package body Taylors2 is

  function InDomain(R: RadPair; F: Taylor2) return Boolean is
  begin
    return ((R(1) <= F.R(1)) and (R(2) <= F.R(2))) or else IsNumbers(F);
  end InDomain;

  procedure SetRho(R: in RadPair; F: in out Taylor2) is
  begin
    if not InDomain(R,F) then Error("Taylors2.SetRho domain error"); end if;
    F.R := R;
  end SetRho;

  function IsZero(F: Taylor2) return Boolean is
  begin
    return F.C(1,1)=ScalZero and then (F.C=Tay_Zero.C);
  end IsZero;

  function Zero_Taylor2 return Taylor2 is
    F: Taylor2;
  begin
    F.R := Some_RadPair;
    for I in Power loop
      for J in Power loop
        F.C(I,J) := ScalZero;
      end loop;
    end loop;
    return F;
  end Zero_Taylor2;

  procedure SetZero(R: in RadPair; F: out Taylor2) is
  begin
    F := Tay_Zero;
    F.R := R;
  end SetZero;

  procedure ZapConst(F: in out Taylor2) is
  begin
    ModNumbers(F.C(0,0));
  end ZapConst;

  procedure AddConst(C: in Scalar; F: in out Taylor2) is
  begin
    Add(C,F.C(0,0));
  end AddConst;

  procedure UnitVector(I,J: in Natural; R: in RadPair; F: out Taylor2) is
--- norm one
    S: constant Scalar := (Scal(R(1))**I)*(Scal(R(2))**J);
  begin
    SetZero(R,F);
    Add(Inv(S),F.C(I,J));
  end UnitVector;

  function QuasiDeg1(D: Integer; F: Taylor2) return Integer is
  begin
    for I in reverse 0 .. D loop
      for J in reverse 0 .. D-I loop
        if F.C(I,J) /= ScalZero then return I; end if;
      end loop;
    end loop;
    return -1;
  end QuasiDeg1;

  function QuasiDeg(F: Taylor2) return Integer is
  begin
    for D in reverse Power loop
      for I in reverse 0 .. D loop
        if F.C(I,D-I) /= ScalZero then return D; end if;
      end loop;
    end loop;
    return -1;
  end QuasiDeg;

  function IsNumbers(F: Taylor2) return Boolean is
  begin
    if not Trunc then
      for I in Power loop
        for J in 0 .. PDeg-I loop
          if not IsNumbers(F.C(I,J)) then return False; end if;
        end loop;
      end loop;
    end if;
    return True;
  end IsNumbers;

  function CenterDisk(F: Taylor2) return Taylor2 is
    G: Taylor2 := F;
  begin
    if not Trunc then
      for I in Power loop
        for J in 0 .. PDeg-I loop
          G.C(I,J) := CenterDisk(G.C(I,J));
        end loop;
      end loop;
    end if;
    return G;
  end CenterDisk;

  procedure ModNumbers(F: in out Taylor2) is
  begin
    if Trunc then
      F := Tay_Zero;
    else
      for I in Power loop
        for J in 0 .. PDeg-I loop
          ModNumbers(F.C(I,J));
        end loop;
      end loop;
    end if;
  end ModNumbers;

  procedure ModNumbers(F: in Taylor2; G: out Taylor2) is
  begin
    if Trunc then
      SetZero(F.R,G);
    else
      G := F;
      for I in Power loop
        for J in 0 .. PDeg-I loop
          ModNumbers(G.C(I,J));
        end loop;
      end loop;
    end if;
  end ModNumbers;

  function ModNumbers(F: Taylor2) return Taylor2 is
    G: Taylor2;
  begin
    ModNumbers(F,G);
    return G;
  end ModNumbers;

  function Norm(R: RadPair; F: Taylor2) return Scalar is
    S1,S2: Scalar;
  begin
    S1 := ScalZero;
    if not InDomain(R,F) then Error("Taylors2.Norm domain error"); end if;
    for I in reverse Power loop
      S2 := ScalZero;
      for J in reverse 0 .. PDeg-I loop
        S2 := R(2)*S2+Norm(F.C(I,J));
      end loop;
      S1 := R(1)*S1+S2;
    end loop;
    return Disk(S1);
  end Norm;

  function Norm(F: Taylor2) return Scalar is
  begin
    return Norm(F.R,F);
  end Norm;

  procedure ToBalls(F: in out Taylor2) is
  begin
    if Trunc then
      F.C := Tay_Zero.C;
    else
      for I in Power loop
        for J in 0 .. PDeg-I loop
          ToBalls(F.C(I,J));
        end loop;
      end loop;
    end if;
  end ToBalls;

  procedure Neg(F: in out Taylor2) is
  begin
    for I in Power loop
      for J in 0 .. PDeg-I loop
        Neg(F.C(I,J));
      end loop;
    end loop;
  end Neg;

  procedure Neg(F: in Taylor2; G: out Taylor2) is
  begin
    G := F;
    Neg(G);
  end Neg;

  function "-"(F: Taylor2) return Taylor2 is
    G: Taylor2 := F;
  begin
    Neg(G);
    return G;
  end "-";

  procedure Add(F: in Taylor2; G: in out Taylor2) is
  begin
    G.R := Min(F.R,G.R);
    for I in Power loop
      for J in 0 .. PDeg-I loop
        Add(F.C(I,J),G.C(I,J));
      end loop;
    end loop;
  end Add;

  procedure Sum(F,G: in Taylor2; H: out Taylor2) is
  begin
    H := F;
    Add(G,H);
  end Sum;

  function "+"(F,G: Taylor2) return Taylor2 is
    H: Taylor2 := F;
  begin
    Add(G,H);
    return H;
  end "+";

  procedure Sub(F: in Taylor2; G: in out Taylor2) is
  begin
    G.R := Min(F.R,G.R);
    for I in Power loop
      for J in 0 .. PDeg-I loop
        Sub(F.C(I,J),G.C(I,J));
      end loop;
    end loop;
  end Sub;

  procedure Diff(F,G: in Taylor2; H: out Taylor2) is
  begin
    H := F;
    Sub(G,H);
  end Diff;

  function "-"(F,G: Taylor2) return Taylor2 is
    H: Taylor2 := F;
  begin
    Sub(G,H);
    return H;
  end "-";

  procedure Mult(K: in Integer; F: in out Taylor2) is
  begin
    if K=0 then
      F.C := Tay_Zero.C;
    elsif K /= 1 then
      for I in Power loop
        for J in 0 .. PDeg-I loop
           Mult(K,F.C(I,J));
        end loop;
      end loop;
    end if;
  end Mult;

  procedure Prod(K: in Integer; F: in Taylor2; G: out Taylor2) is
  begin
    G := F;
    Mult(K,G);
  end Prod;

  function "*"(K: Integer; F: Taylor2) return Taylor2 is
    G: Taylor2 := F;
  begin
    Mult(K,G);
    return G;
  end "*";

  procedure MultAdd(K: in Integer; F: in Taylor2; G: in out Taylor2) is
  begin
    if K=1 then
      Add(F,G);
    elsif K=-1 then
      Sub(F,G);
    elsif K /= 0 then
      G.R := Min(F.R,G.R);
      for I in Power loop
        for J in 0 .. PDeg-I loop
          MultAdd(K,F.C(I,J),G.C(I,J));
        end loop;
      end loop;
    end if;
  end MultAdd;

  procedure Mult(S: in Scalar; F: in out Taylor2) is
  begin
    if S=ScalZero then
      F.C := Tay_Zero.C;
    else
      for I in Power loop
        for J in 0 .. PDeg-I loop
          Mult(S,F.C(I,J));
        end loop;
      end loop;
    end if;
  end Mult;

  procedure Prod(S: in Scalar; F: in Taylor2; G: out Taylor2) is
  begin
    G := F;
    Mult(S,G);
  end Prod;

  function "*"(S: Scalar; F: Taylor2) return Taylor2 is
    G: Taylor2 := F;
  begin
    Mult(S,G);
    return G;
  end "*";

  procedure MultAdd(S: in Scalar; F: in Taylor2; G: in out Taylor2) is
  begin
    if S /= ScalZero then
      G.R := Min(F.R,G.R);
      for I in Power loop
        for J in 0 .. PDeg-I loop
          MultAdd(S,F.C(I,J),G.C(I,J));
        end loop;
      end loop;
    end if;
  end MultAdd;

  procedure Mult(F: in Taylor2; G: in out Taylor2) is
    H: constant Taylor2 := G;
  begin
    G := Tay_Zero;
    MultAdd(F,H,G);
  end Mult;

  procedure Prod(F,G: in Taylor2; H: out Taylor2) is
  begin
    SetZero(F.R,H);
    MultAdd(F,G,H);
  end Prod;

  function "*"(F,G: Taylor2) return Taylor2 is
    H: Taylor2 := Tay_Zero;
  begin
    MultAdd(F,G,H);
    return H;
  end "*";

  procedure Collect(R1,R2: in Radius; DF: in Natural; F: in out RepMatrix; G: in out Components) is
--- collect higher order terms (not symmetric)
  begin
    if DF <= PDeg then return; end if;
    for D in reverse PDeg .. DF-1 loop
      UpMultAdd(R2,F(0,D+1),F(0,D));
      for I in 0 .. D loop
        UpMultAdd(R1,F(I+1,D-I),F(I,D-I));
      end loop;
    end loop;
    for I in Power loop
      Add(BallAt0(F(I,PDeg-I)),G(I,PDeg-I));
    end loop;
  end Collect;

  procedure MultAdd(DF,DG: in Natural; F,G: in Taylor2; H: in out Taylor2) is
--- only adds terms of order PDeg or less
    DF1:  constant Integer := Quasideg1(DF,F);
    DG1:  constant Integer := Quasideg1(DG,G);
    DFG:  constant Integer := IMin(DF+DG,PDeg);
    DFG1: constant Integer := IMin(DF1+DG1,PDeg);
    S: Scalar;
  begin
    for IH in reverse 0 .. DFG1 loop
      for JH in 0 .. DFG-IH loop
        S := ScalZero;
        for I in Imax(IH-DG1,0) .. IMin(IH,DF1) loop
          for J in Imax(JH-DG,0) .. IMin(JH,DF) loop
            MultAdd(F.C(I,J),G.C(IH-I,JH-J),S);
          end loop;
        end loop;
        Add(S,H.C(IH,JH));
      end loop;
    end loop;
  end MultAdd;

  procedure MultAdd(F,G: in Taylor2; H: in out Taylor2) is
    DF:   constant Integer := QuasiDeg(F);
    DG:   constant Integer := QuasiDeg(G);
    DFG:  constant Integer := DF+DG;
  begin
    if DF<0 or else DG<0 then return; end if;
    H.R := Min(Min(F.R,G.R),H.R);
    if Trunc or else DFG <= PDeg then          -- no higher order terms
      MultAdd(DF,DG,F,G,H);
      return;
    end if;
    declare
      DF1:  constant Integer := Quasideg1(DF,F);
      DG1:  constant Integer := Quasideg1(DG,G);
      DFG1: constant Integer := DF1+DG1;
      S: Scalar;
      Higher: RepMatrix := EZero;
    begin
      for IH in reverse 0 .. DFG1 loop
        for JH in 0 .. DFG-IH loop
          S := ScalZero;
          for I in Imax(IH-DG1,0) .. IMin(IH,DF1) loop
            for J in Imax(JH-DG,0) .. IMin(JH,DF) loop
              MultAdd(F.C(I,J),G.C(IH-I,JH-J),S);
            end loop;
          end loop;
          if IH+JH <= PDeg then
            Add(S,H.C(IH,JH));
          else
            Higher(IH,JH) := MaxNorm(S);
          end if;
        end loop;
      end loop;
      Collect(H.R(1),H.R(2),DFG,Higher,H.C);
    end;
  end MultAdd;

  procedure Scale(S1,S2: in Scalar; F: in out Taylor2) is
    D: constant Integer := QuasiDeg(F);
    T1,T2: Scalar;
  begin
    F.R(1) := Inf(Scal(F.R(1))/MaxNorm(S1));
    F.R(2) := Inf(Scal(F.R(2))/MaxNorm(S2));
    if D<1 then return; end if;
    T1 := ScalOne;
    for I in 0 .. D loop
      T2 := T1;
      for J in 0 .. D-I loop
        Mult(T2,F.C(I,J));
        Mult(S2,T2);
      end loop;
      Mult(S1,T1);
    end loop;
  end Scale;

  function Val(S1,S2: Scalar; F: Taylor2) return Scalar is
    R: constant RadPair :=  (MaxNorm(S1),MaxNorm(S2));
    S3,S4: Scalar;
  begin
    if not InDomain(R,F) then Error("Taylors2.Val domain error"); end if;
    S3 := ScalZero;
    for I in reverse Power loop
      S4 := ScalZero;
      for J in reverse 0 .. PDeg-I loop
        Mult(S2,S4);
        Add(F.C(I,J),S4);
      end loop;
      Mult(S1,S3);
      Add(S4,S3);
    end loop;
    return Disk(S3);
  end Val;

  procedure PolyDerU(F: in Taylor2; G: out Taylor2) is
  begin
    SetZero(F.R,G);
    for I in 1 .. PDeg loop
      for J in 0 .. PDeg-I loop
        Prod(I,F.C(I,J),G.C(I-1,J));
      end loop;
    end loop;
  end PolyDerU;

  procedure PolyDerV(F: in Taylor2; G: out Taylor2) is
  begin
    SetZero(F.R,G);
    for J in 1 .. PDeg loop
      for I in 0 .. PDeg-J loop
        Prod(J,F.C(I,J),G.C(I,J-1));
      end loop;
    end loop;
  end PolyDerV;

  function DerFac1(R1,R2: Radius) return Scalar is
--- norm factor for 1st derivative
--- R1: old domain, R2: new domain
    M: constant Scalar := Inv(Log(Scal(R1)/R2));
  begin
    if R1 <= R2 then
      Error("Taylors2.DerFac1 error");
    end if;
    if M<ScalOne then
      return ScalOne/R1;
    else
      return (M/R2)*Exp(-ScalOne);
    end if;
  end DerFac1;

  function DerFac2(R1,R2: Radius) return Scalar is
--- norm factor for 2nd derivative
--- R1: old domain, R2: new domain
    L: constant Scalar := Log(Scal(R1)/R2);
    M: constant Scalar := (L+ScalTwo+Sqrt(L*L+ScalFour))/(Two*L);
  begin
    if R1 <= R2 then
      Error("Taylors2.DerFac1 error");
    end if;
    if M<ScalTwo then
      return Two*Sqr(ScalOne/R1);
    else
      return (M/R2)*((M-ScalOne)/R2)*Exp(-(Two*M-ScalOne)/(M-ScalOne));
    end if;
  end DerFac2;

  procedure Ders(R: in RadPair; F0: in Taylor2; F: out Taylor2) is
  begin
    F := F0;
    SetRho(R,F);
  end Ders;

  procedure Ders(R: in RadPair; F0: in Taylor2; F,Fu,Fv: out Taylor2) is
  begin
    Ders(R,F0,F);
    PolyDerU(F,Fu);
    PolyDerV(F,Fv);
    if IsNumbers(F) then return; end if;
    declare
      U1: constant Scalar := DerFac1(F0.R(1),R(1));
      V1: constant Scalar := DerFac1(F0.R(2),R(2));
      G: Taylor2;
    begin
      ModNumbers(F,G);
      MultAdd(U1,G,Fu);
      MultAdd(V1,G,Fv);
    end;
  end Ders;

  procedure Ders(R: in RadPair; F0: in Taylor2; F,Fu,Fv,Fuu,Fuv,Fvv: out Taylor2) is
  begin
    Ders(R,F0,F);
    PolyDerU(F,Fu);
    PolyDerV(F,Fv);
    PolyDerU(Fu,Fuu);
    PolyDerV(Fu,Fuv);
    PolyDerV(Fv,Fvv);
    if IsNumbers(F) then return; end if;
    declare
      U1: constant Scalar := DerFac1(F0.R(1),R(1));
      V1: constant Scalar := DerFac1(F0.R(2),R(2));
      U2: constant Scalar := DerFac2(F0.R(1),R(1));
      V2: constant Scalar := DerFac2(F0.R(2),R(2));
      G: Taylor2;
    begin
      ModNumbers(Fu,G);
      MultAdd(V1,G,Fuv);
      MultAdd(Two*U1,G,Fuu);
      ModNumbers(Fv,G);
      MultAdd(U1,G,Fuv);
      MultAdd(Two*V1,G,Fvv);
      ModNumbers(F,G);
      MultAdd(U1,G,Fu);
      MultAdd(V1,G,Fv);
      MultAdd(U2,G,Fuu);
      MultAdd(V2,G,Fvv);
      MultAdd(U1*V1,G,Fuv);
    end;
  end Ders;

  procedure Ders(D: in Natural; R: in RadPair; F: in Taylor2; G: out Taylor2Mat) is
  begin
    if D=0 then
      Ders(R,F,G(0,0));
    elsif D=1 then
      Ders(R,F,G(0,0),G(1,0),G(0,1));
    elsif D=2 then
      Ders(R,F,G(0,0),G(1,0),G(0,1),G(2,0),G(1,1),G(0,2));
    else
      Error("Taylors2.Ders error: not implemented");
    end if;
  end Ders;

  procedure Show1(N: in String; H: in Taylor2) is
    W: constant Positive := 3;
    C: Scalar;
  begin
    if IsZero(H) then
      Put(N & "  Zero_Taylor2");
      New_Line;
    else
      Show2(N & " Rho",H.R(1),H.R(2));
      New_Line;
      for I in Power loop
        for J in 0 .. PDeg-I loop
          C := H.C(I,J);
          if C /= ScalZero then
            Show1(N & Strng(I,W) & Strng(J,W),C);
          end if;
        end loop;
      end loop;
    end if;
    New_Line;
  end Show1;

  procedure Show2(N: in String; H1,H2: in Taylor2) is
    W: constant Positive := 4;
    C1,C2: Scalar;
  begin
    if IsZero(H1) and IsZero(H2) then
      Put(N & "  Zero_Taylors2");
      New_Line;
    else
      Show2(N & " 1st Rho",H1.R(1),H2.R(1));
      Show2(N & " 2nd Rho",H1.R(2),H2.R(2));
      New_Line;
      for I in Power loop
        for J in 0 .. PDeg-I loop
          C1 := H1.C(I,J);
          C2 := H2.C(I,J);
          if not (IsZero(C1) and IsZero(C2)) then
            Show2(N & Strng(I,W) & Strng(J,W),C1,C2);
          end if;
        end loop;
      end loop;
    end if;
    New_Line;
  end Show2;

  procedure Get(F: in File_Type; H: out Taylor2; Decimal: in Boolean := False) is
    More: Boolean := False;
    D: Natural;
    C: Scalar;
  begin
    H := Tay_Zero;
    Get(F,H.R(1),Decimal);
    Get(F,H.R(2),Decimal);
    Get(F,D);
    D := D-1;
    declare
      Higher: RepMatrix := ZeroRepMat(D);
    begin
      for I in 0 .. D loop
        for J in 0 .. D-I loop
          Get(F,C,Decimal);
          if I+J <= PDeg then
            H.C(I,J) := C;
          else
            Higher(I,J) := MaxNorm(C);
            More := True;
          end if;
        end loop;
      end loop;
      if More and not Trunc then
        Collect(H.R(1),H.R(2),D,Higher,H.C);
      end if;
    end;
  end Get;

  procedure Put(F: in File_Type; H: in Taylor2; Decimal: in Boolean := False) is
    D: constant Integer := IMax(QuasiDeg(H),0);
  begin
    Put(F,H.R(1),Decimal);
    Put(F,H.R(2),Decimal);
    Put(F,D+1);
    for I in 0 .. D loop
      for J in 0 .. D-I loop
        Put(F,H.C(I,J),Decimal);
      end loop;
    end loop;
  end Put;

  procedure Read(Name: in String; H: out Taylor2; Decimal: in Boolean := False) is
    F: File_Type;
  begin
    Put_Line("Reading " & Name);
    Open(F,In_File,Name);
    Get(F,H,Decimal);
    Close(F);
  end Read;

  procedure Write(Name: in String; H: in Taylor2; Decimal: in Boolean := False) is
    F: File_Type;
  begin
    Put_Line("Writing " & Name);
    Create(F,Out_File,Name);
    Put(F,H,Decimal);
    Close(F);
  end Write;

begin

  Tay_Zero := Zero_Taylor2;

end Taylors2;