File : balls.adb


with Reps.Ops, Reps.IO, Rounding;
use Reps.IO, Rounding;

pragma Optimize (Off);
pragma Elaborate_All (Rounding);

package body Balls is

--- basic

  function IsNumeric(S: Ball) return Boolean is
  begin
    return False;
  end IsNumeric;

  function IsSharp(S: Ball) return Boolean is
  begin
    return (S.R=Zero) and then (S.B=Zero);
  end IsSharp;

  function IsNumbers(S: Ball) return Boolean is
  begin
    return (S.B=Zero);
  end IsNumbers;

  procedure ModNumbers(S: in out Ball) is
  begin
    S.C := Zero;
    S.R := Zero;
  end ModNumbers;

  procedure ToBalls(S: in out Ball) is
  begin
    S.B := (abs(S.C)+S.R)+S.B;
    S.R := Zero;
    S.C := Zero;
  end ToBalls;

  function Approx(S: Ball) return Rep is
  begin
    return S.C;
  end Approx;

--- sets

  function Center0(S: Ball) return Boolean is
  begin
    return (S.C=Zero);
  end Center0;

  function Contains0(S: Ball) return Boolean is
    RB: constant Rep := S.R+S.B;
  begin
    if abs(S.C)>RB then return False; end if;
    if (abs(S.C)-RB)>Zero then raise Undefined; end if;
    return True;
  end Contains0;

  function Contains(S1,S2: Ball) return Boolean is
  begin
    if S2.B>S1.B then return False; end if;
    if S2.R>S1.R then return False; end if;
    if (((S1.B-S2.B)+(S1.R-S2.R))-abs(S2.C-S1.C))<Zero then return False; end if;
    if (((S2.B-S1.B)+(S2.R-S1.R))+abs(S2.C-S1.C))>Zero then raise Undefined; end if;
    return True;
  end Contains;

  function BallAt0(R: Rep) return Ball is
  begin
    return (Zero,Zero,abs(R));
  end BallAt0;

  function BallAt0(S: Ball) return Ball is
  begin
    return (Zero,Zero,(abs(S.C)+S.R)+S.B);
  end BallAt0;

  function DiskAt0(R: Rep) return Ball is
  begin
    return (Zero,abs(R),Zero);
  end DiskAt0;

  function DiskAt0(S: Ball) return Ball is
  begin
    return (Zero,(abs(S.C)+S.R)+S.B,Zero);
  end DiskAt0;

  function Disk(R1,R2: Rep) return Ball is
    Sum: constant Rep := R1+R2;
    Eps: constant Rep := Sum+(-R1-R2);
  begin
    return (Half*Sum,Half*(abs(R2-R1)+Eps),Zero);
  end Disk;

  function Disk(S: Ball) return Ball is
  begin
    return (S.C,S.R+S.B,Zero);
  end Disk;

  function Center(S: Ball) return Ball is
  begin
    return (S.C,Zero,Zero);
  end Center;

  function CenterDisk(S: Ball) return Ball is
  begin
    return (S.C,S.R,Zero);
  end CenterDisk;

  function ModCenter(S: Ball) return Ball is
  begin
    return (Zero,S.R,S.B);
  end ModCenter;

  function Union(S1,S2: Ball) return Ball is
    R1:  constant Rep := RMin(-(S1.R-S1.C),-(S2.R-S2.C));
    R2:  constant Rep := RMax(S1.C+S1.R,S2.C+S2.R);
    Sum: constant Rep := R1+R2;
    Eps: constant Rep := Sum+(-R1-R2);
  begin
    return (Half*Sum,Half*((R2-R1)+Eps),RMax(S1.B,S2.B));
  end Union;

  function Intersection(S1,S2: Ball) return Ball is
    R1:  constant Rep := RMax(-(S1.R-S1.C),-(S2.R-S2.C));
    R2:  constant Rep := RMin(S1.C+S1.R,S2.C+S2.R);
    Sum: constant Rep := R1+R2;
    Eps: constant Rep := Sum+(-R1-R2);
  begin
    if (S1.B /= Zero) or (S2.B /= Zero) then raise Undefined; end if;
    if R2<R1 then raise Undefined; end if;
    return (Half*Sum,Half*((R2-R1)+Eps),Zero);
  end Intersection;

--- order

  function "<"(S1,S2: Ball) return Boolean is
  begin
    if Sup(S1) < Inf(S2) then return True; end if;
    if Inf(S1) < Sup(S2) then raise Undefined; end if;
    return False;
  end "<";

  function "<="(S1,S2: Ball) return Boolean is
  begin
    if Sup(S1) <= Inf(S2) then return True; end if;
    if Inf(S1) <= Sup(S2) then raise Undefined; end if;
    return False;
  end "<=";

  function ">"(S1,S2: Ball) return Boolean is
  begin
    if Inf(S1) > Sup(S2) then return True; end if;
    if Sup(S1) > Inf(S2) then raise Undefined; end if;
    return False;
  end ">";

  function ">="(S1,S2: Ball) return Boolean is
  begin
    if Inf(S1) >= Sup(S2) then return True; end if;
    if Sup(S1) >= Inf(S2) then raise Undefined; end if;
    return False;
  end ">=";

  function Min(S1,S2: Ball) return Ball is
    U1: constant Rep := Sup(S1);
    L2: constant Rep := Inf(S2);
  begin
    if U1 <= L2 then return S1; end if;
    declare
      L1: constant Rep := Inf(S1);
      U2: constant Rep := Sup(S2);
    begin
      if U2 <= L1 then
        return S2;
      else
        return Disk(RMin(L1,L2),RMin(U1,U2));
      end if;
    end;
  end Min;

  function Max(S1,S2: Ball) return Ball is
    U1: constant Rep := Sup(S1);
    L2: constant Rep := Inf(S2);
  begin
    if U1 <= L2 then return S2; end if;
    declare
      L1: constant Rep := Inf(S1);
      U2: constant Rep := Sup(S2);
    begin
      if U2 <= L1 then
        return S1;
      else
        return Disk(RMax(L1,L2),RMax(U1,U2));
      end if;
    end;
  end Max;

  function Inf(S: Ball) return Rep is
  begin
    if S.B /= Zero then raise Undefined; end if;
    return -(S.R-S.C);
  end Inf;

  function Sup(S: Ball) return Rep is
  begin
    if S.B /= Zero then raise Undefined; end if;
    return S.C+S.R;
  end Sup;

--- addition and multiplication

  function IsZero(S: Ball) return Boolean is
  begin
    return (S=Ball_Zero);
  end IsZero;

  procedure SetZero(S: out Ball) is
  begin
    S := Ball_Zero;
  end SetZero;

  procedure Neg(S: in out Ball) is
  begin
    S.C := -S.C;
  end Neg;

  procedure Neg(S1: in Ball; S2: out Ball) is
  begin
    S2.C := -S1.C;
    S2.R :=  S1.R;
    S2.B :=  S1.B;
  end Neg;

  function "-"(S: Ball) return Ball is
  begin
    return (-S.C,S.R,S.B);
  end "-";

  procedure Add(S1: in Ball; S2: in out Ball) is
    Sum: constant Rep := S1.C+S2.C;
    Eps: constant Rep := Sum+(-S1.C-S2.C);
  begin
    S2.C := Sum;
    S2.R := (S1.R+S2.R)+Eps;
    S2.B := S1.B+S2.B;
  end Add;

  procedure Sum(S1,S2: in Ball; S3: out Ball) is
    Sum: constant Rep := S1.C+S2.C;
    Eps: constant Rep := Sum+(-S1.C-S2.C);
  begin
    S3.C := Sum;
    S3.R := (S1.R+S2.R)+Eps;
    S3.B := S1.B+S2.B;
  end Sum;

  function "+"(S1,S2: Ball) return Ball is
    Sum: constant Rep := S1.C+S2.C;
    Eps: constant Rep := Sum+(-S1.C-S2.C);
  begin
    return (Sum,(S1.R+S2.R)+Eps,S1.B+S2.B);
  end "+";

  procedure Sub(S1: in Ball; S2: in out Ball) is
    Dif: constant Rep := S2.C-S1.C;
    Eps: constant Rep := Dif+(S1.C-S2.C);
  begin
    S2.C := Dif;
    S2.R := (S1.R+S2.R)+Eps;
    S2.B := S1.B+S2.B;
  end Sub;

  procedure Diff(S1,S2: in Ball; S3: out Ball) is
    Dif: constant Rep := S1.C-S2.C;
    Eps: constant Rep := Dif+(S2.C-S1.C);
  begin
    S3.C := Dif;
    S3.R := (S1.R+S2.R)+Eps;
    S3.B := S1.B+S2.B;
  end Diff;

  function "-"(S1,S2: Ball) return Ball is
    Dif: constant Rep := S1.C-S2.C;
    Eps: constant Rep := Dif+(S2.C-S1.C);
  begin
    return (Dif,(S1.R+S2.R)+Eps,S1.B+S2.B);
  end "-";

  procedure Mult(I: in Integer; S: in out Ball) is
  begin
    Mult(Rep(I),S);
  end Mult;

  procedure Prod(I: in Integer; S1: in Ball; S2: out Ball) is
  begin
    Prod(Rep(I),S1,S2);
  end Prod;

  function "*"(I: Integer; S: Ball) return Ball is
    T: Ball;
  begin
    Prod(Rep(I),S,T);
    return T;
  end "*";

  procedure MultAdd(I: Integer; S1: in Ball; S2: in out Ball) is
    T: Ball;
  begin
    Prod(Rep(I),S1,T);
    Add(T,S2);
  end MultAdd;

  procedure Mult(R: in Rep; S: in out Ball) is
    Pro: constant Rep := R*S.C;
    Eps: constant Rep := Pro+R*(-S.C);
  begin
    S.C := Pro;
    S.R := abs(R)*S.R+Eps;
    S.B := abs(R)*S.B;
  end Mult;

  procedure Prod(R: in Rep; S1: in Ball; S2: out Ball) is
    Pro: constant Rep := R*S1.C;
    Eps: constant Rep := Pro+R*(-S1.C);
  begin
    S2.C := Pro;
    S2.R := abs(R)*S1.R+Eps;
    S2.B := abs(R)*S1.B;
  end Prod;

  function "*"(R: Rep; S: Ball) return Ball is
    T: Ball;
  begin
    Prod(R,S,T);
    return T;
  end "*";

  procedure MultAdd(R: in Rep; S1: in Ball; S2: in out Ball) is
    T: Ball;
  begin
    Prod(R,S1,T);
    Add(T,S2);
  end MultAdd;

  procedure Mult(S1: in Ball; S2: in out Ball) is
    T: Ball;
  begin
    Prod(S1,S2,T);
    S2 := T;
  end Mult;

  procedure Prod(S1,S2: in Ball; S3: out Ball) is
    R1:  constant Rep := abs(S1.C);
    R2:  constant Rep := abs(S2.C)+S2.R;
    Pro: constant Rep := S1.C*S2.C;
    Eps: constant Rep := Pro+S1.C*(-S2.C);
  begin
    S3.C := Pro;
    S3.R := (R1*S2.R+S1.R*R2)+Eps;
    S3.B := (R1+S1.R)*S2.B+S1.B*(R2+S2.B);
  end Prod;

  function "*"(S1,S2: Ball) return Ball is
    T: Ball;
  begin
    Prod(S1,S2,T);
    return T;
  end "*";

  procedure MultAdd(S1,S2: in Ball; S3: in out Ball) is
    T: Ball;
  begin
    Prod(S1,S2,T);
    Add(T,S3);
  end MultAdd;

  function "/"(S: Ball; R: Rep) return Ball is
    Quo: constant Rep := S.C/R;
    Eps: constant Rep := Quo+S.C/(-R);
  begin
    return (Quo,S.R/abs(R)+Eps,S.B/abs(R));
  end "/";

  procedure Inv(S1: in Ball; S2: out Ball) is
    R1:  constant Rep := S1.R-Abs(S1.C);
    R2:  constant Rep := S1.B+R1;
    Quo: constant Rep := One/S1.C;
    Eps: constant Rep := Quo+NegOne/S1.C;
  begin
    if R2 >= Zero then raise Constraint_Error; end if;  -- division by zero
    S2.C := Quo;
    S2.R := (S1.R/abs(S1.C))/(-R1)+Eps;
    S2.B := (S1.B/(-R1))/(-R2);
  end Inv;

  function Inv(S: Ball) return Ball is
    T: Ball;
  begin
    Inv(S,T);
    return T;
  end Inv;

  function "/"(S1,S2: Ball) return Ball is
    T1,T2: Ball;
  begin
    Inv(S2,T1);
    Prod(S1,T1,T2);
    return T2;
  end "/";

  function "**"(S: Ball; I: Integer) return Ball is
    J: Integer := I;
    X,Y: Ball;
  begin
    if I>0 then
      X := S;
      if (J rem 2)=0 then
        Y := Ball_One;
      else
        Y := X;
      end if;
      J := J/2;
      while J>0 loop
        Mult(X,X);           -- identical args ok with our Mult
        if (J rem 2)>0 then
          Mult(X,Y);
        end if;
        J := J/2;
      end loop;
      return Y;
    elsif I<0 then
      return Inv(S)**(-I);
    else
      return Ball_One;
    end if;
  end "**";

--- functions

  function Sqr(S: Ball) return Ball is
    T: Ball;
  begin
    Prod(S,S,T);
    return T;
  end Sqr;

  function Norm(S: Ball) return Ball is
    T: Ball;
  begin
    T.C := abs(S.C);
    T.R := S.R+S.B;
    T.B := Zero;
    if T.R>T.C then
      T.C := Half*(T.C+T.R);
      T.R := T.C;
    end if;
    return T;
  end Norm;

  function MaxNorm(S: Ball) return Radius is
  begin
    return (abs(S.C)+S.R)+S.B;
  end MaxNorm;

--- could trust/use hardware for these functions

  function ExpUp(R: Rep) return Rep is
    Iter: constant Integer := 13;
    K: Integer := 0;
    X: Rep := R;
    Y: Rep := Rep(33)/Rep(32);  -- assumes Iter>8
  begin
    if R<Zero then return One/ExpDown(-R); end if;
    if R=Zero then return One; end if;
    while X>Quarter loop
      K := K+1;
      X := Half*X;
    end loop;
    for I in reverse 1 .. Iter loop
      Y := One+(X*Y)/Rep(I);
    end loop;
    for I in 1 .. K loop
      Y := Y*Y;
    end loop;
    return Y;
  end ExpUp;

  function ExpDown(R: Rep) return Rep is
    Iter: constant Integer := 13;
    K: Integer := 0;
    X: Rep := R;
    Y: Rep := NegOne;
  begin
    if R<Zero then return -(NegOne/ExpUp(-R)); end if;
    if R=Zero then return One; end if;
    while X>Quarter loop
      K := K+1;
      X := Half*X;
    end loop;
    for I in reverse 1 .. Iter loop
      Y := NegOne+(X*Y)/Rep(I);
    end loop;
    for I in 1 .. K loop
      Y := (-Y)*Y;
    end loop;
    return -Y;
  end ExpDown;

  function Exp(S: Ball) return Ball is
  begin
    return Disk(ExpDown(Inf(S)),ExpUp(Sup(S)));
  end Exp;

  function Log(S: Ball) return Ball is
    use Reps.Ops;
    Iter: constant Integer := 3;
    S0,LogS0,X,Y: Ball;
  begin
    if S=Ball_One then return Ball_Zero; end if;
    if S.B /= Zero then raise Undefined; end if;
    LogS0 := Scal(Log(Approx(S)));                  -- guess
    S0 := Exp(LogS0);
    X := (S0-S)/S0;                                 -- Log(S) = Log(S0)+Log(1-X)
    Y := Ball_One-Norm(X);
    if Inf(Y) <= Zero then raise Constraint_Error; end if;
    Y := (PlusMinus*Inv(Y))/Rep(Iter+1);
    for I in reverse 1 .. Iter loop
      Y := Inv(Scal(I))+X*Y;
    end loop;
    return LogS0-X*Y;
  end Log;

  function Sqrt(S: Ball) return Ball is
    use Reps.Ops;
    S0,SqrtS0,X,Y: Ball;
  begin
    if S=Ball_Zero then return Ball_Zero; end if;
    if Inf(S)<Zero then raise Undefined; end if;    -- also excludes nonzero S.B
    SqrtS0 := Scal(Sqrt(Approx(S)));                -- guess
    S0 := Sqr(SqrtS0);
    X := (S-S0)/S0;                                 -- Sqrt(S) = Sqrt(S0)*Sqrt(1+X)
    Y := ((PlusMinus*X-Scal(2))*X+Scal(8))*X/Rep(16);
    return SqrtS0*(Ball_One+Y);
  end Sqrt;

--- conversion and i/o

  function Scal(K: Integer) return Ball is
  begin
    return (Rep(K),Zero,Zero);
  end Scal;

  function Scal(R: Rep) return Ball is
  begin
    return (R,Zero,Zero);
  end Scal;

  procedure ShowType(S: in Ball) is
  begin
    Show0("Scalar type is Ball");
  end ShowType;

  procedure Show1(N: in String; S: in Ball) is
  begin
    Show3(N,S.C,S.R,S.B);
  end Show1;

  procedure Show2(N: in String; S1,S2: in Ball) is
  begin
    Show3(N,S1.C,S1.R,S1.B,False);
    Show3("   ",S2.C,S2.R,S2.B,True);
  end Show2;

  procedure Get(F: in File_Type; S: out Ball; Decimal: in Boolean := False) is
  begin
    Get(F,S.C,S.R,S.B,Decimal);
  end Get;

  procedure Put(F: in File_Type; S: in Ball; Decimal: in Boolean := False) is
  begin
    Put(F,S.C,S.R,S.B,Decimal);
  end Put;

  procedure UpMultAdd(R1,R2: in Rep; R3: in out Rep) is
  begin
    R3 := R3+R1*R2;
  end UpMultAdd;

begin

  Restore_Checks;

end Balls;