(* ----------------------------------------------------------------------------
 * $Id: MathLib.mi,v 1.4 1993/03/22 10:17:07 kredel Exp $
 * ----------------------------------------------------------------------------
 * Copyright (c) GMD
 * ----------------------------------------------------------------------------
 * $Log: MathLib.mi,v $
 * Revision 1.4  1993/03/22  10:17:07  kredel
 * This file is not part of MAS
 *
 * Revision 1.3  1992/10/15  16:28:15  kredel
 * Changed rcsid variable
 *
 * Revision 1.2  1992/02/12  13:19:06  pesch
 * Moved CONST Definition to the right place.
 *
 * Revision 1.1  1992/01/22  15:08:34  kredel
 * Initial revision
 *
 * ----------------------------------------------------------------------------
 *)
IMPLEMENTATION MODULE MathLib;

(* GMD Mocka MathLib. *)


  CONST
    log2     =  0.693147180559945309E0;  (* ln (2.0) *)
    pi       = 3.1415926535897932384626434;
    twoopi   = 2.0 / pi;
    pio2     =   pi / 2.0;
    pio4     =   pi / 4.0;
    sqrt2    =  1.4142135623730950488016887;
    MaxCardI = 65535;
    MaxCardR = 65535.0;
    
CONST rcsidi = "$Id: MathLib.mi,v 1.4 1993/03/22 10:17:07 kredel Exp $";
CONST copyrighti = "Copyright (c) GMD";



  PROCEDURE Trunc (x : LONGREAL) : INTEGER;
  (* software emulation of TRUNC for LONGREAL, x >= 0.0 *)
    VAR res, exp, i, j : INTEGER; xS : REAL; xL : LONGREAL; c : CARDINAL;
  BEGIN
    exp := 0;
    WHILE x >= MaxCardR DO
      x := x / MaxCardR; INC (exp);
    END;
    xS := x;
    res := TRUNC (xS);
    FOR i := 1 TO exp DO
      xS := x;
      xL := FLOAT ( TRUNC (xS));
      x := (x - xL) * MaxCardR;
      xS := x;
      j := TRUNC (xS);
      res := MaxCardI * res + j;
    END;
    RETURN res;
  END Trunc;

  PROCEDURE Float (x : INTEGER) : LONGREAL;
  (* software emulation of FLOAT for LONGREAL, x >= 0.0 *)
    VAR divC, modC : CARDINAL;
        divR, modR : LONGREAL;
  BEGIN
    divC := x DIV MaxCardI; divR := FLOAT (divC);
    modC := x MOD MaxCardI; modR := FLOAT (modC);
    RETURN divR * MaxCardR + modR;
  END Float;

  PROCEDURE frexp (x: LONGREAL; VAR exp: INTEGER): LONGREAL;
  (* Returns the real mantissa m of 'x' and an integer exp *)
  (* such that 'x' = m * 2 ** exp *)
    VAR neg : BOOLEAN;
  BEGIN (* frexp *)
    exp := 0;
    neg := x < 0.0;
    IF neg THEN x := -x; END;
    IF x >= 1.0 THEN
      REPEAT
        INC (exp); x := x / 2.0;
      UNTIL x < 1.0;
    ELSIF (x < 0.1) & (x # 0.0) THEN
      REPEAT
        DEC (exp); x := x * 2.0;
      UNTIL x >= 0.1;
    END;
    IF neg THEN x := -x; END;
    RETURN x
  END frexp;

  PROCEDURE ldexp (value: LONGREAL; exp: INTEGER): LONGREAL;
  (* Returns value * 2 ** exp *)
    VAR i: INTEGER;
  BEGIN
    IF exp > 0 THEN
      FOR i := 1 TO  exp DO value := value * 2.0 END;
    ELSIF exp < 0 THEN
      FOR i := 1 TO -exp DO value := value / 2.0 END;
    END;
    RETURN value;
  END ldexp;

  PROCEDURE modf (value: LONGREAL; VAR int: INTEGER): LONGREAL;
  (* Returns the positive fractional part of value and (by int) *)
  (* the integer part *)
    VAR neg: BOOLEAN; 
  BEGIN
    neg := value < 0.0;
    IF neg THEN value := -value END;
    int := Trunc (value);
    value := value - Float (int);
    IF neg THEN int := -int END;
    RETURN value;
  END modf;

  PROCEDURE sarctan (arg: LONGREAL): LONGREAL;
  (* Reduces its positive argument to the range {0 .. 0.414} and calls xatan *)

    PROCEDURE xatan (arg: LONGREAL): LONGREAL;
      CONST
	p4 = 0.161536412982230228262E2;
	p3 = 0.26842548195503973794141E3;
	p2 = 0.11530293515404850115428136E4;
	p1 = 0.178040631643319697105464587E4;
	p0 = 0.89678597403663861959987488E3;
	q4 = 0.5895697050844462222791E2;
	q3 = 0.536265374031215315104235E3;
	q2 = 0.16667838148816337184521798E4;
	q1 = 0.207933497444540981287275926E4;
	q0 = 0.89678597403663861962481162E3;
      VAR argsq: LONGREAL;
    BEGIN (* xatan *)
      argsq := arg * arg;
      RETURN ((((         p4  * argsq + p3) * argsq + p2)
	       * argsq + p1) * argsq + p0)
	   / (((((argsq + q4) * argsq + q3) * argsq + q2)
	       * argsq + q1) * argsq + q0) * arg;
    END xatan;

    CONST
      sq2m1 = sqrt2 - 1.0;
      sq2p1 = sqrt2 + 1.0;
  BEGIN (* sarctan *)
    IF arg < sq2m1 THEN
      RETURN xatan (arg);
    ELSIF arg > sq2p1 THEN
      RETURN pio2 - xatan (1.0 / arg);
    ELSE
      RETURN pio4 + xatan ((arg-1.0) / (arg+1.0));
    END;
  END sarctan;

  PROCEDURE sinus (x: LONGREAL; quad: INTEGER): LONGREAL;
    VAR y, dummy, ysq: LONGREAL;
      e, f, k: INTEGER;
    CONST
      p0 =  0.1357884097877375669092680E8;
      p1 = -0.4942908100902844161158627E7;
      p2 =  0.4401030535375266501944918E6;
      p3 = -0.1384727249982452873054457E5;
      p4 =  0.1459688406665768722226959E3;
      q0 =  0.8644558652922534429915149E7;
      q1 =  0.4081792252343299749395779E6;
      q2 =  0.9463096101538208180571257E4;
      q3 =  0.1326534908786136358911494E3;
  BEGIN (* sinus *)
    IF x < 0.0 THEN
     x := -x;
     INC (quad, 2);
    END;
    x := x * twoopi;  (* Underflow? *)
    IF x > 32764.0 THEN
      y := modf (x, e);
      INC (e, quad);
      dummy := modf (0.25 * real (e), f);
      quad := e - 4 * f;
    ELSE
      k := entier (x);
      y := x - realL (k);
      quad := (quad + k) MOD 4;
    END;
    IF ODD (quad) THEN y := 1.0 - y END;
    IF quad > 1 THEN y := -y END;
    ysq := y * y;
    RETURN (((((p4 * ysq + p3) * ysq + p2) * ysq + p1) * ysq + p0) * y)
         /  ((((     ysq + q3) * ysq + q2) * ysq + q1) * ysq + q0);
  END sinus;

  PROCEDURE sqrt (arg: REAL): REAL;
  BEGIN 
    RETURN sqrtL (arg)
  END sqrt;

  PROCEDURE sqrtL (arg: LONGREAL): LONGREAL;
    (* Newton's method. Returns zero if arg < 0 *)

    CONST L30 = 1.073741824E9; (* = 1L<<30 *)
    VAR x, temp: LONGREAL; exp: INTEGER; i: [0..4];

    PROCEDURE TwoPower (b: CARDINAL): LONGREAL;
      (* Returns 1L<<b for b < 32 *)
      VAR i: CARDINAL; result: LONGREAL;
    BEGIN (* TwoPower *)
      result := 1.0;
      FOR i := 1 TO b DO result := result * 2.0 END;
      RETURN result;
    END TwoPower;

  BEGIN (* sqrt *)
    IF arg < 0.0 THEN (* Overflow *) RETURN 0.0 END;
    IF arg = 0.0 THEN RETURN 0.0 END;
    x := frexp (arg, exp);
    WHILE x < 0.5 DO
      x := x * 2.0;
      DEC (exp);
    END;
    IF ODD (exp) THEN
      x := x * 2.0;
      DEC (exp);
    END;
    temp := 0.5 * (1.0 + x);
    WHILE exp > 60 DO
      temp := temp * L30;
      DEC (exp, 60);
    END;
    WHILE exp < -60 DO
      temp := temp / L30;
      INC (exp, 60);
    END;
    IF exp >= 0 THEN
      temp := temp * TwoPower ( exp DIV 2);
    ELSE
      temp := temp / TwoPower (-exp DIV 2);
    END;
    FOR i := 0 TO 4 DO temp := 0.5 * (temp + arg / temp) END;
    RETURN temp;
  END sqrtL;

  PROCEDURE exp (arg: REAL): REAL;
  BEGIN 
    RETURN expL (arg)
  END exp;

  PROCEDURE expL (arg: LONGREAL): LONGREAL;
    (* Returns zero for arg out of range *)
    VAR ent: INTEGER;
      fract, xsq, temp1, temp2: LONGREAL;
    CONST
      maxf = 10000.0;
      p0 = 0.2080384346694663001443843411E7;
      p1 = 0.3028697169744036299076048876E5;
      p2 = 0.6061485330061080841615584556E2;
      q0 = 0.6002720360238832528230907598E7;
      q1 = 0.3277251518082914423057964422E6;
      q2 = 0.1749287689093076403844945335E4;
      log2e = 1.0 / log2;
  BEGIN (* exp *)
    IF arg = 0.0 THEN RETURN 1.0 END;
    IF arg < -maxf THEN RETURN 0.0 END;
    IF arg >  maxf THEN (* Overflow *) RETURN 0.0 END;
    arg := arg * log2e;
    ent := entier (arg);
    fract := (arg - realL (ent)) - 0.5;
    xsq := fract * fract;
    temp1 := ((       p2  * xsq + p1) * xsq + p0) * fract;
    temp2 :=  ((xsq + q2) * xsq + q1) * xsq + q0;
    RETURN ldexp (sqrt2 * (temp2 + temp1) / (temp2 - temp1), ent);
  END expL;

  PROCEDURE ln (arg: REAL): REAL;
  BEGIN 
    RETURN lnL (arg)
  END ln;

  PROCEDURE lnL (arg: LONGREAL): LONGREAL;
    (* Returns zero for arg out of range *)
    VAR asq: LONGREAL; exp: INTEGER;
    CONST
      sqrto2 = 1.0 / sqrt2;
      p0 = -0.240139179559210510E2;
      p1 =  0.309572928215376501E2;
      p2 = -0.963769093368686593E1;
      p3 =  0.421087371217979714E0;
      q0 = -0.120069589779605255E2;
      q1 =  0.194809660700889731E2;
      q2 = -0.891110902798312337E1;
  BEGIN (* ln *)
    IF arg <= 0.0 THEN (* Overflow *) RETURN 0.0 END;
    arg := frexp (arg, exp);
    WHILE arg < sqrto2 DO
      arg := arg * 2.0;
      DEC (exp);
    END;
    arg := (arg - 1.0) / (arg + 1.0);
    asq := arg * arg;
    RETURN (((p3 * asq + p2) * asq + p1) * asq + p0)
         / (((     asq + q2) * asq + q1) * asq + q0)
	 * arg + realL (exp) * log2;
  END lnL;

  PROCEDURE sin (arg: REAL): REAL;
  BEGIN 
    RETURN sinL (arg)
  END sin;

  PROCEDURE sinL (arg: LONGREAL): LONGREAL;
  BEGIN
    RETURN sinus (arg, 0);
  END sinL;

  PROCEDURE cos (arg: REAL): REAL;
  BEGIN 
    RETURN cosL (arg)
  END cos;

  PROCEDURE cosL (arg: LONGREAL): LONGREAL;
  BEGIN
    IF arg < 0.0 THEN arg := -arg END;
    RETURN sinus (arg, 1);
  END cosL;

  PROCEDURE arctan (arg: REAL): REAL;
  BEGIN 
    RETURN arctanL (arg)
  END arctan;

  PROCEDURE arctanL (arg: LONGREAL): LONGREAL;
    (* Returns the value of the arctangent of its argument in the
       range {-pi..+pi} *)
  BEGIN (* arctan *)
    IF arg > 0.0 THEN
      RETURN sarctan (arg);
    ELSE
      RETURN -sarctan (-arg);
    END;
  END arctanL;

  PROCEDURE real (arg: INTEGER): REAL;
  BEGIN 
    RETURN realL (arg)
  END real;

  PROCEDURE realL (arg: INTEGER): LONGREAL;
  BEGIN
    IF arg < 0 THEN
      RETURN -Float (-arg);
    ELSE
      RETURN  Float (arg);
    END;
  END realL;

  PROCEDURE entier (arg: REAL): INTEGER;
  BEGIN 
    RETURN entierL (arg)
  END entier;

  PROCEDURE entierL (arg: LONGREAL): INTEGER;
    (* Returns the largest integer <= arg *)
  BEGIN
    IF arg >= 0.0 THEN
      RETURN  Trunc (arg);
    ELSIF -Float (Trunc (-arg)) = arg THEN
      RETURN -Trunc (-arg);
    ELSE
      (*RETURN -Trunc (arg) - 1;   HE 3/90 *)
      RETURN -Trunc (-arg) - 1;
    END;
  END entierL;

END MathLib.
(* -EOF- *)