MODULE MathRe;
IMPORT NbrInt, NbrRe, DataErrors, MathInt, MathRat, MathReSeries;
VAR
MaxFactorial-, maxIterations: NbrInt.Integer;
delta, expInfinity, expNegligible, expZero, ln2, ln2Inv, ln10, ln10Inv, sqrtInfinity: NbrRe.Real;
TYPE
ArcSinA = OBJECT (MathReSeries.Coefficient)
PROCEDURE Evaluate;
VAR i, k, index: NbrInt.Integer; den, num: NbrRe.Real;
BEGIN {EXCLUSIVE}
IF NbrInt.Odd( n ) THEN
num := 1; den := 1; k := n;
FOR i := k - 2 TO 1 BY -2 DO index := i; num := num * index; den := den * (index + 1) END;
coef := num / (n * den)
ELSE coef := 0
END;
IF n > maxIterations THEN eos := TRUE; DataErrors.ReWarning( x, "Did not converge - timed out." ) END
END Evaluate;
END ArcSinA;
ArcSinhA = OBJECT (MathReSeries.Coefficient)
PROCEDURE Evaluate;
VAR index: NbrInt.Integer;
BEGIN {EXCLUSIVE}
IF n = 0 THEN coef := 0
ELSIF n = 1 THEN coef := 1 / x
ELSE
IF NbrInt.Odd( n ) THEN index := ((n - 2) * (n - 1)) ELSE index := (n * (n - 1)) END;
coef := index * x
END;
IF n > maxIterations THEN eos := TRUE; DataErrors.ReWarning( x, "Did not converge - timed out." ) END
END Evaluate;
END ArcSinhA;
ArcSinhB = OBJECT (MathReSeries.Coefficient)
PROCEDURE Evaluate;
BEGIN {EXCLUSIVE}
IF n = 0 THEN coef := 0 ELSE coef := (2 * n - 1) END
END Evaluate;
END ArcSinhB;
ArcTanhA = OBJECT (MathReSeries.Coefficient)
PROCEDURE Evaluate;
VAR index: NbrInt.Integer;
BEGIN {EXCLUSIVE}
IF n = 0 THEN coef := 0
ELSIF n = 1 THEN coef := 1 / x
ELSE index := (-(n - 1) * (n - 1)); coef := index * x
END;
IF n > maxIterations THEN eos := TRUE; DataErrors.ReWarning( x, "Did not converge - timed out." ) END
END Evaluate;
END ArcTanhA;
ArcTanhB = OBJECT (MathReSeries.Coefficient)
PROCEDURE Evaluate;
BEGIN {EXCLUSIVE}
IF n = 0 THEN coef := 0 ELSE coef := (2 * n - 1) END
END Evaluate;
END ArcTanhB;
TanA = OBJECT (MathReSeries.Coefficient)
PROCEDURE Evaluate;
BEGIN {EXCLUSIVE}
IF n = 0 THEN coef := 0
ELSIF n = 1 THEN coef := 1
ELSE coef := -x
END;
IF n > maxIterations THEN eos := TRUE; DataErrors.ReWarning( x, "Did not converge - timed out." ) END
END Evaluate;
END TanA;
TanB = OBJECT (MathReSeries.Coefficient)
PROCEDURE Evaluate;
BEGIN {EXCLUSIVE}
IF n = 0 THEN coef := 0 ELSE coef := (2 * n - 1) END
END Evaluate;
END TanB;
PROCEDURE Binomial*( top: NbrRe.Real; bottom: NbrInt.Integer ): NbrRe.Real;
VAR i: NbrInt.Integer; coef, prod: NbrRe.Real;
BEGIN
IF bottom < 0 THEN DataErrors.IntError( bottom, "Bottom parameter cannot be negative" ); prod := 0
ELSIF bottom = 0 THEN prod := 1
ELSE
i := bottom; prod := 1;
REPEAT coef := (top - (bottom - i)) / i; prod := coef * prod; NbrInt.Dec( i ) UNTIL i = 0
END;
RETURN prod
END Binomial;
PROCEDURE Factorial*( n: NbrInt.Integer ): NbrRe.Real;
VAR x, n2, n4, n6, n8, nR: NbrRe.Real;
BEGIN
IF n < 0 THEN DataErrors.IntError( n, "Negative arguments are inadmissible." ); x := 0
ELSIF n <= MathInt.MaxFactorial THEN x := MathInt.Factorial( n )
ELSIF n <= MathRat.MaxFactorial THEN x := MathRat.Factorial( n )
ELSIF n <= MaxFactorial THEN
nR := n; n2 := nR * nR; n4 := n2 * n2; n6 := n2 * n4; n8 := n4 * n4;
x := (1 - 1 / (30 * n2) + 1 / (105 * n4) - 1 / (140 * n6) + 1 / (99 * n8)) / (12 * nR);
x := Exp( x + nR * (Ln( nR ) - 1) ); x := x * Sqrt( 2 * NbrRe.Pi * nR )
ELSE DataErrors.IntError( n, "Argument is too large - overflow." ); x := 0
END;
RETURN x
END Factorial;
PROCEDURE Random*( ): NbrRe.Real;
VAR x: NbrRe.Real;
BEGIN
x := MathRat.Random(); RETURN x
END Random;
PROCEDURE Step*( x, x0: NbrRe.Real ): NbrRe.Real;
VAR step: NbrRe.Real;
BEGIN
IF x < x0 THEN step := 0
ELSIF x = x0 THEN step := 0.5
ELSE step := 1
END;
RETURN step
END Step;
PROCEDURE Sqrt*( x: NbrRe.Real ): NbrRe.Real;
VAR sqrt: NbrRe.Real;
BEGIN
IF x < 0 THEN DataErrors.ReError( x, "Argument cannot be negative." ); sqrt := 0
ELSIF x = 0 THEN sqrt := 0
ELSE sqrt := NbrRe.Sqrt( x )
END;
RETURN sqrt
END Sqrt;
PROCEDURE Pythag*( x, y: NbrRe.Real ): NbrRe.Real;
VAR absx, absy, dist, ratio: NbrRe.Real;
BEGIN
absx := NbrRe.Abs( x ); absy := NbrRe.Abs( y );
IF absx > absy THEN ratio := absy / absx; dist := absx * Sqrt( 1 + ratio * ratio )
ELSIF absy = 0 THEN dist := 0
ELSE ratio := absx / absy; dist := absy * Sqrt( 1 + ratio * ratio )
END;
RETURN dist
END Pythag;
PROCEDURE IntPower*( x: NbrRe.Real; n: NbrInt.Integer ): NbrRe.Real;
VAR sign: NbrInt.Integer; max, power: NbrRe.Real;
BEGIN
sign := 1;
IF n = 0 THEN
IF x # 0 THEN power := 1 ELSE DataErrors.Error( "Both argument and exponent cannot be zero." ); power := 1
END
ELSIF x = 0 THEN
IF n > 0 THEN power := 0
ELSE DataErrors.IntError( n, "Exponent cannot be negative when argument is zero." ); power := 0;
END
ELSE
IF x < 0 THEN
x := NbrRe.Abs( x );
IF NbrInt.Odd( n ) THEN sign := -1 END
END;
IF n < 0 THEN x := 1 / x; n := NbrInt.Abs( n ) END;
power := 1;
WHILE n > 0 DO
WHILE ~NbrInt.Odd( n ) & (n > 0) DO
max := NbrRe.MaxNbr / x;
IF x > max THEN x := max; n := 2; DataErrors.Error( "Arithmatic overflow." ) END;
x := x * x; n := n DIV 2
END;
max := NbrRe.MaxNbr / power;
IF x > max THEN x := max; n := 1; DataErrors.Error( "Arithmatic overflow." ) END;
power := power * x; NbrInt.Dec( n )
END
END;
RETURN sign * power
END IntPower;
PROCEDURE Power*( x: NbrRe.Real; y: NbrRe.Real ): NbrRe.Real;
VAR arg, power: NbrRe.Real;
BEGIN
IF x < 0 THEN DataErrors.ReError( x, "Argument cannot be negative." ); power := 0
ELSIF x = 0 THEN
power := 0;
IF y <= 0 THEN DataErrors.Error( "Exponent must be positive when argument is zero." ) END
ELSIF ( x = 1 ) OR ( y = 0 ) THEN power := 1
ELSE arg := y * Ln( x ); power := Exp( arg )
END;
RETURN power
END Power;
PROCEDURE Exp*( x: NbrRe.Real ): NbrRe.Real;
VAR exp: NbrRe.Real;
BEGIN
IF x > expInfinity THEN DataErrors.ReError( x, "Argument is too large." ); exp := NbrRe.MaxNbr
ELSIF x < expZero THEN exp := 0
ELSE exp := NbrRe.Exp( x )
END;
RETURN exp
END Exp;
PROCEDURE Exp2*( x: NbrRe.Real ): NbrRe.Real;
VAR exp, xLn2: NbrRe.Real;
BEGIN
xLn2 := x * ln2;
IF xLn2 > expInfinity THEN DataErrors.ReError( x, "Argument is too large." ); exp := NbrRe.MaxNbr
ELSIF xLn2 < expZero THEN exp := 0
ELSE exp := NbrRe.Exp( xLn2 )
END;
RETURN exp
END Exp2;
PROCEDURE Exp10*( x: NbrRe.Real ): NbrRe.Real;
VAR exp, xLn10: NbrRe.Real;
BEGIN
xLn10 := x * ln10;
IF xLn10 > expInfinity THEN DataErrors.ReError( x, "Argument is too large." ); exp := NbrRe.MaxNbr
ELSIF xLn10 < expZero THEN exp := 0
ELSE exp := NbrRe.Exp( xLn10 )
END;
RETURN exp
END Exp10;
PROCEDURE Ln*( x: NbrRe.Real ): NbrRe.Real;
VAR ln: NbrRe.Real;
BEGIN
IF x > 0 THEN ln := NbrRe.Ln( x ) ELSE DataErrors.ReError( x, "Argument must be positive." ); ln := 0 END;
RETURN ln
END Ln;
PROCEDURE Log2*( x: NbrRe.Real ): NbrRe.Real;
VAR ln, log: NbrRe.Real;
BEGIN
IF x > 0 THEN ln := NbrRe.Ln( x ); log := ln2Inv * ln
ELSE DataErrors.ReError( x, "Argument must be positive." ); log := 0
END;
RETURN log
END Log2;
PROCEDURE Log*( x: NbrRe.Real ): NbrRe.Real;
VAR ln, log: NbrRe.Real;
BEGIN
IF x > 0 THEN ln := NbrRe.Ln( x ); log := ln10Inv * ln
ELSE DataErrors.ReError( x, "Argument must be positive." ); log := 0
END;
RETURN log
END Log;
PROCEDURE Sin*( x: NbrRe.Real ): NbrRe.Real;
BEGIN
RETURN NbrRe.Sin( x )
END Sin;
PROCEDURE Cos*( x: NbrRe.Real ): NbrRe.Real;
BEGIN
RETURN NbrRe.Cos( x )
END Cos;
PROCEDURE Tan*( x: NbrRe.Real ): NbrRe.Real;
VAR cos, sin, tan: NbrRe.Real; a: TanA; b: TanB;
BEGIN
IF NbrRe.Abs( x ) < delta THEN NEW( a ); NEW( b ); tan := MathReSeries.ContinuedFraction( a, b, x )
ELSE
cos := Cos( x ); sin := Sin( x );
IF cos # 0 THEN tan := sin / cos
ELSE
DataErrors.ReError( x, "Division by zero, i.e., the cos(x) = 0." );
IF sin > 0 THEN tan := NbrRe.MaxNbr ELSE tan := NbrRe.MinNbr END
END
END;
RETURN tan
END Tan;
PROCEDURE ArcSin*( x: NbrRe.Real ): NbrRe.Real;
VAR a: ArcSinA; n, d, abs, arcsin: NbrRe.Real;
BEGIN
abs := NbrRe.Abs( x );
IF abs > 1 THEN DataErrors.ReError( x, "Argument is outside the admissible range: -1 <= x <= 1." ); arcsin := 0
ELSIF abs = 1 THEN n := x; d := 0; arcsin := ArcTan2( n, d )
ELSIF abs > delta THEN n := x; d := Sqrt( 1 - x * x ); arcsin := ArcTan2( n, d )
ELSE NEW( a ); arcsin := MathReSeries.PowerSeries( a, x )
END;
RETURN arcsin
END ArcSin;
PROCEDURE ArcCos*( x: NbrRe.Real ): NbrRe.Real;
VAR n, d, abs, arccos: NbrRe.Real;
BEGIN
abs := NbrRe.Abs( x );
IF abs > 1 THEN DataErrors.ReError( x, "Argument is outside the admissible range: -1 <= x <= 1." ); arccos := 0
ELSIF abs = 1 THEN n := 0; d := x; arccos := ArcTan2( n, d )
ELSE n := Sqrt( 1 - x * x ); d := x; arccos := ArcTan2( n, d )
END;
RETURN arccos
END ArcCos;
PROCEDURE ArcTan*( x: NbrRe.Real ): NbrRe.Real;
BEGIN
RETURN NbrRe.ArcTan( x )
END ArcTan;
PROCEDURE ArcTan2*( xn, xd: NbrRe.Real ): NbrRe.Real;
VAR atan: NbrRe.Real;
BEGIN
IF xd = 0 THEN
IF xn # 0 THEN atan := NbrRe.Sign( xn ) * NbrRe.Pi / 2
ELSE DataErrors.Error( "Both arguments cannot be zero." ); atan := 0
END
ELSIF xn = 0 THEN atan := (1 - NbrRe.Sign( xd )) * NbrRe.Pi / 2
ELSE atan := NbrRe.ArcTan( xn / xd ) + NbrRe.Sign( xn ) * (1 - NbrRe.Sign( xd )) * NbrRe.Pi / 2
END;
RETURN atan
END ArcTan2;
PROCEDURE Sinh*( x: NbrRe.Real ): NbrRe.Real;
VAR abs, expM1, sinh: NbrRe.Real;
BEGIN
abs := NbrRe.Abs( x );
IF abs < 1 THEN expM1 := Exp( abs ) - 1; sinh := NbrRe.Sign( x ) * (expM1 + expM1 / (1 + expM1)) / 2
ELSIF abs < expNegligible THEN sinh := (Exp( x ) - Exp( -x )) / 2
ELSE sinh := NbrRe.Sign( x ) * Exp( abs ) / 2
END;
RETURN sinh
END Sinh;
PROCEDURE Cosh*( x: NbrRe.Real ): NbrRe.Real;
VAR abs, cosh: NbrRe.Real;
BEGIN
abs := NbrRe.Abs( x );
IF abs < expNegligible THEN cosh := (Exp( x ) + Exp( -x )) / 2 ELSE cosh := Exp( abs ) / 2 END;
RETURN cosh
END Cosh;
PROCEDURE Tanh*( x: NbrRe.Real ): NbrRe.Real;
VAR abs, exp, expM, exp2xM1, tanh: NbrRe.Real;
BEGIN
abs := NbrRe.Abs( x );
IF abs < 1 THEN exp2xM1 := Exp( 2 * abs ) - 1; tanh := NbrRe.Sign( x ) * exp2xM1 / (2 + exp2xM1)
ELSIF abs < expNegligible THEN exp := Exp( x ); expM := Exp( -x ); tanh := (exp - expM) / (exp + expM)
ELSE tanh := NbrRe.Sign( x )
END;
RETURN tanh
END Tanh;
PROCEDURE ArcSinh*( x: NbrRe.Real ): NbrRe.Real;
VAR abs, asinh: NbrRe.Real; a: ArcSinhA; b: ArcSinhB;
BEGIN
abs := NbrRe.Abs( x );
IF abs > sqrtInfinity THEN DataErrors.ReError( x, "Argument is too large." ); asinh := 0
ELSIF x < -delta THEN asinh := -Ln( abs + Sqrt( abs ) * Sqrt( abs + 1 / abs ) )
ELSIF x < delta THEN
IF x = 0 THEN asinh := 0
ELSE NEW( a ); NEW( b ); asinh := x * Sqrt( 1 + x * x ) * MathReSeries.ContinuedFraction( a, b, x )
END
ELSE asinh := Ln( x + Sqrt( x ) * Sqrt( x + 1 / x ) )
END;
RETURN asinh
END ArcSinh;
PROCEDURE ArcCosh*( x: NbrRe.Real ): NbrRe.Real;
VAR acosh: NbrRe.Real;
BEGIN
IF x < 1 THEN DataErrors.ReError( x, "Argument is out of range, i.e., x >= 1." ); acosh := 0
ELSIF x = 1 THEN acosh := 0
ELSIF x < sqrtInfinity THEN acosh := Ln( x + Sqrt( x ) * Sqrt( x - 1 / x ) )
ELSE DataErrors.ReError( x, "Argument is too large." ); acosh := 0
END;
RETURN acosh
END ArcCosh;
PROCEDURE ArcTanh*( x: NbrRe.Real ): NbrRe.Real;
VAR abs, atanh: NbrRe.Real; a: ArcTanhA; b: ArcTanhB;
BEGIN
abs := NbrRe.Abs( x );
IF abs > 1 THEN DataErrors.ReError( x, "Argument is out of range, i.e., -1 <= x <= 1." ); atanh := 0
ELSIF abs = 1 THEN atanh := NbrRe.Sign( x ) * expNegligible
ELSIF abs > delta THEN atanh := Ln( (1 + x) / (1 - x) ) / 2
ELSIF abs > 0 THEN NEW( a ); NEW( b ); atanh := x * MathReSeries.ContinuedFraction( a, b, x )
ELSE atanh := 0
END;
RETURN atanh
END ArcTanh;
BEGIN
IF NbrRe.MaxNbr = MAX( REAL ) THEN MaxFactorial := 34 ELSE MaxFactorial := 171 END;
maxIterations := 1000; expZero := Ln( 1/NbrRe.MaxNbr ); expInfinity := Ln( NbrRe.MaxNbr );
expNegligible := -Ln( NbrRe.Epsilon ) / 2; sqrtInfinity := Sqrt( NbrRe.MaxNbr ) / 2; delta := 0.1;
ln10 := NbrRe.Ln( 10 ); ln10Inv := 1 / ln10; ln2 := NbrRe.Ln( 2 ); ln2Inv := 1 / ln2
END MathRe.