File : fouriers-ops.adb


with Messages, Reps.Ops;
use Messages;

pragma Elaborate_All (Messages, Reps.Ops);

package body Fouriers.Ops is

  use Components_Ops;

  Small:    constant Rep    := Half**10;
  ScalTwo:  constant Scalar := Scal(Two);
  ScalHalf: constant Scalar := Scal(Half);
  OnePlus:  constant Scalar := Widen(ScalOne);                     --- used in norms
  HalfPlus: constant Scalar := Widen(ScalHalf);                    --- used in norms

  function IMin(I1,I2: Integer) return Integer renames Integer'Min;
  function IMax(I1,I2: Integer) return Integer renames Integer'Max;
  function Deg(P: Taylor) return Degree renames Components.Deg;    --- want to inline it

  pragma Inline (Imin,Imax,Deg);

  function DmaxCount return Integer is
    D: Integer := 0;
  begin
    for M in Freq1 loop
      for N in Nmin(M) .. Nmax(M) loop
        D := D+Dmax(M,N)+1;
      end loop;
    end loop;
    return D;
  end DmaxCount;

  function ErrCount return Integer is
    C: Integer;
  begin
    C := (Pdeg+1)*NumErrs(Lmax,Wmax);
    for M in Freq1 loop
      for N in Nmin(M) .. Nmax(M) loop
        if Dmax(M,N) >= 0 then C := C+1; end if;
      end loop;
    end loop;
    return C;
  end ErrCount;

  function InRange(M,N: Integer; K: Natural := 0; D: Degrees := Dmax) return Boolean is
  begin
    return M in Freq1 and then N in Freq2 and then K<=D(M,N);
  end InRange;

  procedure Component(K: Natural; H1: in Fourier; H2: out Fourier) is
    E,S: Scalar;
  begin
    H2.P := H1.P;
    H2.J := H1.J;
    H2.R := H1.R;
    SetZero(H2.C(0,0));
    S := ScalZero;
    for M in Freq1 loop
      for N in Nmin(M,H1.J) .. Nmax(M) loop
        ComponentSplit(K,H1.C(M,N),H2.C(M,N),E);
        if not H1.P then
          S := S+E*NormFactor(H1.R.Q,M,N,True);
        end if;
      end loop;
    end loop;

    if Trunc then return; end if;
    if H1.P then
      ResetErrComp(H2);
      return;
    end if;

    for I in FreqE loop
      ComponentSplit(K,H1.E(I),H2.E(I),E);
      S := S+E;
    end loop;
    AddErrComp(Abs(S),H2.E(0));
  end Component;

  procedure SetComponent(M: in Freq1; N: in Freq2; K: in Natural; S: in Scalar; H: in out Fourier) is
--- allowing K's larger than Dmax(M,N)
  begin
    if InRange(M,N) and (H.J=0 or else M /= 0 or else N /= 0 or else S=ScalZero) then
      SetComponent(K,S,H.C(M,N));
      if K<Dho or else Err0(S) then return; end if;
      H.P := False;  -- S may depend on p
    else
      Message("Fouriers.Ops.SetComponent",Index_Error);
    end if;
  end SetComponent;

  procedure AddComponent(M: in Freq1; N: in Freq2; K: in Natural; S: in Scalar; H: in out Fourier) is
  begin
    if InRange(M,N) and (H.J=0 or else M /= 0 or else N /= 0 or else S=ScalZero) then
      AddComponent(K,S,H.C(M,N));
      if K<Dho or else Err0(S) then return; end if;
      H.P := False;  -- S may depend on p
    else
      Message("Fouriers.Ops.AddComponent",Index_Error);
    end if;
  end AddComponent;

  procedure SetErrComp(I: in FreqE; K: in Natural; S: in Scalar; H: in out Fourier) is
  begin
    if Trunc then return; end if;
    if K <= Pdeg then
      SetComponent(K,Ball(S),H.E(I));
      if S /= ScalZero then H.P := False; end if;
    else
      Message("Fouriers.Ops.SetErrComp",Index_Error);
    end if;
  end SetErrComp;

  procedure AddErrComp(I: in FreqE; P: in Taylor; H: in out Fourier) is
  begin
    if Trunc or else Deg(P)<0 then return; end if;
    MultAdd(Ball(One),P,H.E(I));
    H.P := False;
  end AddErrComp;

  procedure AddBall(S: in Scalar; H: in out Fourier) is
  begin
    if Trunc or else S=ScalZero then return; end if;
    AddErrComp(Abs(S),H.E(0));
    H.P := False;
  end AddBall;

  procedure Copy(H1: in Fourier; H2: out Fourier) is
  begin
    H2.P := H1.P;
    H2.J := H1.J;
    H2.R := H1.R;
    for M in Freq1 loop
      for N in Nmin(M) .. Nmax(M) loop
        Copy(H1.C(M,N),H2.C(M,N));
      end loop;
    end loop;
    if Trunc then return; end if;
    for I in FreqE loop
      Copy(H1.E(I),H2.E(I));
    end loop;
  end Copy;

  procedure ZeroConstant(H: in out Fourier) is
  begin
    if Dho>0 then
      SetComponent(0,0,0,ScalZero,H);
    else
      ResetCenter(H.C(0,0),0,0);
    end if;
  end ZeroConstant;

  procedure ErrConvert(H: in Fourier; P: out Taylor) is
    Dn: Integer;
  begin
    SetZero(P);
    if Trunc then return; end if;
    for I in reverse FreqE loop
       MultAdd(Ball(One),H.E(I),P);
    end loop;
    for M in Freq1 loop
      EffDeg(M,H,Dn);
      for N in Nmin(M,H.J) .. Dn loop
        MultAdd(Ball(NormFactor(H.R.Q,M,N)),H.C(M,N),P);
      end loop;
    end loop;
  end ErrConvert;

  procedure QDegConvert(L,W: in Positive; H: in out Fourier) is
    Lt: constant Positive := Imin(L,Lmax);
    Wt: constant Positive := Imin(W,Wmax);
    D: Integer;
  begin
    for M in Freq1 loop
      for N in Nmin(M) .. Nmax(M) loop
        Cleanup(H.C(M,N));
        if Kcut(M,N,Lt,Wt)<0 and then Deg(H.C(M,N)) >= 0 then
          if not Trunc then
            MultAdd(Ball(NormFactor(H.R.Q,M,N)),H.C(M,N),H.E(Ierr(M,N)));
            H.P := False;
          end if;
          SetZero(H.C(M,N));
        end if;
      end loop;
    end loop;
    if H.P then return; end if;

    D := NumErrs(Lt,Wt);
    for I in D+1 .. SizeE loop
      Add(H.E(I),H.E(D));
      SetZero(H.E(I));
    end loop;
  end QDegConvert;

  function Coeff(M,N: Integer; K: Natural; H: Fourier) return Scalar is
    C: Scalar := ScalZero;
  begin
    if H.P then
      if InRange(M,N) then
        C := Component(K,H.C(M,N));
      end if;
    else
      for I in 0 .. Ierr(M,N) loop
        C := C+Coeff(H.R.P,K,H.E(I));
      end loop;
      C := Ball(C/NormFactor(H.R.Q,M,N));
      if InRange(M,N) then
        C := C+Coeff(H.R.P,K,H.C(M,N));
      end if;
    end if;
    return C;
  end Coeff;

  procedure UpdateSavedFactors(R: in Radius) is
    Fm,Fn,Pm,Pmn: Scalar;
  begin
    if R=SavedFactors.R then return; end if;
    SavedFactors.R := R;
    Fn := Exp((-R)*InvGold);
    Fm := Exp(Scal(R));                                       --- start nonnegative m's
    Pm := ScalOne;
    for M in 0 .. SizeE1 loop
      Pmn := Pm;
      for N in FreqE2 loop
        SavedFactors.C(M,N) := HalfPlus*(Pmn+Inv(Pmn));
        Pmn := Pmn*Fn;
      end loop;
      Pm := Pm*Fm;
    end loop;
    Fm := Inv(Fm);                                             --- now negative m's
    Pm := Fm;
    for M in reverse -SizeE1 .. -1 loop
      Pmn := Pm;
      for N in FreqE2 loop
        SavedFactors.C(M,N) := HalfPlus*(Pmn+Inv(Pmn));
        Pmn := Pmn*Fn;
      end loop;
      Pm := Pm*Fm;
    end loop;
  end UpdateSavedFactors;

  function NormFactor(R: Radius; M,N: Integer; Update: in Boolean := False) return Scalar is
  begin
    if N<0 then
      return NormFactor(R,-M,-N,Update);
    elsif R=SavedFactors.R then
      if M in FreqE1 and then N in FreqE2 then
        return SavedFactors.C(M,N);
      end if;
    elsif Update then
      UpdateSavedFactors(R);
    end if;
    return OnePlus*Cosh(R*(Scal(M)+Rep(-N)*InvGold));
  end NormFactor;

  function Norm(R: Weights; H: Fourier) return Scalar is
    E: Scalar := ScalZero;
  begin
    if not H.P then
      for I in reverse FreqE loop
        E := E+Norm(R.P,H.E(I));
      end loop;
    end if;

    if R.Q=SavedFactors.R then
      for M in Freq1 loop
        for N in Nmin(M,H.J) .. Nmax(M) loop
          E := E+SavedFactors.C(M,N)*Norm(R.P,H.C(M,N));
        end loop;
      end loop;
      return E;
    end if;

    declare
      Fm,Fn,Pm,Pmn: Scalar;
    begin
      Fn := Exp((-R.Q)*InvGold);
      Fm := Exp(Scal(R.Q));                                     ---- start nonnegative m's
      Pm := ScalOne;
      for M in 0 .. Size1 loop
        Pmn := Pm;
        for N in 0 .. Nmax(M) loop
          E := E+HalfPlus*(Pmn+Inv(Pmn))*Norm(R.P,H.C(M,N));
          Pmn := Pmn*Fn;
        end loop;
        Pm := Pm*Fm;
      end loop;
      Fm := Inv(Fm);                                             ---- now negative m's
      Pm := Fm;
      for M in reverse -Size1 .. -1 loop
        Pmn := Pm*Fn;
        for N in 1 .. Nmax(M) loop
          E := E+HalfPlus*(Pmn+Inv(Pmn))*Norm(R.P,H.C(M,N));
          Pmn := Pmn*Fn;
        end loop;
        Pm := Pm*Fm;
      end loop;
    end;

    return E;
  end Norm;

  function Norm(H: Fourier) return Scalar is
  begin
    return Norm(H.R,H);
  end Norm;

  function Val(Z: Scalar; H: Fourier) return Scalar is
    S: Scalar := ScalZero;
  begin
    if H.J=1 then return ScalZero; end if;
    if not H.P then
      for I in reverse FreqE loop
        S := S+Val(H.R.P,Ball(Z),H.E(I));
      end loop;
    end if;
    for M in Freq1 loop
      for N in Nmin(M) .. Nmax(M) loop
        S:=S+Val(H.R.P,Z,H.C(M,N));
      end loop;
    end loop;
    return S;
  end Val;

  function ValQ0(H: Fourier) return Taylor is
    P: Taylor;
  begin
    SetZero(P);
    if H.J=1 then return P; end if;
    if not H.P then
      for I in reverse FreqE loop
        MultAdd(Ball(One),H.E(I),P);
      end loop;
    end if;
    for M in Freq1 loop
      for N in Nmin(M) .. Nmax(M) loop
        Add(H.C(M,N),P);
      end loop;
    end loop;
    return P;
  end ValQ0;

  procedure Neg(H1: in Fourier; H2: out Fourier) is
  begin
    H2.P := H1.P;
    H2.J := H1.J;
    H2.R := H1.R;
    for M in Freq1 loop
      for N in Nmin(M) .. Nmax(M) loop
        Neg(H1.C(M,N),H2.C(M,N));
      end loop;
    end loop;
    if Trunc then return; end if;
    for I in FreqE loop
      Copy(H1.E(I),H2.E(I));
    end loop;
  end Neg;

  function "-"(H: Fourier) return Fourier is
    Ht: Fourier;
  begin
    Neg(H,Ht);
    return Ht;
  end "-";

  procedure Mult(S: in Scalar; H: in out Fourier) is
  begin
    if S = ScalZero then
      SetZero(H.J,H.R,H);
      return;
    end if;
    H.P := H.P and Err0(S);          -- S may depend on p
    for M in Freq1 loop
      for N in Nmin(M,H.J) .. Nmax(M) loop
        Mult(S,H.C(M,N));
      end loop;
    end loop;
    if H.P then return; end if;
    for I in FreqE loop
      Mult(Abs(S),H.E(I));
    end loop;
  end Mult;

  procedure Prod(S: in Scalar; H1: in Fourier; H2: out Fourier) is
  begin
    if S = ScalZero then
      SetZero(H1.J,H1.R,H2);
      return;
    end if;
    H2.P := H1.P and Err0(S);          -- S may depend on p
    H2.J := H1.J;
    H2.R := H1.R;
    for M in Freq1 loop
      for N in Nmin(M) .. Nmax(M) loop
        Prod(S,H1.C(M,N),H2.C(M,N));
      end loop;
    end loop;

    if Trunc then return; end if;
    if H1.P then
      ResetErrComp(H2);
      return;
    end if;

    for I in FreqE loop
      Prod(Abs(S),H1.E(I),H2.E(I));
    end loop;
  end Prod;

  function "*"(S: Scalar; H: Fourier) return Fourier is
    Ht: Fourier;
  begin
    Prod(S,H,Ht);
    return Ht;
  end "*";

  procedure Add(H1: in Fourier; H2: in out Fourier) is
  begin
    if H1.J /= H2.J then
      Message("Fouriers.Ops.Add",Parity_Error);
    end if;
    H2.R := Min(H1.R,H2.R);
    for M in Freq1 loop
      for N in Nmin(M,H1.J) .. Nmax(M) loop
        Add(H1.C(M,N),H2.C(M,N));
      end loop;
    end loop;
    if H1.P then return; end if;
    H2.P := Trunc;
    for I in FreqE loop
      Add(H1.E(I),H2.E(I));
    end loop;
  end Add;

  procedure Sum(H1,H2: in Fourier; H3: out Fourier) is
  begin
    if H1.J /= H2.J then
      Message("Fouriers.Ops.Sum",Parity_Error);
    end if;
    H3.P := H1.P and H2.P;
    H3.J := H1.J;
    H3.R := Min(H1.R,H2.R);
    for M in Freq1 loop
      for N in Nmin(M) .. Nmax(M) loop
        Sum(H1.C(M,N),H2.C(M,N),H3.C(M,N));
      end loop;
    end loop;

    if Trunc then return; end if;
    if H3.P then
      ResetErrComp(H3);
      return;
    end if;

    for I in FreqE loop
      Sum(H1.E(I),H2.E(I),H3.E(I));
    end loop;
  end Sum;

  function "+"(H1,H2: Fourier) return Fourier is
    Hr: Fourier;
  begin
    Sum(H1,H2,Hr);
    return Hr;
  end "+";

  procedure Sub(H1: in Fourier; H2: in out Fourier) is
  begin
    if H1.J /= H2.J then
      Message("Fouriers.Ops.Sub",Parity_Error);
    end if;
    H2.R := Min(H1.R,H2.R);
    for M in Freq1 loop
      for N in Nmin(M,H1.J) .. Nmax(M) loop
        Sub(H1.C(M,N),H2.C(M,N));
      end loop;
    end loop;
    if H1.P then return; end if;
    H2.P := Trunc;
    for I in FreqE loop
      Add(H1.E(I),H2.E(I));
    end loop;
  end Sub;

  procedure Diff(H1,H2: in Fourier; H3: out Fourier) is
  begin
    if H1.J /= H2.J then
      Message("Fouriers.Ops.Diff",Parity_Error);
    end if;
    H3.P := H1.P and H2.P;
    H3.J := H1.J;
    H3.R := Min(H1.R,H2.R);
    for M in Freq1 loop
      for N in Nmin(M) .. Nmax(M) loop
        Diff(H1.C(M,N),H2.C(M,N),H3.C(M,N));
      end loop;
    end loop;

    if Trunc then return; end if;
    if H3.P then
      ResetErrComp(H3);
      return;
    end if;

    for I in FreqE loop
      Sum(H1.E(I),H2.E(I),H3.E(I));
    end loop;
  end Diff;

  function "-"(H1,H2: Fourier) return Fourier is
    Hr: Fourier;
  begin
    Diff(H1,H2,Hr);
    return Hr;
  end "-";

  procedure MultAdd(S: in Scalar; H1: in Fourier; H2: in out Fourier) is
  begin
    if S=ScalZero then return; end if;
    if H1.J /= H2.J then
      Message("Fouriers.Ops.MultAdd",Parity_Error);
    end if;
    H2.P := Err0(S) and H1.P and H2.P;         -- S may depend on p
    H2.R := Min(H1.R,H2.R);
    for M in Freq1 loop
      for N in Nmin(M,H1.J) .. Nmax(M) loop
        MultAdd(S,H1.C(M,N),H2.C(M,N));
      end loop;
    end loop;
    if H1.P then return; end if;
    for I in FreqE loop
      MultAdd(Abs(S),H1.E(I),H2.E(I));
    end loop;
  end MultAdd;

  procedure Iminus(L: in Degrees; H: in out Fourier) is
  begin
    for M in Freq1 loop
      for N in Nmin(M,H.J) .. Nmax(M) loop
        if L(M,N)<0 then
          SetZero(H.C(M,N));
        else
          ResetComponent(H.C(M,N),L(M,N)+1);
        end if;
      end loop;
    end loop;
  end Iminus;

  function Iminus(L: Degrees; H: Fourier) return Fourier is
    Hr: Fourier;
  begin
    Copy(H,Hr);
    Iminus(L,Hr);
    return Hr;
  end Iminus;

  procedure Iplus(L: in Degrees; H: in out Fourier) is
  begin
    for M in Freq1 loop
      for N in Nmin(M,1) .. Nmax(M) loop
        if H.P then
          ResetComponent(H.C(M,N),0,L(M,N));
        else
          ResetComponent(H.C(M,N),0,Imin(Dho-1,L(M,N)));
          ResetCenter(H.C(M,N),Imin(Dho-1,L(M,N))+1,L(M,N));
        end if;
      end loop;
    end loop;
  end Iplus;

  function Iplus(L: Degrees; H: Fourier) return Fourier is
    Hr: Fourier;
  begin
    Copy(H,Hr);
    Iplus(L,Hr);
    return Hr;
  end Iplus;

  procedure Normalize(M,N: in Integer; M3,N3: out Integer; Flip: out Boolean) is
  begin
    if N<0 or else (N=0 and then M<0) then
      M3 := -M; N3 := -N; Flip := True;
    else
      M3 := M; N3 := N; Flip := False;
    end if;
  end Normalize;

  pragma Inline (Normalize);

  procedure MultAdd(S: in Scalar; M,N: in Integer; P1: in Taylor; H2: in Fourier; H3: in out Fourier; D: in Degrees := Dmax; Monom1: in Boolean := False) is
    J1: constant Parity := (H2.J+H3.J) mod 2;
    Flip: Boolean;
    M1,M3,N1,N3,I1: Integer;
    Cp,Cm1,Cm2,F: Scalar;

  begin
    Normalize(M,N,M1,N1,Flip);           --- (M,N) need not be inrange
    if Flip and then J1=1 then
      Cp := (-Half)*S;
    else
      Cp := Half*S;
    end if;
    Cm1 := Cp;
    Cm2 := Cp;
    if J1 < H2.J then
      Cm1 := -Cp;
    elsif J1 > H2.J then
      Cm2 := -Cp;
    elsif J1 = 1 then
      Cp := -Cp;
    end if;

    if not Trunc then
      if Min(H2.R,H3.R) /= H3.R then
        Message("Fouriers.Ops.MultAdd",Domain_Violation);
      end if;
      H3.P := False;
      UpdateSavedFactors(H3.R.Q);
    end if;

    for M2 in Freq1 loop
      for N2 in Nmin(M2,H2.J) .. Nmax(M2) loop
        if Deg(H2.C(M2,N2))>=0 then

          Normalize(M1+M2,N1+N2,M3,N3,Flip);
          if InRange(M3,N3) then                                             --- frequency sum
            MultAdd(H3.R.P,Cp,P1,H2.C(M2,N2),H3.C(M3,N3),D(M3,N3),Monom1);
          elsif not Trunc then
            F := Ball(Cp*SavedFactors.C(M3,N3));
            MultAdd(H3.R.P,F,P1,H2.C(M2,N2),H3.E(Ierr(M3,N3)),Pdeg,Monom1);
          end if;

          Normalize(M1-M2,N1-N2,M3,N3,Flip);
          if InRange(M3,N3) then                                             --- frequency difference
            if not Flip then                                                 --- 1st minus 2nd frequency
              MultAdd(H3.R.P,Cm1,P1,H2.C(M2,N2),H3.C(M3,N3),D(M3,N3),Monom1);
            else                                                             --- 2nd minus 1st frequency
              MultAdd(H3.R.P,Cm2,P1,H2.C(M2,N2),H3.C(M3,N3),D(M3,N3),Monom1);
            end if;
          elsif not Trunc then
            F := Ball(Cp*SavedFactors.C(M3,N3));
            MultAdd(H3.R.P,F,P1,H2.C(M2,N2),H3.E(Ierr(M3,N3)),Pdeg,Monom1);
          end if;

        end if;
      end loop;
    end loop;

    if H3.J=1 then
      SetZero(H3.C(0,0));
    end if;
    if H2.P then return; end if;

    if M1=0 and then N1=0 then I1 := -1; else I1 := Ierr(M1,N1); end if;
    F := Ball(S*SavedFactors.C(M1,N1));
    for I2 in FreqE loop
      MultAdd(H3.R.P,F,P1,H2.E(I2),H3.E(Imax(0,I2-I1-1)),Pdeg,Monom1);       --- add coeffs*errors
    end loop;
  end MultAdd;

  procedure MultAdd(S: in Scalar; H1,H2: in Fourier; H3: in out Fourier; D: in Degrees := Dmax) is
    I2: Integer;
    F: Scalar;
  begin
    if not Trunc then
      if Min(Min(H1.R,H2.R),H3.R) /= H3.R then
        Message("Fouriers.Ops.MultAdd",Domain_Violation);
      end if;
      UpdateSavedFactors(H3.R.Q);
    end if;

    for M in Freq1 loop
      for N in Nmin(M,H1.J) .. Nmax(M) loop
        if Deg(H1.C(M,N))>=0 then
          MultAdd(S,M,N,H1.C(M,N),H2,H3,D);                                  --- includes coeffs*errors
        end if;
      end loop;
    end loop;
    if H1.P then return; end if;

    UpdateSavedFactors(H3.R.Q);
    for M in Freq1 loop
      for N in Nmin(M,H2.J) .. Nmax(M) loop
        if M=0 and then N=0 then I2 := -1; else I2 := Ierr(M,N); end if;
        F := Ball(S*SavedFactors.C(M,N));
        for I1 in FreqE loop
          MultAdd(H3.R.P,F,H1.E(I1),H2.C(M,N),H3.E(Imax(0,I1-I2-1)),Pdeg);   --- add errors*coeffs
        end loop;
      end loop;
    end loop;
    if H2.P then return; end if;

    declare
      E1,E2: Taylor;
    begin
      SetZero(E1);
      SetZero(E2);
      for I in reverse FreqE loop
        Add(H1.E(I),E1);
        Add(H2.E(I),E2);
      end loop;
      MultAdd(H3.R.P,Abs(S),E1,E2,H3.E(0),Pdeg);                              --- add errors*errors
    end;
  end MultAdd;

  procedure Mult(H1: in Fourier; H2: in out Fourier) is
    Ht: Fourier;
  begin
    Copy(H2,Ht);
    SetZero((H1.J+Ht.J) mod 2,Min(H1.R,Ht.R),H2);
    MultAdd(ScalOne,H1,Ht,H2);
  end Mult;

  procedure Prod(R: in Weights; S: in Scalar; H1,H2: in Fourier; H3: out Fourier; D: in Degrees := Dmax) is
  begin
    SetZero((H1.J+H2.J) mod 2,R,H3);
    MultAdd(S,H1,H2,H3,D);
  end Prod;

  procedure Prod(R: in Weights; H1,H2: in Fourier; H3: out Fourier; D: in Degrees := Dmax) is
  begin
    SetZero((H1.J+H2.J) mod 2,R,H3);
    MultAdd(ScalOne,H1,H2,H3,D);
  end Prod;

  function "*"(H1,H2: Fourier) return Fourier is
    Hr: Fourier;
  begin
    SetZero((H1.J+H2.J) mod 2,Min(H1.R,H2.R),Hr);
    MultAdd(ScalOne,H1,H2,Hr);
    return Hr;
  end "*";

  procedure Powers(R: in Weights; H: in Fourier; Hp: in FouVecPtr) is
    I: Integer;
  begin
    if not (H.P or else Min(R,H.R)=R) then
      Message("Fouriers.Ops.Powers",Domain_Violation);
    end if;
    Copy(H,Hp(1));
    Hp(1).R := R;
    I := 1;
    for K in 2 .. Hp'Last loop
      Prod(R,Hp(I),Hp(K-I),Hp(K));
      if I+I=K then I := K; end if;
    end loop;
  end Powers;

  procedure Comp(IsPoly: in Boolean; R: in Radius; P: in Taylor; Hp: in FouVecPtr; Hr: out Fourier; D: in Power := Dho) is
--- Hp(K) is assumed to be the K-th power of Hp(1)
    S,C,E: Scalar;
    Pe: Taylor;
  begin
--    TraceEnter("Comp");
    SetZero(0,Hp(1).R,Hr);
    if Deg(P)<0 then
--      TraceLeave;
      return;
    end if;

    if Hp(1).J /= 0 then
      Message("Fouriers.Ops.Comp",Not_Implemented);
    end if;

    if IsPoly then
      for K in reverse 1 .. Deg(P) loop
        MultAdd(Component(K,P),Hp(K),Hr);
      end loop;
      AddComponent(0,Component(0,P),Hr.C(0,0));
--      TraceLeave;
      return;
    end if;

    S := Norm(Hp(1));
    if Sup(S)>R then
      Message("Fouriers.Ops.Comp",Domain_Violation);
    end if;

    for K in reverse Imax(D,1) .. Deg(P) loop
      Split(Component(K,P),C,E);
      MultAdd(C,Hp(K),Hr);
      ErrConvert(Hp(K),Pe);
      MultAdd(E,Pe,Hr.E(0));
    end loop;
    for K in reverse 1 .. D-1 loop
      MultAdd(Component(K,P),Hp(K),Hr);
    end loop;
    if D=0 then
      Split(Component(0,P),C,E);
      AddComponent(0,C,Hr.C(0,0));
      AddErrComp(E,Hr.E(0));
    else
      AddComponent(0,Component(0,P),Hr.C(0,0));
    end if;
    AddErrComp(ErrComp(P),Hr.E(0));
--    TraceLeave;
  end Comp;

  procedure Inv(R: in Weights; H1: in Fourier; H2: in out Fourier) is
--- just adds ball around approximate inverse H2
  begin
    if Trunc then return; end if;
    declare
      Ea,Eb,Ec,Ed: Scalar;
      Ht: Fourier;
    begin
      Prod(R,H1,H2,Ht);
      AddComponent(0,0,0,-ScalOne,Ht);
      Ea := Norm(Ht);
      Eb := Norm(R,H1);
      Ec := Norm(R,H2)*Ea;
      Ed := Two*(Ea+Eb*Ec);
      if Sup(Ed)>Half or Sup(Ed+Two*Eb*Ec)>=One then
        Message("Fouriers.Ops.Inv",Domain_Violation);     --- to stop, use Domain_Error
      end if;
      H2.P := False;
      AddErrComp(Ec*Inv(ScalOne-Ed),H2.E(0));
    end;
  end Inv;

  procedure Scale(S: in Scalar; H1: in Fourier; H2: out Fourier) is
  begin
    H2.P := H1.P;                        -- S is constant
    H2.J := H1.J;
    H2.R := H1.R;
    H2.R.P := Inf(Scal(H2.R.P)/Abs(S));
    for M in Freq1 loop
      for N in Nmin(M) .. Nmax(M) loop
        Scale(S,H1.C(M,N),H2.C(M,N));
      end loop;
    end loop;

    if Trunc then return; end if;
    if H1.P then
      ResetErrComp(H2);
      return;
    end if;

    for I in FreqE loop
      Scale(Abs(S),H1.E(I),H2.E(I));
    end loop;
  end Scale;

  procedure Scale(S: in Scalar; H: in out Fourier) is
  begin
    H.R.P := Inf(Scal(H.R.P)/Abs(S));
    for M in Freq1 loop
      for N in Nmin(M,H.J) .. Nmax(M) loop
        Scale(S,H.C(M,N));
      end loop;
    end loop;
    if H.P then return; end if;
    for I in FreqE loop
      Scale(Abs(S),H.E(I));
    end loop;
  end Scale;

  function Scale(S: Scalar; H: Fourier) return Fourier is
    Hr: Fourier;
  begin
    Scale(S,H,Hr);
    return Hr;
  end Scale;

  procedure ScaleRem1(Eps: in Scalar; H1: in Fourier; H2: out Fourier) is
  begin
    SetZero(H1.J,H1.R,H2);
    H2.P := H1.P;           -- Eps is a constant
    for M in Freq1 loop
      for N in Nmin(M,H1.J) .. Nmax(M) loop
        ScaleRem1(Eps,H1.C(M,N),H2.C(M,N),H1.P);
      end loop;
    end loop;
  end ScaleRem1;

  procedure ScaleRem2(Eps: in Scalar; H1: in Fourier; H2: out Fourier) is
  begin
    SetZero(H1.J,H1.R,H2);
    H2.P := H1.P;           -- Eps is a constant
    for M in Freq1 loop
      for N in Nmin(M,H1.J) .. Nmax(M) loop
        ScaleRem2(Eps,H1.C(M,N),H2.C(M,N),H1.P);
      end loop;
    end loop;
  end ScaleRem2;

  procedure OmegaDq(R2: in Radius; H1: in Fourier; H2: out Fourier) is
    S: Scalar;
  begin
    H2.P := H1.P;
    H2.J := 1-H1.J;
    H2.R.Q := R2;
    H2.R.P := H1.R.P;
    for M in Freq1 loop
      for N in Nmin(M,1) .. Nmax(M) loop
        if H1.J = 0 then
          Prod(Scal(-M)+Rep(N)*InvGold,H1.C(M,N),H2.C(M,N));
        else
          Prod(Scal(M)+Rep(-N)*InvGold,H1.C(M,N),H2.C(M,N));
        end if;
      end loop;
    end loop;
    SetZero(H2.C(0,0));

    if Trunc then return; end if;
    if H1.P then
      ResetErrComp(H2);
      return;
    end if;                -- not really used beyond this

    if R2>=H1.R.Q then
      Message("Fouriers.Ops.OmegaDq",Domain_Violation);
    end if;
    S := GammaMax(Scal(R2)/H1.R.Q,ScalZero,ScalZero);
    S := S/(Exp(ScalOne)*(Scal(H1.R.Q)-Scal(R2)));
    for I in FreqE loop
      Prod(Abs(S),H1.E(I),H2.E(I));
    end loop;
  end OmegaDq;

  procedure Der3(R3: in Radius; H1: in Fourier; H2: out Fourier) is
    Mt: Integer;
    Ft: Scalar;
  begin
    H2.P := H1.P;
    H2.J := H1.J;
    H2.R := H1.R;
    for M in Freq1 loop
      for N in Nmin(M) .. Nmax(M) loop
        Der(H1.R.P,R3,H1.C(M,N),H2.C(M,N),H1.P);
      end loop;
    end loop;
    H2.R.P := R3;

    if Trunc then return; end if;
    if H1.P then
      ResetErrComp(H2);
    end if;

    Fmax1(Scal(R3)/H1.R.P,Mt,Ft);
    Ft := Ft/R3;
    for I in FreqE loop
      Prod(Ft,H1.E(I),H2.E(I));
    end loop;
  end Der3;

  function Der3(R3: Radius; H: Fourier) return Fourier is
    Hr: Fourier;
  begin
    Der3(R3,H,Hr);
    return Hr;
  end Der3;

  function DerVal3(Z: Scalar; H: Fourier) return Scalar is
    Hd: Fourier;
  begin
    if H.J=1 then return ScalZero; end if;
    Der3(SupAbs(Z),H,Hd);
    return Val(Z,Hd);
  end DerVal3;

  procedure Gradient(R: in Weights; H: in Fourier; Hdq,Hdp: out Fourier) is
  begin
    OmegaDq(R.Q,H,Hdq);
    Der3(R.P,H,Hdp);
  end Gradient;

  function DerDer3(H: Fourier) return Fourier is
    Hr: Fourier;
  begin
    Copy(H,Hr);
    for M in Freq1 loop
      for N in Nmin(M) .. Nmax(M) loop
        DerDer(Hr.C(M,N),Hr.P);
      end loop;
    end loop;
    return Hr;
  end DerDer3;

  procedure Comp3(H: in Fourier; Hp3: in FouVecPtr; Hr: out Fourier) is
--- Hp3(K) is assumed to be the K-th power of Hp3(1)
    R: Weights;
    Hk: Fourier;
  begin
    TraceEnter("Comp3");
    if Hp3(1).J /= 0 then
      Message("Fouriers.Ops.Comp3",Not_Implemented);
    end if;
    R.Q := Radius'Min(H.R.Q,Hp3(1).R.Q);
    R.P := Hp3(1).R.P;
    SetZero(H.J,R,Hr);

    if not H.P then
      R.P := Sup(Norm(R,Hp3(1)));
      if R.P>H.R.P then
        Message("Fouriers.Ops.Comp3",Domain_Violation);
      end if;
    end if;

    for K in reverse 1 .. Pdeg loop
      Component(K,H,Hk);
      MultAdd(ScalOne,Hk,Hp3(K),Hr);
    end loop;
    Component(0,H,Hk);
    Add(Hk,Hr);
    TraceLeave;
  end Comp3;

  procedure Comp3(H,H3: in Fourier; Hr: out Fourier) is
    Hp3: FouVecPtr := new FouVec(1 .. Pdeg);
  begin
    Powers((Radius'Min(H.R.Q,H3.R.Q),H3.R.P),H3,Hp3);
    Comp3(H,Hp3,Hr);
  end Comp3;

  procedure Comp3(H3: in Fourier; H: in out Fourier) is
    Hr: Fourier;
    Hp3: FouVecPtr := new FouVec(1 .. Pdeg);
  begin
    Powers((Radius'Min(H.R.Q,H3.R.Q),H3.R.P),H3,Hp3);
    Comp3(H,Hp3,Hr);
    Copy(Hr,H);
  end Comp3;

  function Comp3(H,H3: Fourier) return Fourier is
    Hr: Fourier;
    Hp3: FouVecPtr := new FouVec(1 .. Pdeg);
  begin
    Powers((Radius'Min(H.R.Q,H3.R.Q),H3.R.P),H3,Hp3);
    Comp3(H,Hp3,Hr);
    return Hr;
  end Comp3;

  procedure Comp3(H3: in Fourier; H: in FouVecPtr) is
    Hr: Fourier;
    Hp3: FouVecPtr := new FouVec(1 .. Pdeg);
  begin
    Powers((Radius'Min(H(1).R.Q,H3.R.Q),H3.R.P),H3,Hp3);
    for L in H'Range loop
      Comp3(H(L),Hp3,Hr);
      Copy(Hr,H(L));
    end loop;
  end Comp3;

  procedure CosSin(H: in Fourier; Hw1,Hw2,Hc,Hs: out Fourier) is
--- uses Hw1 and Hw2 as workspace only
    Iter: constant Integer := 6;
    Small: constant Rep := Half;
    K: Natural := 0;
    S,E,Ec,Es: Scalar;
  begin
    E := Norm(H);
    while Sup(E)>Small loop
      K := K+1;
      E := Half*E;
    end loop;
    SetZero(0,H.R,Hc);
    AddComponent(0,0,0,ScalOne,Hc);
    S := Scal(Half**K);
    Prod(S,H,Hs);
    Copy(Hs,Hw1);
    Ec := E**(2*Iter+1);
    for I in 1 .. Iter loop                                   -- Taylor polynomials
      Prod(H.R,S,H,Hw1,Hw2);
      Mult(Inv(Scal(-2*I)),Hw2);
      Add(Hw2,Hc);
      Prod(H.R,S,H,Hw2,Hw1);
      Mult(Inv(Scal(2*I+1)),Hw1);
      Add(Hw1,Hs);
      Ec := Ec/Rep(2*I*(2*I+1));
    end loop;
    if not Trunc then
      Ec := E*Ec/Rep(2*Iter+2);
      Es := E*Ec/Rep(2*Iter+3);
      Ec := Ec/(ScalOne-Sqr(E)/Rep((2*Iter+3)*(2*Iter+4)));
      AddErrComp(Ec,Hc.E(0));                                  -- Remainder for Cos(H)
      Es := Es/(ScalOne-Sqr(E)/Rep((2*Iter+4)*(2*Iter+5)));
      AddErrComp(Es,Hs.E(0));                                  -- Remainder for Sin(H)
      Hc.P := False;
      Hs.P := False;
    end if;
    for I in 1 .. K loop                                      -- double angle formula, K times
      Prod(H.R,Hs,Hs,Hw1);
      Prod(H.R,Hs,Hc,Hw2);
      Prod(ScalTwo,Hw2,Hs);
      Prod(H.R,Hc,Hc,Hw2);                                    -- could avoid one multiplication
      Diff(Hw2,Hw1,Hc);
    end loop;
  end CosSin;

  procedure GammaGuessX(Err: in Radius; S,T,X0: in Rep; XL,XR: out Rep) is
--- Guess where Gamma takes its maximum value
    use Reps.Ops;
    Esx,Ex,XM,YL,YM,YR: Rep;

    function DGammaSign(X: Rep) return Rep is
    begin
      Esx := Exp(-Two*S*X);
      Ex:= Exp(-Two*X);
      return Two*Ex*(One+Esx)/(One+Ex)-(T+Two*S)*Esx-T;
    end DGammaSign;

  begin
    XR := X0;
    YR := DGammaSign(XR);
    if YR<=Zero then
      XL := X0;
      return;
    end if;
    while YR>Zero loop
      XL := XR;
      YL := YR;
      XR := XL+One;
      YR := DGammaSign(XR);
    end loop;
    while XR-XL>Err loop
      XM := Half*(XL+XR);
      YM := DGammaSign(XM);
      if YM>Zero then
        XL := XM;
        YL := YM;
      elsif YM<Zero then
        XR := XM;
        YR := YM;
      else
        XL := XM;
        XR := XM;
        return;
      end if;
    end loop;
  end GammaGuessX;

  function Gamma(S,T,X: Scalar) return Scalar is
  begin
    return Exp(-T*X)*(ScalOne+Exp((-Two)*S*X))/(ScalOne+Exp((-Two)*X));
  end Gamma;

  function GammaMax(S,T,X0: Scalar) return Scalar is
    Err: constant Radius := 1.0E-6;
    Eps:  constant Radius := 1.0E-5;
    XL,XR: Rep;
    XM,Esx,Ex: Scalar;

    function DGammaSign(X: Scalar) return Scalar is
    begin
      Esx := Exp((-Two)*S*X);
      Ex:= Exp((-Two)*X);
      return Two*Ex*(ScalOne+Esx)/(ScalOne+Ex)-(T+Two*S)*Esx-T;
    end DGammaSign;

  begin
    if Inf(T)<Zero then
      Message("RG_Ops.GammaMax",Parameter_Error);
    end if;
    if Sup(DGammaSign(X0))<Zero then
      return Gamma(S,T,X0);
    end if;
    GammaGuessX(Err,Approx(S),Approx(T),Approx(X0),XL,XR);
    XM := Scal(Half*(XR+XL))+Ball(Half*(XR-XL)+Eps);
    if not Trunc and then (Inf(DGammaSign(Scal(Inf(XM))))<Zero or Sup(DGammaSign(Scal(Sup(XM))))>Zero) then
      Message("RG_Ops.GammaMax",Parameter_Error);
    end if;
    return Gamma(S,T,XM);
  end GammaMax;

  procedure EffDeg(M: in Integer; H: in Fourier; Dn: out Integer) is
  begin
    for N in reverse Nmin(M,H.J) .. Nmax(M) loop
      if not IsZero(H.C(M,N)) then
        Dn := N;
        return;
      end if;
    end loop;
    Dn := -1;
  end EffDeg;

  procedure EffDeg(H: in Fourier; Dm: out Integer) is
  begin
    Dm := -1;
    for M in Freq1 loop
      for N in Nmin(M,H.J) .. Nmax(M) loop
        if not IsZero(H.C(M,N)) then
          Dm := Imax(Abs(M),Dm);
        end if;
      end loop;
    end loop;
  end EffDeg;

  procedure IncCS(R: in Weights; Hc1,Hs1: in Fourier; Hwc,Hws: out Fourier; Hc2,Hs2: in out Fourier) is
--- make Hc2 := cos(H2+H1), Hs2 := sin(H2+H1)
--- from Hc1=cos(H1), Hs1=sin(H1), Hc2=cos(H2), Hs2=sin(H2)
--- uses Hwc and Hws as workspace only
  begin
    Copy(Hc2,Hwc);
    Copy(Hs2,Hws);
    Prod(R,Hc1,Hws,Hs2);
    MultAdd(ScalOne,Hs1,Hwc,Hs2);
    Prod(R,Hc1,Hwc,Hc2);
    Multadd(-ScalOne,Hs1,Hws,Hc2);
  end IncCS;

  procedure UHatEtc(Hq: in Fourier; Rp,R: in Weights; SigHat,UHat: out Scalar) is
    B: Weights;
  begin
    B.Q := Sup(Norm(R,Hq));
    B.P := R.P;
    SigHat := Scal(Rp.Q)-R.Q*InvGold-B.Q*InvGold;
    if Inf(Rp.P*Sqr(Gold))<B.P then
      Message("Fouriers.Ops.UHatEtc",Domain_Violation);
    end if;
    UHat := GammaMax(R.Q*InvGold/Rp.Q,SigHat/Rp.Q,ScalZero);
  end UHatEtc;

  procedure CompOmegaTmu0(R: in Weights; H,Hq: in Fourier; Hr: out Fourier; DoErr: in Boolean := True) is
    S0: constant Scalar := -Gold-ScalOne;  -- same as -Gold^2
    S2: constant Scalar := -Sqr(InvGold);  -- same as -1/Gold^2
    Dm,Dn: Integer;
    M: Freq1;
    S1,SigHat,UHat: Scalar;
    P: Taylor;
    Hw1,Hw2,Hc1,Hs1,Hc2,Hs2,Hmc,Hms,Hmnc,Hmns: Fourier;
  begin
    TraceEnter("CompOmegaTmu0");
    if Hq.J /= 1 then
      Message("Fouriers.Ops.CompOmegaTmu0",Not_Implemented);
    end if;

    SetZero(H.J,R,Hr);
    if DoErr and not H.P then              -- could be improved with more info on H
      for I in FreqE loop
        Add(H.E(I),Hr.E(0));
      end loop;
      UHatEtc(Hq,H.R,R,SigHat,UHat);       -- domains get checked here
      Scale(Abs(S2),Hr.E(0));
      Mult(UHat*Abs(S0),Hr.E(0));
    end if;

    EffDeg(H,Dm);                          -- looks only at H.C
    if Dm<0 then
      TraceLeave;
      return;
    end if;

    S1 := Rep(2*H.J-1)*S0;

    CosSin((-InvGold)*Hq,Hw1,Hw2,Hc1,Hs1);
    CosSin(Sqr(InvGold)*Hq,Hw1,Hw2,Hc2,Hs2);
    SetZero(0,R,Hmc);
    SetComponent(0,0,0,ScalOne,Hmc);
    SetZero(1,R,Hms);

    for Mabs in 0 .. Dm loop
      if Mabs>0 then
        IncCS(R,Hc1,Hs1,Hw1,Hw2,Hmc,Hms);
      end if;
      for I in 0 .. 1 loop
        M := (2*I-1)*Mabs;
        Hms := -Hms;
        if M<0 or else I=1 then
          Copy(Hmc,Hmnc);
          Copy(Hms,Hmns);
          EffDeg(M,H,Dn);
          for N in Nmin(M,H.J) .. Dn loop
            if N > 0 then
              IncCS(R,Hc2,Hs2,Hw1,Hw2,Hmnc,Hmns);
            end if;
            Scale(S2,H.C(M,N),P);
            MultAdd(S0,N,M+N,P,Hmnc,Hr);
            MultAdd(S1,N,M+N,P,Hmns,Hr);
          end loop;
        end if;
      end loop;
    end loop;
    TraceLeave;
  end CompOmegaTmu0;

  procedure CompOmegaTmu0(R: in Weights; H: in FouVecPtr; Hq: in Fourier; Hr: in FouVecPtr; DoErr: in Boolean := True) is
--- R.P does not enter directly
    Lf: constant Positive := Imin(H'Last,Hr'Last);
    S0: constant Scalar := -Gold-ScalOne;   -- same as -Gold^2
    S2: constant Scalar := Gold-ScalTwo;    -- same as -1/Gold^2
    Dt,Dm,Dn: Integer;
    M: Freq1;
    S1,SigHat,UHat: Scalar;
    P: Taylor;
    Hw1,Hw2,Hc1,Hs1,Hc2,Hs2,Hmc,Hms,Hmnc,Hmns: Fourier;
  begin
    TraceEnter("CompOmegaTmu0");
    if Hq.J /= 1 then
      Message("Fouriers.Ops.CompOmegaTmu0",Not_Implemented);
    end if;

    for L in 1 .. Lf loop
      SetZero(H(L).J,R,Hr(L));
      if DoErr and not H(L).P then           -- can be improved with more info on H
        for I in FreqE loop
          Add(H(L).E(I),Hr(L).E(0));
        end loop;
        UHatEtc(Hq,H(L).R,R,SigHat,UHat);
        Scale(Abs(S2),Hr(L).E(0));
        Mult(UHat*Abs(S0),Hr(L).E(0));
      end if;
    end loop;

    Dm := -1;
    for L in 1 .. Lf loop
      EffDeg(H(L),Dt);                       -- looks only at H(L).C
      Dm := IMax(Dm,Dt);
    end loop;
    if Dm<0 then
      TraceLeave;
      return;
    end if;

    CosSin((-InvGold)*Hq,Hw1,Hw2,Hc1,Hs1);
    CosSin(Sqr(InvGold)*Hq,Hw1,Hw2,Hc2,Hs2);
    SetZero(0,R,Hmc);
    SetComponent(0,0,0,ScalOne,Hmc);
    SetZero(1,R,Hms);

    for Mabs in 0 .. Dm loop
      if Mabs>0 then
        IncCS(R,Hc1,Hs1,Hw1,Hw2,Hmc,Hms);
      end if;
      for I in 0 .. 1 loop
        M := (2*I-1)*Mabs;
        Hms := -Hms;
        if M<0 or else I=1 then
          Copy(Hmc,Hmnc);
          Copy(Hms,Hmns);
          Dn := -1;
          for L in 1 .. Lf loop
            EffDeg(M,H(L),Dt);
            Dn := IMax(Dn,Dt);
          end loop;
          for N in Nmin(M) .. Dn loop
            if N > 0 then
              IncCS(R,Hc2,Hs2,Hw1,Hw2,Hmnc,Hmns);
            end if;
            for L in 1 .. Lf loop
              Scale(S2,H(L).C(M,N),P);
              S1 := Scal(2*H(L).J-1)*S0;
              MultAdd(S0,N,M+N,P,Hmnc,Hr(L));
              MultAdd(S1,N,M+N,P,Hmns,Hr(L));
            end loop;
          end loop;
        end if;
      end loop;
    end loop;
    TraceLeave;
  end CompOmegaTmu0;

  procedure CompOmega3(H1,H2: in Fourier; Hq,Hp: in Fourier; H3,H4: out Fourier) is
--- p-domains get checked in Comp
    Rg: constant Weights := Min(Hq.R,Hp.R);
    D1,D2,Dm,Dn: Integer;
    M: Freq1;
    S: Radius;
    P: Taylor;
    Hw1,Hw2,Hc1,Hs1,Hc2,Hs2,Hmc,Hms,Hmnc,Hmns: Fourier;
    Hpp: FouVecPtr := new FouVec(1 .. Pdeg);
  begin
    TraceEnter("CompOmega3 fff");
    if (Hq.J /= 1) or (Hp.J /= 0) then
      Message("Fouriers.Ops.CompOmega3",Not_Implemented);
    end if;
    SetZero(H1.J,Rg,H3);
    SetZero(H2.J,Rg,H4);
    Powers(Rg,Hp,Hpp);
    S := Sup(Scal(Rg.Q)+Norm(Rg,Hq));
    if not H1.P then
      if H1.R.Q<S then
        Message("Fouriers.Ops.CompOmega3",Domain_Violation);
      end if;
      SetZero(P);
      for I in FreqE loop
        Add(H1.E(I),P);
      end loop;
      Mult(Ball(One),P);
      Comp(False,H1.R.P,P,Hpp,Hw1,0);         -- non-coherent addition
      Hw1.J := H1.J;                          -- since P has parity of H1
      MultAdd(GammaMax(Scal(Rg.Q)/H1.R.Q,ScalZero,ScalZero),Hw1,H3);
    end if;
    if not H2.P then
      if H2.R.Q<S then
        Message("Fouriers.Ops.CompOmega3",Domain_Violation);
      end if;
      SetZero(P);
      for I in FreqE loop
        Add(H2.E(I),P);
      end loop;
      Mult(Ball(One),P);
      Comp(False,H2.R.P,P,Hpp,Hw2,0);         -- non-coherent addition
      Hw2.J := H2.J;                          -- since P has parity of H2
      MultAdd(GammaMax(Scal(Rg.Q)/H2.R.Q,ScalZero,ScalZero),Hw2,H4);
    end if;

    EffDeg(H1,D1);
    EffDeg(H2,D2);
    Dm := Imax(D1,D2);
    if Dm<0 then
      TraceLeave;
      return;
    end if;

    CosSin(Hq,Hw1,Hw2,Hc1,Hs1);
    CosSin((-InvGold)*Hq,Hw1,Hw2,Hc2,Hs2);
    SetZero(0,Rg,Hmc);
    SetComponent(0,0,0,ScalOne,Hmc);
    SetZero(1,Rg,Hms);

    for Mabs in 0 .. Dm loop
      if Mabs>0 then
        IncCS(Rg,Hc1,Hs1,Hw1,Hw2,Hmc,Hms);
      end if;
      for I in 0 .. 1 loop
        M := (2*I-1)*Mabs;
        Hms := -Hms;
        if M<0 or else I=1 then
          Hmnc := Hmc;
          Hmns := Hms;
          EffDeg(M,H1,D1);
          EffDeg(M,H2,D2);
          Dn := Imax(D1,D2);
          for N in Nmin(M) .. Dn loop
            if N>0 then
              IncCS(Rg,Hc2,Hs2,Hw1,Hw2,Hmnc,Hmns);
            end if;
            SetZero(P);
            SetComponent(0,ScalOne,P);

            SetZero(H1.J,Rg,Hw1);
            MultAdd(ScalOne,M,N,P,Hmnc,Hw1);
            MultAdd(Scal(2*H1.J-1),M,N,P,Hmns,Hw1);
            Comp(H1.P,H1.R.P,H1.C(M,N),Hpp,Hw2);
            MultAdd(ScalOne,Hw1,Hw2,H3);

            SetZero(H2.J,Rg,Hw1);
            MultAdd(ScalOne,M,N,P,Hmnc,Hw1);
            MultAdd(Scal(2*H2.J-1),M,N,P,Hmns,Hw1);
            Comp(H2.P,H2.R.P,H2.C(M,N),Hpp,Hw2);
            MultAdd(ScalOne,Hw1,Hw2,H4);
          end loop;
        end if;
      end loop;
    end loop;
    TraceLeave;
  end CompOmega3;

  procedure ZxDer3(H1: in Fourier; H2: out Fourier) is
  begin
    H2.P := H1.P;
    H2.J := H1.J;
    H2.R := H1.R;
    for M in Freq1 loop
      for N in Nmin(M) .. Nmax(M) loop
        ZxDer(H1.C(M,N),H2.C(M,N),H1.P);
      end loop;
    end loop;
    if Trunc then return; end if;
    ResetErrComp(H2);
  end ZxDer3;

  procedure DqDot(S1,S2: in Scalar; H: in out Fourier) is
  begin
    if not H.P then
      Message("Fouriers.Ops.DqDot",Not_Implemented);
    end if;
    H.J := 1-H.J;
    for M in Freq1 loop
      for N in Nmin(M,1) .. Nmax(M) loop
        if H.J = 0 then
          Mult(Rep(-M)*S1+Rep(-N)*S2,H.C(M,N));
        else
          Mult(Rep(M)*S1+Rep(N)*S2,H.C(M,N));
        end if;
      end loop;
    end loop;
    SetZero(H.C(0,0));
  end DqDot;

  procedure Gig(Sig: in Rep; H: in out Fourier) is
--  Sig defines a cutoff a la Iminus, unless H.P
  begin
    if not IsZero(H.C(0,0)) then
      Message("Fouriers.Ops.Gig",Domain_Violation);
    end if;
    for M in Freq1 loop
      for N in Nmin(M,1) .. Nmax(M) loop
        if Deg(H.C(M,N))>=0 then
          Mult((Scal(M)+Rep(-N)*InvGold)/(Rep(M)*InvGold+Scal(N)),H.C(M,N));
        end if;
      end loop;
    end loop;
    SetZero(H.C(0,0));
    if H.P then return; end if;

    for I in FreqE loop
      Mult(ScalOne/Sig,H.E(I));            -- operator_norm <= 1/Sig
    end loop;
  end Gig;

  procedure Gig(Sig: in Rep; H1: in Fourier; H2: out Fourier) is
--  Sig defines a cutoff a la Iminus, unless H1.P
  begin
    if not IsZero(H1.C(0,0)) then
      Message("Fouriers.Ops.Gig",Domain_Violation);
    end if;
    H2.P := H1.P;
    H2.J := H1.J;
    H2.R := H1.R;
    for M in Freq1 loop
      for N in Nmin(M,1) .. Nmax(M) loop
        if Deg(H1.C(M,N))>=0 then
          Prod((Scal(M)+Rep(-N)*InvGold)/(Rep(M)*InvGold+Scal(N)),H1.C(M,N),H2.C(M,N));
        else
          SetZero(H2.C(M,N));
        end if;
      end loop;
    end loop;
    SetZero(H2.C(0,0));
    if Trunc then return; end if;

    if H1.P then
      ResetErrComp(H2);
      return;
    end if;

    for I in FreqE loop
      Prod(ScalOne/Sig,H1.E(I),H2.E(I));   -- operator_norm <= 1/Sig
    end loop;
  end Gig;

  function Gig(Sig: in Rep; H: Fourier) return Fourier is
    Hr: Fourier;
  begin
    Gig(Sig,H,Hr);
    return Hr;
  end Gig;

  procedure Dig(Kap: in Rep; H: in out Fourier) is
--  Kap defines a cutoff a la Iminus, unless H.P
    KapInv: constant Radius := SupAbs(ScalOne/Kap);
  begin
    if not IsZero(H.C(0,0)) then
      Message("Fouriers.Ops.Dig",Domain_Violation);
    end if;
    if H.J /= 0 then
      Message("Fouriers.Ops.Dig",Not_Implemented);
    end if;

    H.J := 1;
    for M in Freq1 loop
      for N in Nmin(M,1) .. Nmax(M) loop
        DerMult(H.R.P,KapInv,Inv(Rep(M)*InvGold+Scal(N)),H.C(M,N),H.P);
      end loop;
    end loop;
    SetZero(H.C(0,0));
    if H.P then return; end if;

    for I in FreqE loop
      DerMult(H.R.P,KapInv,ScalOne/Kap,H.E(I));
    end loop;
  end Dig;

  procedure Dig(Kap: in Rep; H1: in Fourier; H2: out Fourier) is
--  Kap defines a cutoff a la Iminus, unless H1.P
    KapInv: constant Radius := SupAbs(ScalOne/Kap);
  begin
    if not IsZero(H1.C(0,0)) then
      Message("Fouriers.Ops.Dig",Domain_Violation);
    end if;
    if H1.J /= 0 then
      Message("Fouriers.Ops.Dig",Not_Implemented);
    end if;
    H2.P := H1.P;
    H2.J := 1;
    H2.R := H1.R;
    for M in Freq1 loop
      for N in Nmin(M,1) .. Nmax(M) loop
        DerProd(H1.R.P,KapInv,Inv(Rep(M)*InvGold+Scal(N)),H1.C(M,N),H2.C(M,N),H1.P);
      end loop;
    end loop;
    SetZero(H2.C(0,0));
    if Trunc then return; end if;

    if H1.P then
      ResetErrComp(H2);
      return;
    end if;

    for I in FreqE loop
      DerProd(H1.R.P,KapInv,ScalOne/Kap,H1.E(I),H2.E(I));
    end loop;
  end Dig;

  function Dig(Kap: in Rep; H: Fourier) return Fourier is
    Hr: Fourier;
  begin
    Dig(Kap,H,Hr);
    return Hr;
  end Dig;

--- sobolev stuff

  procedure SetComponent(M: in Freq1; N: in Freq2; S: in Scalar; H: in out Sobolev) is
  begin
    if InRange(M,N) and (H.J=0 or else M /= 0 or else N /= 0 or else S=ScalZero) then
      H.C(M,N) := S;
    else
      Message("Fouriers.Ops.SetComponent",Index_Error);
    end if;
  end SetComponent;

  procedure AddComponent(M: in Freq1; N: in Freq2; S: in Scalar; H: in out Sobolev) is
  begin
    if InRange(M,N) and (H.J=0 or else M /= 0 or else N /= 0 or else S=ScalZero) then
      H.C(M,N) := H.C(M,N)+S;
    else
      Message("Fouriers.Ops.AddComponent",Index_Error);
    end if;
  end AddComponent;

  procedure AddErrComp(I: in FreqE; S: in Scalar; H: in out Sobolev) is
  begin
    if Trunc then return; end if;
    H.E(I) := H.E(I)+Abs(S);
  end AddErrComp;

  procedure AddBall(S: in Scalar; H: in out Sobolev) is
  begin
    if Trunc or else S=ScalZero then return; end if;
    H.E(0) := H.E(0)+Abs(S);
  end AddBall;

  function Coeff(M,N: Integer; H: Sobolev) return Scalar is
    C: Scalar := ScalZero;
  begin
    if not Trunc then
      for I in 0 .. Ierr(M,N) loop
        C := C+H.E(I);
      end loop;
      C := Ball(C/NormFactor2(H.R,M,N));
    end if;
    if InRange(M,N) then
      C := C+H.C(M,N);
    end if;
    return C;
  end Coeff;

  procedure ZeroConstant(H: in out Sobolev) is
  begin
    H.C(0,0) := ScalZero;
  end ZeroConstant;

  procedure UpdateSavedFactors2(R: in Radius) is
  begin
    if R=SavedFactors2.R then return; end if;
    SavedFactors2.R := R;
    for M in FreqE1 loop
      for N in FreqE2 loop
        SavedFactors2.C(M,N) := OnePlus*Exp(R*Log(ScalOne+Abs(Scal(M)+Rep(-N)*InvGold)));
      end loop;
    end loop;
  end UpdateSavedFactors2;

  function NormFactor2(R: Radius; M,N: Integer; Update: in Boolean := False) return Scalar is
  begin
    if N<0 then
      return NormFactor2(R,-M,-N,Update);
    elsif R=SavedFactors2.R then
      if M in FreqE1 and then N in FreqE2 then
        return SavedFactors2.C(M,N);
      end if;
    elsif Update then
      UpdateSavedFactors2(R);
    end if;
    return OnePlus*Exp(R*Log(ScalOne+Abs(Scal(M)+Rep(-N)*InvGold)));
  end NormFactor2;

  function Norm(R: Radius; H: Sobolev) return Scalar is
    E: Scalar := ScalZero;
  begin
    if not Trunc then
      for I in FreqE loop
        E := E+H.E(I);
      end loop;
    end if;

    if R=SavedFactors2.R then
      for M in Freq1 loop
        for N in Nmin(M,H.J) .. Nmax(M) loop
          E := E+SavedFactors2.C(M,N)*Abs(H.C(M,N));
        end loop;
      end loop;
    else
      for M in Freq1 loop
        for N in Nmin(M,H.J) .. Nmax(M) loop
          E := E+OnePlus*Exp(R*Log(ScalOne+Abs(Scal(M)+Rep(-N)*InvGold)))*Abs(H.C(M,N));
        end loop;
      end loop;
    end if;
    return E;
  end Norm;

  function Norm(H: Sobolev) return Scalar is
  begin
    return Norm(H.R,H);
  end Norm;

  procedure Neg(H1: in Sobolev; H2: out Sobolev) is
  begin
    H2.J := H1.J;
    H2.R := H1.R;
    for M in Freq1 loop
      for N in Nmin(M) .. Nmax(M) loop
        H2.C(M,N) := -H1.C(M,N);
      end loop;
    end loop;
    if Trunc then return; end if;
    H2.E := H1.E;
  end Neg;

  function "-"(H: Sobolev) return Sobolev is
    Ht: Sobolev;
  begin
    Neg(H,Ht);
    return Ht;
  end "-";

  procedure Mult(S: in Scalar; H: in out Sobolev) is
  begin
    for M in Freq1 loop
      for N in Nmin(M) .. Nmax(M) loop
        H.C(M,N) := S*H.C(M,N);
      end loop;
    end loop;
    if Trunc then return; end if;
    for I in FreqE loop
      H.E(I) := Abs(S)*H.E(I);
    end loop;
  end Mult;

  procedure Prod(S: in Scalar; H1: in Sobolev; H2: out Sobolev) is
  begin
    H2.J := H1.J;
    H2.R := H1.R;
    for M in Freq1 loop
      for N in Nmin(M) .. Nmax(M) loop
        H2.C(M,N) := S*H1.C(M,N);
      end loop;
    end loop;
    if Trunc then return; end if;
    for I in FreqE loop
      H2.E(I) := Abs(S)*H1.E(I);
    end loop;
  end Prod;

  function "*"(S: Scalar; H: Sobolev) return Sobolev is
    Ht: Sobolev;
  begin
    Prod(S,H,Ht);
    return Ht;
  end "*";

  procedure Add(H1: in Sobolev; H2: in out Sobolev) is
  begin
    if H1.J /= H2.J then
      Message("Fouriers.Ops.Add",Parity_Error);
    end if;
    H2.R := Radius'Min(H1.R,H2.R);
    for M in Freq1 loop
      for N in Nmin(M) .. Nmax(M) loop
        H2.C(M,N) := H2.C(M,N)+H1.C(M,N);
      end loop;
    end loop;
    if Trunc then return; end if;
    for I in FreqE loop
      H2.E(I) := H2.E(I)+H1.E(I);
    end loop;
  end Add;

  procedure Sum(H1,H2: in Sobolev; H3: out Sobolev) is
  begin
    if H1.J /= H2.J then
      Message("Fouriers.Ops.Sum",Parity_Error);
    end if;
    H3.J := H1.J;
    H3.R := Radius'Min(H1.R,H2.R);
    for M in Freq1 loop
      for N in Nmin(M) .. Nmax(M) loop
        H3.C(M,N) := H1.C(M,N)+H2.C(M,N);
      end loop;
    end loop;
    if Trunc then return; end if;
    for I in FreqE loop
      H3.E(I) := H1.E(I)+H2.E(I);
    end loop;
  end Sum;

  function "+"(H1,H2: Sobolev) return Sobolev is
    Hr: Sobolev;
  begin
    Sum(H1,H2,Hr);
    return Hr;
  end "+";

  procedure Sub(H1: in Sobolev; H2: in out Sobolev) is
  begin
    if H1.J /= H2.J then
      Message("Fouriers.Ops.Sub",Parity_Error);
    end if;
    H2.R := Radius'Min(H1.R,H2.R);
    for M in Freq1 loop
      for N in Nmin(M) .. Nmax(M) loop
        H2.C(M,N) := H2.C(M,N)-H1.C(M,N);
      end loop;
    end loop;
    if Trunc then return; end if;
    for I in FreqE loop
      H2.E(I) := H2.E(I)+H1.E(I);
    end loop;
  end Sub;

  procedure Diff(H1,H2: in Sobolev; H3: out Sobolev) is
  begin
    if H1.J /= H2.J then
      Message("Fouriers.Ops.Diff",Parity_Error);
    end if;
    H3.J := H1.J;
    H3.R := Radius'Min(H1.R,H2.R);
    for M in Freq1 loop
      for N in Nmin(M) .. Nmax(M) loop
        H3.C(M,N) := H1.C(M,N)-H2.C(M,N);
      end loop;
    end loop;
    if Trunc then return; end if;
    for I in FreqE loop
      H3.E(I) := H1.E(I)+H2.E(I);
    end loop;
  end Diff;

  function "-"(H1,H2: Sobolev) return Sobolev is
    Hr: Sobolev;
  begin
    Diff(H1,H2,Hr);
    return Hr;
  end "-";

  procedure MultAdd(S: in Scalar; H1: in Sobolev; H2: in out Sobolev) is
  begin
    if S=ScalZero then return; end if;
    if H1.J /= H2.J then
      Message("Fouriers.Ops.MultAdd",Parity_Error);
    end if;
    H2.R := Radius'Min(H1.R,H2.R);
    for M in Freq1 loop
      for N in Nmin(M) .. Nmax(M) loop
        H2.C(M,N) := H2.C(M,N)+S*H1.C(M,N);
      end loop;
    end loop;
    if Trunc then return; end if;
    for I in FreqE loop
      H2.E(I) := H2.E(I)+Abs(S)*H1.E(I);
    end loop;
  end MultAdd;

  procedure MultAdd(S1: in Scalar; M,N: in Integer; H2: in Sobolev; H3: in out Sobolev) is
    J1: constant Parity := (H2.J+H3.J) mod 2;
    Flip: Boolean;
    M1,M3,N1,N3,I1,I3: Integer;
    Cp,Cm1,Cm2,F: Scalar;
  begin
    if S1=ScalZero then return; end if;
    Normalize(M,N,M1,N1,Flip);           --- (M,N) need not be inrange
    if Flip and then J1=1 then
      Cp := (-Half)*S1;
    else
      Cp := Half*S1;
    end if;
    Cm1 := Cp;
    Cm2 := Cp;
    if J1 < H2.J then
      Cm1 := -Cp;
    elsif J1 > H2.J then
      Cm2 := -Cp;
    elsif J1 = 1 then
      Cp := -Cp;
    end if;

    if not Trunc then
      if H3.R > H2.R then
        Message("Fouriers.Ops.MultAdd",Domain_Violation);
      end if;
      UpdateSavedFactors2(H3.R);
    end if;

    for M2 in Freq1 loop
      for N2 in Nmin(M2,H2.J) .. Nmax(M2) loop
        if H2.C(M2,N2) /= ScalZero then
          Normalize(M1+M2,N1+N2,M3,N3,Flip);
          if InRange(M3,N3) then                                             --- frequency sum
            H3.C(M3,N3) := H3.C(M3,N3)+Cp*H2.C(M2,N2);
          elsif not Trunc then
            I3 := Ierr(M3,N3);
            H3.E(I3) := H3.E(I3)+Abs(Cp*H2.C(M2,N2))*SavedFactors2.C(M3,N3);
          end if;

          Normalize(M1-M2,N1-N2,M3,N3,Flip);
          if InRange(M3,N3) then                                             --- frequency difference
            if not Flip then                                                 --- 1st minus 2nd frequency
              H3.C(M3,N3) := H3.C(M3,N3)+Cm1*H2.C(M2,N2);
            else                                                             --- 2nd minus 1st frequency
              H3.C(M3,N3) := H3.C(M3,N3)+Cm2*H2.C(M2,N2);
            end if;
          elsif not Trunc then
            I3 := Ierr(M3,N3);
            H3.E(I3) := H3.E(I3)+Abs(Cp*H2.C(M2,N2))*SavedFactors2.C(M3,N3);
          end if;

        end if;
      end loop;
    end loop;

    if H3.J=1 then
      H3.C(0,0) := ScalZero;
    end if;
    if Trunc then return; end if;

    if M1=0 and then N1=0 then I1 := -1; else I1 := Ierr(M1,N1); end if;
    F := Abs(S1)*SavedFactors2.C(M1,N1);
    for I2 in FreqE loop
      I3 := Imax(0,I2-I1-1);
      H3.E(I3) := H3.E(I3)+F*H2.E(I2);                                       --- add coeff*errors
    end loop;
  end MultAdd;

  procedure MultAdd(S: in Scalar; H1,H2: in Sobolev; H3: in out Sobolev) is
    I2,I3: Integer;
    F: Scalar;
    E1,E2: Scalar := ScalZero;
  begin
    if not Trunc then
      if (H3.R > H1.R) or (H3.R > H2.R) then
        Message("Fouriers.Ops.MultAdd",Domain_Violation);
      end if;
      UpdateSavedFactors2(H3.R);
    end if;

    for M in Freq1 loop
      for N in Nmin(M,H1.J) .. Nmax(M) loop
        if H1.C(M,N) /= ScalZero then
          MultAdd(S*H1.C(M,N),M,N,H2,H3);                                  --- includes coeffs*errors
        end if;
      end loop;
    end loop;
    if Trunc then return; end if;

    UpdateSavedFactors2(H3.R);
    for M in Freq1 loop
      for N in Nmin(M,H2.J) .. Nmax(M) loop
        if M=0 and then N=0 then I2 := -1; else I2 := Ierr(M,N); end if;
        F := Abs(S*H2.C(M,N))*SavedFactors2.C(M,N);
        for I1 in FreqE loop
          I3 := Imax(0,I1-I2-1);
          H3.E(I3) := H3.E(I3)+H1.E(I1)*F;                                   --- add errors*coeffs
        end loop;
      end loop;
    end loop;

    for I in reverse FreqE loop
      E1 := E1+H1.E(I);
      E2 := E2+H2.E(I);
    end loop;
    H3.E(0) := H3.E(0)+Abs(S)*E1*E2;                                         --- add errors*errors
  end MultAdd;

  procedure Mult(H1: in Sobolev; H2: in out Sobolev) is
    Ht: Sobolev;
  begin
    Ht := H2;
    SetZero((H1.J+Ht.J) mod 2,Radius'Min(H1.R,Ht.R),H2);
    MultAdd(ScalOne,H1,Ht,H2);
  end Mult;

  procedure Prod(S: in Scalar; H1,H2: in Sobolev; H3: out Sobolev) is
  begin
    SetZero((H1.J+H2.J) mod 2,Radius'Min(H1.R,H2.R),H3);
    MultAdd(S,H1,H2,H3);
  end Prod;

  procedure Prod(H1,H2: in Sobolev; H3: out Sobolev) is
  begin
    SetZero((H1.J+H2.J) mod 2,Radius'Min(H1.R,H2.R),H3);
    MultAdd(ScalOne,H1,H2,H3);
  end Prod;

  function "*"(H1,H2: Sobolev) return Sobolev is
    Hr: Sobolev;
  begin
    SetZero((H1.J+H2.J) mod 2,Radius'Min(H1.R,H2.R),Hr);
    MultAdd(ScalOne,H1,H2,Hr);
    return Hr;
  end "*";

  procedure Powers(H: in Sobolev; Hp: in SobVecPtr) is
    I: Integer;
  begin
    if H.J /= 0 then
      Message("Fouriers.Ops.Powers", Parity_Error);
    end if;
    Hp(1) := H;
    I := 1;
    for K in 2 .. Hp'Last loop
      Prod(Hp(I),Hp(K-I),Hp(K));
      if I+I=K then I := K; end if;
    end loop;
  end Powers;

  procedure Comp(IsPoly: in Boolean; R: in Radius; P: in Taylor; Hp: in SobVecPtr; Hr: out Sobolev) is
--- Hp(K) is assumed to be the K-th power of Hp(1)
    S,C,E: Scalar;
  begin
--    TraceEnter("Comp");
    SetZero(0,Hp(1).R,Hr);
    if Deg(P)<0 then
--      TraceLeave;
      return;
    end if;

    if Hp(1).J /= 0 then
      Message("Fouriers.Ops.Comp",Not_Implemented);
    end if;

    if IsPoly then
      for K in reverse 1 .. Deg(P) loop
        MultAdd(Component(K,P),Hp(K),Hr);
      end loop;
      Hr.C(0,0) := Hr.C(0,0)+Component(0,P);
--      TraceLeave;
      return;
    end if;

    S := Norm(Hp(1));
    if Sup(S)>R then
      Message("Fouriers.Ops.Comp",Domain_Violation);
    end if;

    for K in reverse Imax(Dho,1) .. Deg(P) loop
      Split(Component(K,P),C,E);
      MultAdd(C,Hp(K),Hr);
      Hr.E(0) := Hr.E(0)+SupAbs(E)*(S**K);
    end loop;
    for K in reverse 1 .. Dho-1 loop
      MultAdd(Component(K,P),Hp(K),Hr);
    end loop;
    if Dho=0 then
      Split(Component(0,P),C,E);
      Hr.C(0,0) := Hr.C(0,0)+C;
      Hr.E(0) := Hr.E(0)+Abs(E);
    else
      Hr.C(0,0) := Hr.C(0,0)+Component(0,P);
    end if;
    Hr.E(0) := Hr.E(0)+ErrComp(P);
--    TraceLeave;
  end Comp;

  procedure CompTinv(H1: in Sobolev; H2: out Sobolev) is
-- errors->errors estimate not optimal
    M2,N2,I2: Integer;
    Flip: Boolean;
  begin
    SetZero(H1.J,H1.R,H2);
    for M1 in Freq1 loop
      for N1 in Nmin(M1,H1.J) .. Nmax(M1) loop
        Normalize(N1-M1,M1,M2,N2,Flip);
        if InRange(M2,N2) then
          if Flip and then H1.J=1 then
            H2.C(M2,N2) := -H1.C(M1,N1);
          else
            H2.C(M2,N2) := H1.C(M1,N1);
          end if;
        elsif not Trunc then
          I2 := Ierr(M2,N2);
          H2.E(I2) := H2.E(I2)+Abs(H1.C(M1,N1))*NormFactor2(H1.R,M2,N2);
        end if;
      end loop;
    end loop;
    if Trunc then return; end if;

    for I1 in FreqE loop
      I2 :=IntCeiling(Rep(I1)*InvGold);
      H2.E(I2) := Gold*H1.E(I1);
    end loop;
  end CompTinv;

  procedure CosSin(H: in Sobolev; Hw1,Hw2,Hc,Hs: out Sobolev) is
--- uses Hw1 and Hw2 as workspace only
    Iter: constant Integer := 6;
    Small: constant Rep := Half;
    K: Natural := 0;
    S,E,Ec,Es: Scalar;
  begin
    E := Norm(H);
    while Sup(E)>Small loop
      K := K+1;
      E := Half*E;
    end loop;
    SetZero(0,H.R,Hc);
    Hc.C(0,0) := Hc.C(0,0)+ScalOne;
    S := Scal(Half**K);
    Prod(S,H,Hs);
    Hw1 := Hs;
    Ec := E**(2*Iter+1);
    for I in 1 .. Iter loop                                   -- Taylor polynomials
      Prod(S,H,Hw1,Hw2);
      Mult(Inv(Scal(-2*I)),Hw2);
      Add(Hw2,Hc);
      Prod(S,H,Hw2,Hw1);
      Mult(Inv(Scal(2*I+1)),Hw1);
      Add(Hw1,Hs);
      Ec := Ec/Rep(2*I*(2*I+1));
    end loop;
    if not Trunc then
      Ec := E*Ec/Rep(2*Iter+2);
      Es := E*Ec/Rep(2*Iter+3);
      Ec := Ec/(ScalOne-Sqr(E)/Rep((2*Iter+3)*(2*Iter+4)));
      Hc.E(0) := Hc.E(0)+Ec;                                  -- Remainder for Cos(H)
      Es := Es/(ScalOne-Sqr(E)/Rep((2*Iter+4)*(2*Iter+5)));
      Hs.E(0) := Hs.E(0)+Es;                                  -- Remainder for Sin(H)
    end if;
    for I in 1 .. K loop                                      -- double angle formula, K times
      Prod(Hs,Hs,Hw1);
      Prod(Hs,Hc,Hw2);
      Prod(ScalTwo,Hw2,Hs);
      Prod(Hc,Hc,Hw2);                                        -- could avoid one multiplication
      Diff(Hw2,Hw1,Hc);
    end loop;
  end CosSin;

  procedure BetaGuessX(Err: in Radius; S,Rq,R: in Rep; XL,XR: out Rep) is
--- Guess where Beta takes its maximum value
  use Reps.Ops;
  F: constant Rep := 1.2;
  X0,XM,YL,YM,YR: Rep;

    function DBetaSign(X: Rep) return Rep is
    begin
      return (S+R/(One+X))*Cosh(Rq*X)-Rq*Sinh(Rq*X);
    end DBetaSign;

  begin
    X0 := Log((Rq+S)/(Rq-S))/(Two*Rq);  -- minimizer when R=0

    XR := X0;
    YR := DBetaSign(XR);
    while YR>Zero loop
      XR := XR*F;
      YR := DBetaSign(XR);
    end loop;
    XL := X0;
    YL := DBetaSign(XL);
    while YL<Zero loop
      XL := XL/F;
      YL := DBetaSign(XL);
    end loop;

    while XR-XL>Err loop
      XM := Half*(XL+XR);
      YM := DBetaSign(XM);
      if YM>Zero then
        XL := XM;
        YL := YM;
      elsif YM<Zero then
        XR := XM;
        YR := YM;
      else
        XL := XM;
        XR := XM;
        return;
      end if;
    end loop;
  end BetaGuessX;

  function Beta(Rq,R: Rep; S,T: Scalar) return Scalar is
  begin
    return Exp(R*Log(ScalOne+T)+S*T)/Cosh(Rq*T);
  end Beta;

  function BetaMax(Rq,R: Rep; S: Scalar) return Scalar is
    Err: constant Radius := 1.0E-5;
    Eps:  constant Radius := 1.0E-4;
    XL,XR: Rep;
    XM: Scalar;

    function DBetaSign(X: Scalar) return Scalar is
    begin
      return (S+R*Inv(ScalOne+X))*Cosh(Rq*X)-Rq*Sinh(Rq*X);
    end DBetaSign;

  begin
    if Sup(S) >= Rq then
      Message("RG_Ops.BetaMax 1",Parameter_Error);
    end if;
    BetaGuessX(Err,Approx(S),Rq,R,XL,XR);
    XM := Scal(Half*(XR+XL))+Ball(Half*(XR-XL)+Eps);
    if not Trunc and then (Inf(DBetaSign(Scal(Inf(XM))))<Zero or Sup(DBetaSign(Scal(Sup(XM))))>Zero) then
      Message("RG_Ops.BetaMax 2",Parameter_Error);
    end if;
    return Beta(Rq,R,S,XM);
  end BetaMax;

  procedure IncCS(Hc1,Hs1: in Sobolev; Hwc,Hws: out Sobolev; Hc2,Hs2: in out Sobolev) is
--- make Hc2 := cos(H2+H1), Hs2 := sin(H2+H1)
--- from Hc1=cos(H1), Hs1=sin(H1), Hc2=cos(H2), Hs2=sin(H2)
--- uses Hwc and Hws as workspace only
  begin
    Hwc := Hc2;
    Hws := Hs2;
    Prod(Hc1,Hws,Hs2);
    MultAdd(ScalOne,Hs1,Hwc,Hs2);
    Prod(Hc1,Hwc,Hc2);
    Multadd(-ScalOne,Hs1,Hws,Hc2);
  end IncCS;

  procedure CompOmega3(H1,H2: in Fourier; Gq,Gp: in Sobolev; G1,G2: out Sobolev) is
--- domains get checked in Comp and BetaMax
    Rg: constant Radius := Radius'Min(Gq.R,Gp.R);
    D1,D2,Dm,Dn: Integer;
    M: Freq1;
    S: Radius;
    Gw1,Gw2,Gc1,Gs1,Gc2,Gs2,Gmc,Gms,Gmnc,Gmns: Sobolev;
    Gpp: SobVecPtr := new SobVec(1 .. Pdeg);
  begin
    TraceEnter("CompOmega3 fss");
    if (Gq.J /= 1) or (Gp.J /= 0) then
      Message("Fouriers.Ops.CompOmega3",Not_Implemented);
    end if;
    SetZero(H1.J,Rg,G1);
    SetZero(H2.J,Rg,G2);
    S := Sup(Norm(Rg,Gq));
    if not H1.P then
      for I in FreqE loop
         G1.E(0) := G1.E(0)+Norm(S,H1.E(I));
      end loop;
      G1.E(0) := G1.E(0)*BetaMax(H1.R.Q,Rg,Scal(S));
    end if;
    if not H2.P then
      for I in FreqE loop
         G2.E(0) := G2.E(0)+Norm(S,H2.E(I));
      end loop;
      G2.E(0) := G2.E(0)*BetaMax(H2.R.Q,Rg,Scal(S));
    end if;
    EffDeg(H1,D1);
    EffDeg(H2,D2);
    Dm := Imax(D1,D2);
    if Dm<0 then
      TraceLeave;
      return;
    end if;

    CosSin(Gq,Gw1,Gw2,Gc1,Gs1);
    CosSin((-InvGold)*Gq,Gw1,Gw2,Gc2,Gs2);
    SetZero(0,Rg,Gmc);
    Gmc.C(0,0) := ScalOne;
    SetZero(1,Rg,Gms);
    Powers(Gp,Gpp);

    for Mabs in 0 .. Dm loop
      if Mabs>0 then
        IncCS(Gc1,Gs1,Gw1,Gw2,Gmc,Gms);
      end if;
      for I in 0 .. 1 loop
        M := (2*I-1)*Mabs;
        Gms := -Gms;
        if M<0 or else I=1 then
          Gmnc := Gmc;
          Gmns := Gms;
          EffDeg(M,H1,D1);
          EffDeg(M,H2,D2);
          Dn := Imax(D1,D2);
          for N in Nmin(M) .. Dn loop
            if N>0 then
              IncCS(Gc2,Gs2,Gw1,Gw2,Gmnc,Gmns);
            end if;
            SetZero(H1.J,Rg,Gw1);
            MultAdd(ScalOne,M,N,Gmnc,Gw1);
            MultAdd(Scal(2*H1.J-1),M,N,Gmns,Gw1);
            Comp(H1.P,H1.R.P,H1.C(M,N),Gpp,Gw2);
            MultAdd(ScalOne,Gw1,Gw2,G1);

            SetZero(H2.J,Rg,Gw1);
            MultAdd(ScalOne,M,N,Gmnc,Gw1);
            MultAdd(Scal(2*H2.J-1),M,N,Gmns,Gw1);
            Comp(H2.P,H2.R.P,H2.C(M,N),Gpp,Gw2);
            MultAdd(ScalOne,Gw1,Gw2,G2);
          end loop;
        end if;
      end loop;
    end loop;
    TraceLeave;
  end CompOmega3;

----- procedures that are implemented only numerically

  procedure NumInv(Err: in Radius; H1: in Fourier; H2: out Fourier) is
---  iterate h2 -> h2*[2-h1*h2]
    S,E: Scalar;
    Hw1,Hw2: Fourier;
  begin
    if not Trunc then
      Message("Fouriers.Ops.NumInv",Numeric_Only);
    end if;
    S := Component(0,0,0,H1);
    if S /= ScalZero then
      Prod(-Inv(Sqr(S)),H1,H2);
      AddComponent(0,0,0,ScalTwo/S,H2);
      for I in 1 .. 30 loop
        Prod(H1.R,H1,H2,Hw1);
        AddComponent(0,0,0,-ScalOne,Hw1);
        E := Norm(H1.R,Hw1);
        AddComponent(0,0,0,-ScalOne,Hw1);
        Prod(H1.R,H2,Hw1,Hw2);
        Neg(Hw2,H2);
        if Sup(E) <= Err then
          return;
        end if;
      end loop;
      Message("Fouriers.Ops.NumInv",Giving_Up);
    else
      Message("Fouriers.Ops.NumInv",Division_By_Zero);
    end if;
  end NumInv;

  function NumInv(Err: Radius; H: Fourier) return Fourier is
    Hr: Fourier;
  begin
    NumInv(Err,H,Hr);
    return Hr;
  end NumInv;

  procedure NumInvComp3(Err: in Radius; H1,H2: in Fourier; H3: out Fourier) is
--- iterate h3 -> h3-[h2(..,h3)-h1]/(D_3 h2)(..,h3)
    R: Weights;
    E: Scalar;
    H,Hd,Hd2,Hd3: Fourier;
  begin
    TraceEnter("NumInvComp3");
    if not Trunc then
      Message("Fouriers.Ops.NumInvComp3",Numeric_Only);
    end if;
    if H1.J /= H2.J then
      Message("Fouriers.Ops.NumInvComp3",Not_Implemented);
    end if;
    R := Min(H1.R,H2.R);                                    -- arbitrary
    SetZero(0,R,H3);
    InvComp(R.P,Err,H1.C(0,0),H2.C(0,0),H3.C(0,0));
    Der3(R.P,H2,H);
    NumInv(Err,H,Hd2);                                      -- need not be that precise
    for I in 1 .. 20 loop
      Comp3(H2,H3,H);
      Sub(H1,H);
      Comp3(Hd2,H3,Hd);
      Prod(R,H,Hd,Hd3);
      Sub(Hd3,H3);
      E := Norm(R,Hd3);
      if Sup(E) <= Err then
        TraceLeave;
        return;
      end if;
    end loop;
    Message("Fouriers.Ops.NumInvComp3",Giving_Up);
    TraceLeave;
  end NumInvComp3;

  procedure NumFixComp3(Err: in Radius; H: in Fourier; Hz: out Fourier) is
    H1,H2: Fourier;
  begin
    Copy(H,H2);
    AddComponent(0,0,1,ScalOne,H2);
    SetZero(0,H.R,H1);
    SetComponent(0,0,1,ScalOne,H1);
    NumInvComp3(Err,H1,H2,Hz);
--    AddComponent(0,0,1,-ScalOne,Hz);    -- originally last line
    Comp3(H,Hz,H1);                     -- only for contractions
    Neg(H1,Hz);                         -- only for contractions
  end NumFixComp3;

  function NumFixComp3(Err: Radius; H: Fourier) return Fourier is
    Hz: Fourier;
  begin
    NumFixComp3(Err,H,Hz);
    return Hz;
  end NumFixComp3;

  procedure NumInvAplus1(Err: in Radius; L: in Degrees; Hdq,Hdp,F: in Fourier; Psi: out Fourier) is
--- solve  (I-)[A+1]Psi=(I-)F  by iteration Psi -> (I-)[F-A(Psi)]
--- where A(Psi)=Hdp*Gig(Psi)-Hdq*Dig(Psi)
    Iter: constant Integer := 50;
    I: Integer := 1;
    E: Scalar;
    D: Degrees(Freq1,Freq2);
    Psid,Psit: Fourier;
  begin
    if not Trunc then
      Message("Fouriers.Ops.NumInvAplus1",Numeric_Only);
    end if;
    for M in Freq1 loop
      for N in Nmin(M) .. Nmax(M) loop
        D(M,N) := Imin(L(M,N),Dmax(M,N));
      end loop;
    end loop;
    Copy(F,Psit);
    Iminus(L,Psit);                      -- start with Psit := (I-)F
    loop
      Copy(F,Psi);
      Dig(Small,Psit,Psid);
      MultAdd(ScalOne,Hdq,Psid,Psi,D);   -- add (I-)*Hdq*Dig(Psit)
      Gig(Small,Psit,Psid);
      MultAdd(-ScalOne,Hdp,Psid,Psi,D);  -- subtract (I-)*Hdp*Gig(Psit)
      E := Norm(Psi-Psit);
      exit when I=Iter or Sup(E)<Err;
      Copy(Psi,Psit);
      I := I+1;
    end loop;
    if I=Iter then
      Message("RG_Ops.NumInvAplus1",Giving_Up);
    end if;
    Show("NumInvAplus1: ",I,False);
    Show("  |Psi-Psit| =",Sup(E));
  end NumInvAplus1;

  function NumInvAplus1(Err: Radius; L: Degrees; Hdq,Hdp,F: Fourier) return Fourier is
    Psi: Fourier;
  begin
    NumInvAplus1(Err,L,Hdq,Hdp,F,Psi);
    return Psi;
  end NumInvAplus1;

end Fouriers.Ops;