File : zeros.adb


with Messages;
use Messages;

package body Zeros is

  Iter: constant Integer := 30;

  function FindZero(S0,S1,S2: Number; Eps1,Eps2: Radius) return Number is
    FoundPos,FoundNeg: Boolean := False;
    N: Integer := 8;
    S: Number := S0;
    Err: Rep;
    SP,SN,DS,DFS,Rad,K: Number;
  begin

    if not Z_Trunc then                     --- just add error estimate to the guess S0
      S := Center(S);
      DFS := DF(S);
      DS := F(S)/DFS;
      Rad := Rep(N+1)*Norm(DS)/Rep(N);      --- radius of ball
      S := S+DiskAt0(Rad);                  --- ball
      K := Scal_One-DF(S)/DFS;              --- contraction factor
      if MaxNorm(Rep(N+1)*K)>One then
        Error("Zeros.FindZero error: no contraction");
      end if;
      return S;
     end if;

    if S=Scal_Zero then                      --- bisection
      for I in 1 .. Iter loop
        if (I mod 2)=0 then N := I/2; else N := -(I/2); end if;
        S := Scal(1-N)*S1+Scal(N)*S2;
        if F(S) >= Scal_Zero then
          FoundPos := True;
          SP := S;
          exit when FoundNeg;
        else
          FoundNeg := True;
          SN := S;
          exit when FoundPos;
        end if;
      end loop;
      if not (FoundPos and FoundNeg) then
        Error("Zeros.FindZero error: no zero found");
      end if;
      for I in 1 .. Iter loop
        Err := MaxNorm(SP-SN);
        S := (SP+SN)/Scal_Two;
        exit when Err<Eps1;
        if F(S) >= Scal_Zero then SP := S; else SN := S; end if;
        if F(S) >= Scal_Zero then SP := S; else SN := S; end if;
      end loop;
      if Err>Eps1 then
        Show("Zeros.FindZero: bisection precision only",Err,1);
      end if;
    end if;

    for I in 1 .. Iter loop                 --- Newton
      DS := F(S)/DF(S);
      Err := MaxNorm(DS);
      S := S-DS;
      exit when Err<Eps2;
    end loop;
    if Err>Eps2 then
      Show("Zeros.FindZero: Newton precision only",Err,1);
    end if;
    return S;

  end FindZero;

end Zeros;