File : funs2.adb


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

package body Funs2 is

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

  procedure MultAdd(S: in Scalar; P: in Affine; F: in out Taylor) is
  begin
    MultAdd(S,P.A,F.C(1,0));
    MultAdd(S,P.B,F.C(0,1));
    MultAdd(S,P.C,F.C(0,0));
  end MultAdd;

  procedure Mult(P: in Affine; F: in out Taylor; Tmp: out Taylor) is
--- Tmp is scratch space
  begin
    Tmp := F;
    F := TayZero;
    MultAdd(P,Tmp,F);
  end Mult;

  procedure Mult(P: in Affine; F: in out Taylor) is
    Tmp: constant Taylor := F;
  begin
    F := TayZero;
    MultAdd(P,Tmp,F);
  end Mult;

  procedure MultAdd(P: in Affine; F: in Taylor; G: in out Taylor) is
    DF: constant Integer := QuasiDeg(F);
    D1: constant Integer := IMin(DF+1,PDeg);
    E: Scalar;
  begin
    if DF<0 then return; end if;
    MultAdd(P.C,F,G);
    if P.A /= ScalZero then
      for I in 1 .. D1 loop
        for J in 0 .. D1-I loop
          MultAdd(P.A,F.C(I-1,J),G.C(I,J));
        end loop;
      end loop;
    elsif P.B=ScalZero then return; end if;
    if P.B /= ScalZero then
      for J in 1 .. D1 loop
        for I in 0 .. D1-J loop
          MultAdd(P.B,F.C(I,J-1),G.C(I,J));
        end loop;
      end loop;
    end if;
    G.R := Min(F.R,G.R);
    if Trunc or else DF<PDeg then return; end if;
    E := P.A*BallAt0(F.R(1))+P.B*BallAt0(F.R(2));
    for I in Power loop
      MultAdd(E,F.C(I,PDeg-I),G.C(I,PDeg-I));
    end loop;
  end MultAdd;

  procedure Comp(U,V: in Affine; F: in Taylor; G: out Taylor) is
    D: constant Integer := QuasiDeg(F);
  begin
    G := TayZero;
    if D<0 then return; end if;
    declare
      D1: constant Integer := QuasiDeg1(D,F);
      R:  constant RadPair := Dom(U,V,F.R);
      D2: Integer;
      H,Tmp: Taylor2;
    begin
      for I in reverse 0 .. D1 loop
        SetZero(R,H);
        D2 := QuasiDeg2(D,I,F.C);
        if D2 >=0 then
          H.C(0,0) := F.C(I,D2);
          for J in reverse 0 .. D2-1 loop
            Mult(V,H,Tmp);
            Add(F.C(I,J),H.C(0,0));
          end loop;
        end if;
        Mult(U,G,Tmp);
        Add(H,G);
      end loop;
    end;
  end Comp;

  procedure FillZero(D: in Natural; G: out TaylorMat) is
  begin
    for N in 0 .. D loop
      for I in 0 .. N loop
        G(I,N-I) := TayZero;
      end loop;
    end loop;
  end FillZero;

-------------------------------

  procedure CheckArgs(F: in Fun; G: in out Fun; OK: out Boolean) is
  begin
    if F.A=G.A or else IsZero(F) then
      OK := True;
    else
      G.A := F.A;
      OK := IsZero(G);
    end if;
  end CheckArgs;

------------------------------

  function Rho1(F: Fun) return Radius is
  begin
    return RMin(F.P.R(1),F.Q.R(1));
  end Rho1;

  function Rho2(F: Fun) return Radius is
  begin
    return RMin(F.P.R(2),F.Q.R(2));
  end Rho2;

  function Rho(F: Fun) return RadPair is
  begin
    return Min(F.P.R,F.Q.R);
  end Rho;

  procedure SetRho(R: in RadPair; F: in out Fun) is
  begin
    SetRho(R,F.P);
    SetRho(R,F.Q);
  end SetRho;

  function Dom(F: Fun) return Domain is
    D: Domain;
  begin
    D.A := F.A;
    D.R := Rho(F);
    return D;
  end Dom;

  function IsZero(F: Fun) return Boolean is
  begin
    return IsZero(F.P) and then IsZero(F.Q);
  end IsZero;

  procedure FillZero(F: in out Fun) is
--- set F.A elsewhere
  begin
    F.E := True;
    F.P := TayZero;
    F.Q := TayZero;
  end FillZero;

  procedure SetZero(A: in Args; F: out Fun) is
  begin
    F.A := A;
    FillZero(F);
  end SetZero;

  procedure SetZero(Template: in Fun; F: out Fun) is
  begin
    F.A := Template.A;
    FillZero(F);
  end SetZero;

  procedure ToBalls(F: in out Fun) is
  begin
    ToBalls(F.P);
    ToBalls(F.Q);
  end ToBalls;

  procedure Symm(F: in out Fun) is
--- extract symmetric part
    R: constant RadPair := Rho(F);
  begin
    if not (F.E or else IsZero(F.Q)) then
      SetRho(R,F);
      MultAdd(TNorm(F.A,R),ModNumbers(F.Q),F.P);
    end if;
    F.Q := TayZero;
    F.E := True;
  end Symm;

  function Symm(F: Fun) return Fun is
    G: Fun := F;
  begin
    Symm(G);
    return G;
  end Symm;

  procedure AddConst(C: in Scalar; F: in out Fun) is
  begin
    AddConst(C,F.P);
  end AddConst;

  procedure AddBall(C: in Scalar; F: in out Fun) is
  begin
    if not Trunc then
      AddConst(BallAt0(C),F.P);
      F.E := F.E and then IsZero(F.Q);
    end if;
  end AddBall;

  procedure AddT1(C: in Scalar; F: in out Fun) is
  begin
    AddConst(C,F.Q);
  end AddT1;

  procedure AddS1(C: in Scalar; F: in out Fun) is
  begin
    MultAdd(C,CoordS1(F.A),F.P);
  end AddS1;

  procedure AddX(C: in Scalar; F: in out Fun) is
  begin
    AddT1(Half*C,F);
    AddS1(Half*C,F);
  end AddX;

  procedure AddY(C: in Scalar; F: in out Fun) is
  begin
    AddT1(Half*C,F);
    AddS1((-Half)*C,F);
  end AddY;

  function IsNumbers(F: Fun) return Boolean is
  begin
    return IsNumbers(F.P) and then IsNumbers(F.Q);
  end IsNumbers;

  function ModNumbers(F: Fun) return Fun is
    G: Fun := F;
  begin
    ModNumbers(G.P);
    ModNumbers(G.Q);
    return G;
  end ModNumbers;

  function Norm(R: RadPair; F: Fun) return Scalar is
  begin
    return Norm(R,F.P)+TNorm(F.A,R)*Norm(R,F.Q);
  end Norm;

  function Norm(F: Fun) return Scalar is
  begin
    return Norm(Rho(F),F);
  end Norm;

  procedure Neg(F: in out Fun) is
  begin
    Neg(F.P);
    Neg(F.Q);
  end Neg;

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

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

  procedure Add(F: in Fun; G: in out Fun) is
    OK: Boolean;
  begin
    CheckArgs(F,G,OK);
    if not OK then Error("Funs2.Add error"); end if;
    if G.E then G.E := F.E; end if;
    Add(F.P,G.P);
    Add(F.Q,G.Q);
  end Add;

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

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

  procedure Sub(F: in Fun; G: in out Fun) is
    OK: Boolean;
  begin
    CheckArgs(F,G,OK);
    if not OK then Error("Funs2.Sub error"); end if;
    if G.E then G.E := F.E; end if;
    Sub(F.P,G.P);
    Sub(F.Q,G.Q);
  end Sub;

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

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

  procedure Mult(S: in Scalar; F: in out Fun) is
  begin
    Mult(S,F.P);
    Mult(S,F.Q);
    if S=ScalZero then F.E := True; end if;
  end Mult;

  procedure Prod(S: in Scalar; F: in Fun; G: out Fun) is
  begin
    if S=ScalZero then
      SetZero(F.A,G);
    else
      G := F;
      Mult(S,G);
    end if;
  end Prod;

  function "*"(S: Scalar; F: Fun) return Fun is
    G: Fun;
  begin
    Prod(S,F,G);
    return G;
  end "*";

  procedure MultAdd(S: in Scalar; F: in Fun; G: in out Fun) is
    OK: Boolean;
  begin
    if S /= ScalZero then
      CheckArgs(F,G,OK);
      if not OK then Error("Funs2.MultAdd error"); end if;
      MultAdd(S,F.P,G.P);
      MultAdd(S,F.Q,G.Q);
    end if;
  end MultAdd;

  procedure Mult(F: in Fun; G: in out Fun; Tmp: out Fun) is
--- Tmp is scratch space
  begin
    Tmp := G;
    FillZero(G);
    MultAdd(F,Tmp,G);
  end Mult;

  procedure Mult(F: in Fun; G: in out Fun) is
    Tmp: Fun;
  begin
    Mult(F,G,Tmp);
  end Mult;

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

  function "*"(F,G: Fun) return Fun is
    H: Fun;
  begin
    SetZero(F.A,H);
    MultAdd(F,G,H);
    return H;
  end "*";

  function Sqr(F: Fun) return Fun is
    H: Fun;
  begin
    SetZero(F.A,H);
    MultAdd(F,F,H);
    return H;
  end Sqr;

  procedure MultAdd(F,G: in Fun; H: in out Fun) is
    OK: Boolean;
  begin
    MultAdd(F.P,G.P,H.P);
    MultAdd(F.Q,G.P,H.Q);
    MultAdd(F.P,G.Q,H.Q);
    MultAdd(CoordT2(F.A),F.Q*G.Q,H.P);
    if H.E then H.E := (F.E and G.E); end if;
    CheckArgs(F,H,OK);
    if not OK then Error("Funs2.MultAdd error F"); end if;
    CheckArgs(G,H,OK);
    if not OK then Error("Funs2.MultAdd error G"); end if;
  end MultAdd;

  procedure ChangeVar(A: in Args; F: in out Fun) is
    G: constant Fun := F;
    B: Mat3;
  begin
    if F.A=A then return; end if;
    if IsZero(F) then F.A := A; return; end if;
    if not F.E then
      Error("Funs2.ChangeVar error: not implemented");
    end if;
    Coord(F.A,A,B);
    F.A := A;
    Comp(B.U,B.V,G.P,F.P);
    Comp(B.U,B.V,G.Q,F.Q);
  end ChangeVar;

  procedure Scale(S: in Scalar; A: in Args; F: in out Fun) is
    G: constant Fun := F;
    B: Mat3;
  begin
    if not F.E then
      Error("Funs2.Scale error: not implemented");
    end if;
    Scale(S,F.A,A,B);
    F.A := A;
    Comp(B.U,B.V,G.P,F.P);
    Comp(B.U,B.V,G.Q,F.Q);
  end Scale;

  procedure Scale(S: in Scalar; F: in out Fun) is
    A: Args;
    B: Mat3;
  begin
    Convert(F.A,B);
    Scale(S,B);
    Approx(B,A);
    Scale(S,A,F);
  end Scale;

  procedure DerTS(FA: in Args; DP: in TaylorMat; F1,F2: in out Fun) is
--- not using/setting Args or setting/checking E of F1,F2
  begin
--- P1 := Q+2*(T^2)*[DQ(1,0)+A*DQ(0,1)]]
    F1.P := TayZero;
--- Q1 := 2*[DP(1,0)+A*DP(0,1)]
    F1.Q := DP(1,0);
    MultAdd(Scal(FA.A),DP(0,1),F1.Q);
    Mult(ScalTwo,F1.Q);
--- P2 := B*DP(1,0)+DP(0,1)
    F2.P := DP(0,1);
    MultAdd(Scal(FA.B),DP(1,0),F2.P);
--- Q2 := B*DQ(1,0)+DQ(0,1)
    F2.Q := TayZero;
  end DerTS;

  procedure DerTS(FA: in Args; DP: in TaylorMat; F11,F12,F22: out Fun) is
--- not using/setting Args or setting/checking E of F11,F12,F22
  begin
--- P11 := 2*[DP(1,0)+A*DP(0,1)]+4*(T^2)[DP(2,0)+2*A*DP(1,1)+A*A*DP(0,2)]
    Prod(ScalTwo,DP(1,1),F11.P);
    MultAdd(Scal(FA.A),DP(0,2),F11.P);
    Mult(Scal(FA.A),F11.P);
    Add(DP(2,0),F11.P);
    Mult(CoordT2(FA),F11.P);
    Mult(ScalFour,F11.P);
    MultAdd(ScalTwo,DP(1,0),F11.P);
    MultAdd(FA.A*ScalTwo,DP(0,1),F11.P);
--- Q11 := 6*[DQ(1,0)+A*DQ(0,1)]+4*(T^2)[DQ(2,0)+2*A*DQ(1,1)+A*A*DQ(0,2)]
    F11.Q := TayZero;
--- P12 := [B*DQ(1,0)+DQ(0,1)]+2*(T^2)*[B*DQ(2,0)+(1+A*B)*DQ(1,1)+A*DQ(0,2)]
    F12.P := TayZero;
--- Q12 := 2*[B*DP(2,0)+(1+A*B)*DP(1,1)+A*DP(0,2)]
    Prod(ScalOne+FA.A*Scal(FA.B),DP(1,1),F12.Q);
    MultAdd(Scal(FA.A),DP(0,2),F12.Q);
    MultAdd(Scal(FA.B),DP(2,0),F12.Q);
    Mult(ScalTwo,F12.Q);
--- P22 := B*B*DP(2,0)+2*B*DP(1,1)+DP(0,2)
    F22.P := DP(0,2);
    MultAdd(FA.B*ScalTwo,DP(1,1),F22.P);
    MultAdd(FA.B*Scal(FA.B),DP(2,0),F22.P);
--- Q22 := B*B*DQ(2,0)+2*B*DQ(1,1)+DQ(0,2)
    F22.Q := TayZero;
  end DerTS;

  DP: TaylorMat(0..2,0..2);           -- used by derivative procedures below

  procedure MDer(R: in RadPair; F: in Fun; G: out Fun) is
--- generator of scaling
--- uses global DP, as storage only
    H: Taylor;
  begin
    if not F.E then
      Error("Funs2.MDer error: not implemented");
    end if;
    G := F;
    Ders(1,R,F.P,DP);
--- P2 := S*[B*DP(1,0)+DP(0,1)]
    G.P := DP(0,1);
    MultAdd(Scal(F.A.B),DP(1,0),G.P);
    Mult(CoordS1(F.A),G.P);
--- P1 := 2*(T^2)*[DP(1,0)+A*DP(0,1)]
    H := DP(1,0);
    MultAdd(Scal(F.A.A),DP(0,1),H);
    Mult(CoordT2(F.A),H);
    MultAdd(ScalTwo,H,G.P);
  end MDer;

  procedure DerTS(R: in RadPair; F: in Fun; F1,F2: out Fun) is
--- uses global DP, as storage only
  begin
    if not F.E then
      Error("Funs2.DerTS error: not implemented");
    end if;
    Ders(1,R,F.P,DP);
    DerTS(F.A,DP,F1,F2);
    F1.A := F.A;
    F1.E := True;
    F2.A := F.A;
    F2.E := True;
  end DerTS;

  procedure Der(R: in RadPair; F: in Fun; F1,F2: out Fun) is
    FT,FS: Fun;
  begin
    DerTS(R,F,FT,FS);
    Sum(FT,FS,F1);
    Diff(FT,FS,F2);
  end Der;

  procedure DerTS(R: in RadPair; F: in Fun; F1,F2,F11,F12,F22: out Fun) is
--- uses global DP, as storage only
  begin
    if not F.E then
      Error("Funs2.DerTS error: not implemented");
    end if;
    Ders(2,R,F.P,DP);
    DerTS(F.A,DP,F1,F2);
    DerTS(F.A,DP,F11,F12,F22);
    F1.A := F.A;
    F1.E := True;
    F2.A := F.A;
    F2.E := True;
    F11.A := F.A;
    F11.E := True;
    F12.A := F.A;
    F12.E := True;
    F22.A := F.A;
    F22.E := True;
  end DerTS;

  procedure Der(R: in RadPair; F: in Fun; F1,F2,F11,F12,F22: out Fun) is
    FT,FS,FTT,FTS,FSS: Fun;
  begin
    DerTS(R,F,FT,FS,FTT,FTS,FSS);
    Sum(FT,FS,F1);
    Diff(FT,FS,F2);
    Sum(FTT,FSS,F11);
    F22 := F11;
    MultAdd(ScalTwo,FTS,F11);
    MultAdd(-ScalTwo,FTS,F22);
    Diff(FTT,FSS,F12);
  end Der;

  function ValTS(T,S: Scalar; F: Fun) return Scalar is
    U,V: Scalar;
  begin
    ValTS(T,S,F.A,U,V);
    return Val(U,V,F.P)+T*Val(U,V,F.Q);
  end ValTS;

  function Val(X,Y: Scalar; F: Fun) return Scalar is
  begin
    return ValTS(X+Y,X-Y,F);
  end Val;

  DF1,DF2,DF11,DF12,DF22: Fun;   --- used by ValDerTS

  procedure ValDerTS(T,S: in Scalar; F: in Fun; D1,D2: out Scalar) is
--- uses globals DF1,DF2
    R: RadPair;
    U,V: Scalar;
  begin
    ValTS(T,S,F.A,U,V);
    R := (MaxNorm(U),MaxNorm(V));
    DerTS(R,F,DF1,DF2);
    D1 := ValTS(T,S,DF1);
    D2 := ValTS(T,S,DF2);
  end ValDerTS;

  procedure ValDer(X,Y: in Scalar; F: in Fun; D1,D2: out Scalar) is
    DT,DS: Scalar;
  begin
    ValDerTS(X+Y,X-Y,F,DT,DS);
    D1 := DT+DS;
    D2 := DT-DS;
  end ValDer;

  procedure ValDerTS(T,S: in Scalar; F: in Fun; D1,D2,D11,D12,D22: out Scalar) is
--- uses globals DF1,DF2,DF11,DF12,DF22
    R: RadPair;
    U,V: Scalar;
  begin
    ValTS(T,S,F.A,U,V);
    R := (MaxNorm(U),MaxNorm(V));
    DerTS(R,F,DF1,DF2,DF11,DF12,DF22);
    D1 := ValTS(T,S,DF1);
    D2 := ValTS(T,S,DF2);
    D11 := ValTS(T,S,DF11);
    D12 := ValTS(T,S,DF12);
    D22 := ValTS(T,S,DF22);
  end ValDerTS;

  procedure ValDer(X,Y: in Scalar; F: in Fun; D1,D2,D11,D12,D22: out Scalar) is
    DT,DS,DTT,DTS,DSS: Scalar;
  begin
    ValDerTS(X+Y,X-Y,F,DT,DS,DTT,DTS,DSS);
    D1 := DT+DS;
    D2 := DT-DS;
    D11 := DTT+DSS+Two*DTS;
    D22 := DTT+DSS-Two*DTS;
    D12 := DTT-DSS;
  end ValDer;

  procedure SplitParity(F: in Fun; F0,F1: out Fun) is
  begin
    F0 := F;
    F1 := F;
    if F.E then        -- only applies when Trunc
      F0.Q := TayZero;
      F1.P := TayZero;
    else
      ModNumbers(F0.Q);
      ModNumbers(F1.P);
    end if;
  end SplitParity;

  procedure Reflect(F: in out Fun) is
  begin
    Neg(F.Q);
  end Reflect;

  function Reflect(F: Fun) return Fun is
    G: Fun := F;
  begin
    Reflect(G);
    return G;
  end Reflect;

  function "*"(L: Flop; F: Fun) return Fun is
--- L(1)*F+L(2)*Reflect(F)
    G: Fun := F;
  begin
    Reflect(G);
    Mult(L(2),G);
    MultAdd(L(1),F,G);
    return G;
  end "*";

  function "*"(L1,L2: Flop) return Flop is
    L: Flop;
  begin
    L(1) := L1(1)*L2(1)+L1(2)*Reflect(L2(2));
    L(2) := L1(1)*L2(2)+L1(2)*Reflect(L2(1));
    return L;
  end "*";

  function "-"(L1,L2: Flop) return Flop is
    L: Flop;
  begin
    Diff(L1(1),L2(1),L(1));
    Diff(L1(2),L2(2),L(2));
    return L;
  end "-";

  function Norm(R: RadPair; L: Flop) return Scalar is
  begin
    return Norm(R,L(1))+Norm(R,L(2));
  end Norm;

  function CompTSRadii(FT,FS: Fun; A: Args) return RadPair is
    FU,FV: Fun;
  begin
    CompTS(FT,FS,A,FU,FV);
    return (Sup(Norm(FU)),Sup(Norm(FV)));
  end CompTSRadii;

  function CompVWRadii(V: Fun; A: Args) return RadPair is
--- domain radii needed to compose with (V,W)
    V0,V1: Fun;
  begin
    SplitParity(ScalTwo*V,V0,V1);
    return CompTSRadii(V1,V0,A);
  end CompVWRadii;

  function CompRadii(FX,FY: Fun; A: Args) return RadPair is
    FU,FV: Fun;
  begin
    Comp(FX,FY,A,FU,FV);
    return (Sup(Norm(FU)),Sup(Norm(FV)));
  end CompRadii;

  function CompXRadii(FX: Fun; A: Args) return RadPair is
    FY: Fun;
  begin
    SetZero(FX.A,FY);
    AddY(ScalOne,FY);
    return CompRadii(FX,FY,A);
  end CompXRadii;

  function CompYRadii(FY: Fun; A: Args) return RadPair is
    FX: Fun;
  begin
    SetZero(FY.A,FX);
    AddX(ScalOne,FX);
    return CompRadii(FX,FY,A);
  end CompYRadii;

  procedure CompTS(FT,FS: in Fun; A: in Args; FU,FV: out Fun) is
    TT0: constant Scalar := A.T0*Scal(A.T0);
  begin
    Prod(FT,FT,FU);
    FV := FS;
    MultAdd(Scal(A.A),FU,FV);
    MultAdd(Scal(A.B),FS,FU);
    AddConst(-(TT0+A.B*Scal(A.S0)),FU.P);
    AddConst(-(A.A*TT0+Scal(A.S0)),FV.P);
  end CompTS;

  procedure Comp(FX,FY: in Fun; A: in Args; FU,FV: out Fun) is
    FT,FS: Fun;
  begin
    Sum(FX,FY,FT);
    Diff(FX,FY,FS);
    CompTS(FT,FS,A,FU,FV);
  end Comp;

  procedure Eval(V1,V2: in Fun; F: in Taylor; V3: out Fun) is
    D:  constant Integer := QuasiDeg(F);
    RV: constant RadPair := Min(Rho(V1),Rho(V2));
    RF: constant RadPair := (Sup(Norm(RV,V1)),Sup(Norm(RV,V2)));
    V4,Tmp: Fun;
  begin
    SetZero(V1,V3);
    if D<0 then return; end if;
    if not InDomain(RF,F) then
      Error("Taylors2.Eval domain error");
    end if;
    if V1.A /= V2.A then
      Error("Funs2.Eval error: arguments differ");
    end if;
    for I in reverse 0 .. QuasiDeg1(D,F) loop
      SetZero(V2,V4);
      for J in reverse 0 .. QuasiDeg2(D,I,F.C) loop
        Mult(V2,V4,Tmp);
        AddConst(F.C(I,J),V4);
      end loop;
      Mult(V1,V3,Tmp);
      Add(V4,V3);
    end loop;
    SetRho(RV,V3);
    if V3.E then V3.E := IsNumbers(F); end if;
  end Eval;

  procedure CompTS(FT,FS,G: in Fun; H: out Fun) is
    FU,FV,HQ: Fun;
  begin
    CompTS(FT,FS,G.A,FU,FV);
    Eval(FU,FV,G.P,H);
    Eval(FU,FV,G.Q,HQ);
    MultAdd(FT,HQ,H);
    if (not G.E) and then Norm(Rho(H),FT)>TNorm(G.A,Rho(G)) then
      Error("Funs2.CompTS error: FT too large",T3);
    end if;
  end CompTS;

  procedure Comp(FX,FY,G: in Fun; H: out Fun) is
    FT,FS: Fun;
  begin
    Sum(FX,FY,FT);
    Diff(FX,FY,FS);
    CompTS(FT,FS,G,H);
  end Comp;

  procedure CompX(FX,G: in Fun; H: out Fun) is
    FY: Fun;
  begin
    SetZero(FX,FY);
    AddY(ScalOne,FY);
    Comp(FX,FY,G,H);
  end CompX;

  function CompX(FX,G: Fun) return Fun is
    H: Fun;
  begin
    CompX(FX,G,H);
    return H;
  end CompX;

  procedure CompY(FY,G: in Fun; H: out Fun) is
    FX: Fun;
  begin
    SetZero(FY,FX);
    AddX(ScalOne,FX);
    Comp(FX,FY,G,H);
  end CompY;

  function CompY(FY,G: Fun) return Fun is
    H: Fun;
  begin
    CompY(FY,G,H);
    return H;
  end CompY;

  function CompVW(V,F: Fun) return Fun is
--- compose F with (V,W) where W(x,y)=-V(-y,-x)
    H,V0,V1: Fun;
  begin
    Prod(ScalTwo,V,H);
    SplitParity(H,V0,V1);
    CompTS(V1,V0,F,H);
    return H;
  end CompVW;

  function FF(Fx,Gy,V: Fun) return Fun is
--- V -> Gy(x,V)+Fx(V,W), W=-SV
  begin
    return CompY(V,Gy)+CompVW(V,Fx);
  end FF;

  function DFF(Fxx,Fxy,Gyy,V: Fun) return Flop is
    L: Flop;
  begin
    L(1) := CompY(V,Gyy)+CompVW(V,Fxx);
    L(2) := -CompVW(V,Fxy);
    return L;
  end DFF;

  procedure Show1(N: in String; H: in Fun) is
  begin
    Show1(N,H.A);
    New_Line;
    Show1(N & " type",Int(not H.E));
    New_Line;
    Show1(N & " even",H.P);
    New_Line;
    Show1(N & "  odd",H.Q);
    New_Line;
    New_Line;
  end Show1;

  procedure Show2(N: in String; H1,H2: in Fun) is
  begin
    Show2(N,H1.A,H2.A);
    New_Line;
    Show2(N & " types",Int(not H1.E),Int(not H2.E));
    New_Line;
    Show2(N & "  even",H1.P,H2.P);
    New_Line;
    Show2(N & "   odd",H1.Q,H2.Q);
    New_Line;
    New_Line;
  end Show2;

  procedure Get(F: in File_Type; H: out Fun; Decimal: in Boolean := False) is
  begin
    Get(F,H.A.T0,Decimal);
    Get(F,H.A.S0,Decimal);
    Get(F,H.A.B,Decimal);
    Get(F,H.A.A,Decimal);
    Get(F,H.P,Decimal);
    Get(F,H.Q,Decimal);
    H.E := IsNumbers(H) or else IsZero(H.Q);
  end Get;

  procedure Put(F: in File_Type; H: in Fun; Decimal: in Boolean := False) is
--- H.E is not saved
  begin
    Put(F,H.A.T0,Decimal);
    Put(F,H.A.S0,Decimal);
    Put(F,H.A.B,Decimal);
    Put(F,H.A.A,Decimal);
    Put(F,H.P,Decimal);
    Put(F,H.Q,Decimal);
    if H.E /= (IsNumbers(H) or else IsZero(H.Q)) then
      Error("Funs2.Put error: not implemented");
    end if;
  end Put;

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

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

------------------------------------ some implicit equations

  procedure RVal(G: in Fun; X,C: in Scalar; S: in out Scalar) is
--- solves G(x,s)=c
    R0: constant Scalar := -ScalTwo;  -- guess
    R1: constant Scalar :=  ScalTwo;  -- guess
    GX,GY: Scalar;

    function H(Y: Scalar) return Scalar is
    begin
      return Val(X,Y,G)-C;
    end H;

    function DH(Y: Scalar) return Scalar is
    begin
      ValDer(X,Y,G,GX,GY);
      return GY;
    end DH;

    function Find is new FindZero(F => H, DF => DH);

  begin
    S := Find(S,R0,R1,Eps1,Eps2);
  end RVal;

  procedure LVal(F: in Fun; Y,C: in Scalar; S: in out Scalar) is
--- solves F(s,y)=c
    R0: constant Scalar := -ScalOne;  -- guess
    R1: constant Scalar :=  ScalOne;  -- guess
    FX,FY: Scalar;

    function H(X: Scalar) return Scalar is
    begin
      return Val(X,Y,F)-C;
    end H;

    function DH(X: Scalar) return Scalar is
    begin
      ValDer(X,Y,F,FX,FY);
      return FX;
    end DH;

    function Find is new FindZero(F => H, DF => DH);

  begin
     S := Find(S,R0,R1,Eps1,Eps2);
  end LVal;

  procedure ZVal(F,G: in Fun; X,Y: Scalar; S: in out Scalar) is
--- solve F(x,s)+G(s,y)=0
    R0: constant Scalar := -ScalOne;  -- guess
    R1: constant Scalar :=  ScalOne;  -- guess
    FX,FY,GX,GY: Scalar;

    function H(Z: Scalar) return Scalar is
    begin
      return Val(X,Z,F)+Val(Z,Y,G);
    end H;

    function DH(Z: Scalar) return Scalar is
    begin
      ValDer(X,Z,F,FX,FY);
      ValDer(Z,Y,G,GX,GY);
      return FY+GX;
    end DH;

    function Find is new FindZero(F => H, DF => DH);

  begin
    S := Find(S,R0,R1,Eps1,Eps2);
  end ZVal;

  procedure VVal(F,G: in Fun; X: Scalar; S: in out Scalar) is
--- solve G(x,s)+F(s,-s)=0
    R0: constant Scalar := -ScalOne;  -- guess
    R1: constant Scalar :=  ScalOne;  -- guess
    GX,GY,FX,FY: Scalar;

    function H(V: Scalar) return Scalar is
    begin
      return Val(X,V,G)+Val(V,-V,F);
    end H;

    function DH(V: Scalar) return Scalar is
    begin
      ValDer(X,V,G,GX,GY);
      ValDer(V,-V,F,FX,FY);
      return GY+FX-FY;
    end DH;

    function Find is new FindZero(F => H, DF => DH);

  begin
     S := Find(S,R0,R1,Eps1,Eps2);
  end VVal;

  procedure YVal(G: in Fun; S: in out Scalar) is
--- estimate solution of Gx(s^2,s)=[Gy(s^2,s)]^2
    R0: constant Scalar := Scal(-1454)/Rep(2048);  -- guess
    R1: constant Scalar := Scal(-1441)/Rep(2048);  -- guess
    GX,GY,GXX,GXY,GYY: Scalar;

    function H(Y: Scalar) return Scalar is
    begin
      ValDer(Y*Y,Y,G,GX,GY);
      return GX-Sqr(GY);
    end H;

    function DH(Y: Scalar) return Scalar is
    begin
      ValDer(Y*Y,Y,G,GX,GY,GXX,GXY,GYY);
      return Two*Y*GXX+GXY-Two*GY*(Two*Y*GXY+GYY);
    end DH;

    function Find is new FindZero(F => H, DF => DH);

  begin
    S := Find(S,R0,R1,Eps1,Eps2);
  end YVal;

end Funs2;