(* CAPO - Computational Analysis Platform for Oberon - by Alan Freed and Felix Friedrich. *)
(* Version 1, Update 2 *)

MODULE MathErf;   (** AUTHOR "adf"; PURPOSE "The error function:  erf(x) = (2/Vp) x0x exp(-t2) dt"; *)

(* To change to 64-bit reals, address the code fragments written in light red. *)

(* Ref:  J.F. Hart, E.W. Cheney, C.L. Lawson, H.J. Maehly, C.K. Mesztenyi, J.R. Rice, H.G. Thacher, Jr., and C. Witzgall,
		"Computer Approximations," in: The SIAM Series in Applied MathLematics, Wiley, New York, 1968. *)

IMPORT NbrInt, NbrRe, DataErrors, MathRe, MathReSeries;

VAR
	maxIterations: NbrInt.Integer;  twoBySqrtPi: NbrRe.Real;
	(* Whenever NbrRe.Real is a 32-bit real, define the following arrays. *)
	erfcP1: ARRAY 3 OF NbrRe.Real;
	erfcP, erfcQ1: ARRAY 4 OF NbrRe.Real;
	erfcQ: ARRAY 5 OF NbrRe.Real;
	(* Or whenever NbrRe.Real is a 64-bit real, define the following arrays. *)
	(*  erfcP1: ARRAY 6 OF NbrRe.Real;
	erfcP, erfcQ1: ARRAY 7 OF NbrRe.Real;
	erfcQ: ARRAY 8 OF NbrRe.Real;  *)

TYPE
	ErfP = OBJECT (MathReSeries.Coefficient)

		PROCEDURE Evaluate;
		VAR m: NbrInt.Integer;
		BEGIN
			IF n = 0 THEN coef := 0
			ELSIF n = 1 THEN coef := 1
			ELSE
				coef := 2 * (n - 1) * x;  m := n + 1;
				IF NbrInt.Odd( m ) THEN coef := -coef END
			END;
			IF n > maxIterations THEN eos := TRUE;  DataErrors.ReWarning( x, "Did not converge -  timed out." ) END
		END Evaluate;

	END ErfP;

	ErfQ = OBJECT (MathReSeries.Coefficient)

		PROCEDURE Evaluate;
		BEGIN
			IF n = 0 THEN coef := 0
			ELSIF n = 1 THEN coef := 1
			ELSE coef := 1 + 2 * (n - 1)
			END
		END Evaluate;

	END ErfQ;

	PROCEDURE Erfc( x: NbrRe.Real ): NbrRe.Real;
	VAR max: NbrInt.Integer;  erfc: NbrRe.Real;
	BEGIN
		max := 26;   (* Asymptote reached by this value. *)
		IF x > max THEN erfc := 0
		ELSIF x < -max THEN erfc := 2
		ELSE
			IF NbrRe.Abs( x ) > 9 THEN
				erfc := MathReSeries.TruncatedRationalFunction( erfcP1, erfcQ1, NbrRe.Abs( x ) )
			ELSE erfc := MathReSeries.TruncatedRationalFunction( erfcP, erfcQ, NbrRe.Abs( x ) )
			END;
			erfc := erfc * NbrRe.Exp( -x * x );
			IF x < 0 THEN erfc := 2 - erfc END
		END;
		RETURN erfc
	END Erfc;

	PROCEDURE Fn*( x: NbrRe.Real ): NbrRe.Real;
	VAR erf: NbrRe.Real;  p: ErfP;  q: ErfQ;
	BEGIN
		IF NbrRe.Abs( x ) > 0.1 THEN erf := 1 - Erfc( x )
		ELSE
			NEW( p );  NEW( q );
			erf := twoBySqrtPi * MathRe.Exp( -x * x ) * MathReSeries.ContinuedFraction( p, q, x )
		END;
		RETURN erf
	END Fn;

(*
	PROCEDURE CplxFn*( z: NbrCplx.Complex ): NbrCplx.Complex;
	VAR
	BEGIN
	END CplxFn;
*)


BEGIN
	maxIterations := 1000;  twoBySqrtPi := 2 / MathRe.Sqrt( NbrRe.Pi );
	(* Whenever NbrRe.Real is a 32-bit real, use the following eonstants. *)
	(* Constants from Table ERFC 5663 from "Computer Approximations".   *)
	erfcP[0] := 1.000464117E1;  erfcP[1] := 8.426552865;  erfcP[2] := 3.460259332;  erfcP[3] := 5.623536121E-1;
	erfcQ[0] := 1.000464117E1;  erfcQ[1] := 1.971558074E1;  erfcQ[2] := 1.570228809E1;  erfcQ[3] := 6.090748787;
	erfcQ[4] := 1.0;
	(* Constants from Table ERFC 5722 from "Computer Approximations".  *)
	erfcP1[0] := 6.141050179E-1;  erfcP1[1] := 3.295899049E-1;  erfcP1[2] := 5.641895902E-1;
	erfcQ1[0] := 2.953483222E-1;  erfcQ1[1] := 1.588352760;  erfcQ1[2] := 5.841849230E-1;  erfcQ1[3] := 1.0
	(* Or, whenever NbrRe.Real is a 64-bit real, use the following eonstants. *)
	(*  (* Constants from Table ERFC 5666 from "Computer Approximations". *)
	erfcP[0] := 4.4041373582475223325269D2;  erfcP[1] := 6.25686535769683006135193D2;
	erfcP[2] := 4.483171659914834429345063D2;  erfcP[3] := 1.9184014058796685752390301D2;
	erfcP[4] := 5.0991697628253203795669523D1;  erfcP[5] := 7.9239298287415448527194039D0;
	erfcP[6] := 0.56419994559825748305038673D0;  erfcQ[0] := 4.4041373582475221430599D2;
	erfcQ[1] := 1.122640220177046578853681D3;  erfcQ[2] := 1.2746672667576622866580268D3;
	erfcQ[3] := 8.3881036547840644197941778D2;  erfcQ[4] := 3.47122928811784331429078057D2;
	erfcQ[5] := 9.08727112035370362595507371D1;  erfcQ[6] := 1.404533730546113829847322587D1;
	erfcQ[7] := 1.0D0;
	(* Constants from Table ERFC 5725 from "Computer Approximations". *)
	erfcP1[0] := 2.9788656263939928862D0;  erfcP1[1] := 7.409740605964741794425D0;
	erfcP1[2] := 6.1602098531096305440906D0;  erfcP1[3] := 5.019049726784267463450058D0;
	erfcP1[4] := 1.275366644729965952479585264D0;  erfcP1[5] := 0.5641895835477550741253201704D0;
	erfcQ1[0] := 3.3690752069827527677D0;  erfcQ1[1] := 9.608965327192787870698D0;
	erfcQ1[2] := 1.708144074746600431571095D1;  erfcQ1[3] := 1.20489519278551290360340491D1;
	erfcQ1[4] := 9.396034016235054150430579648D0;  erfcQ1[5] := 2.260528520767326969591866945D0;
	erfcQ1[6] := 1.0D0  *)
END MathErf.