123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433 |
- (* CAPO - Computational Analysis Platform for Oberon - by Alan Freed and Felix Friedrich. *)
- (* Version 1, Update 2 *)
- MODULE MathRe; (** AUTHOR "adf"; PURPOSE "Real math functions"; *)
- 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;
- (** h n i G(n+1)
- | | = ---------- , m 3 0
- j m k m!G(n-m+1)
- *)
- PROCEDURE Binomial*( top: NbrRe.Real; bottom: NbrInt.Integer ): NbrRe.Real;
- (* Formula 6:3:1 of: J. Spanier and K. B. Oldham, An Atlas of Functions, Hemisphere Publishing Corp., Washington DC, 1987. *)
- 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;
- (** Computes n! = n * (n - 1) * (n - 2) * ... * 1, MaxFactorial 3 n 3 0. *)
- 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
- (* Use Stirling's approximation. *)
- 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;
- (** Returns a pseudo-random number r uniformly distributed over the unit interval, i.e., r N (0, 1). *)
- PROCEDURE Random*( ): NbrRe.Real;
- VAR x: NbrRe.Real;
- BEGIN
- x := MathRat.Random(); RETURN x
- END Random;
- (** Returns the Heaviside step function: 0 if x < x0, 1/2 if x = x0, and 1 if x > x0. *)
- 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;
- (** Computes the square root. *)
- 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;
- (** Computes the Pythagorean distance: V(x2 + y2). *)
- 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;
- (** Computes xn, {x,n} 9 {0,0}. *)
- 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
- (* Sending an error message is ok, but if without stopping people in most cases do expect 0^0 =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;
- (** Computes xy, x 3 0, {x,y} 9 {0,0} . *)
- 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;
- (** Computes ex *)
- 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;
- (** Computes 2x *)
- 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;
- (** Computes 10x *)
- 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;
- (** Computes Loge(x) - the natural log. *)
- 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;
- (** Computes Log2(x) - log base 2. *)
- 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;
- (** Computes Log10(x) - log base 10. *)
- 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;
- (** Triganometric Functions *)
- 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;
- (* Returns the arcus sine of 'x' in the range [-p/2, p/2] where -1 <= x <= 1 *)
- 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;
- (* Returns the arcus cosine of 'x' in the range [0, p] where -1 <= x <= 1 *)
- 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; (** Quadrant-correct arcus tangent: atan(xn/xd). *)
- 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;
- (** Hyperbolic Functions *)
- 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;
- (* ArcSinh(x) is the arcus hyperbolic sine of 'x'. *)
- 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;
- (* ArcCosh(x) is the arcus hyperbolic cosine of 'x'.
- All arguments greater than or equal to 1 are legal. *)
- 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;
- (* ArcTanh(x) is the arcus hyperbolic tangent of 'x'. *)
- 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.
|