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

MODULE CalcGauss;   (** AUTHOR "adf"; PURPOSE "Accurate computation of an integral"; *)

(* To change to 64-bit reals, go to the module body and select the appropriate constants commented in light red. *)

(**  Gauss-Kronrod integrators that solve to a specified error tolerance achieved by automatic subdivision.  *)

IMPORT NbrInt, NbrRe, NbrCplx, Data, DataLists, CalcFn;

CONST
	(** Admissible parameters to pass that select the 'integrator'.
				Coarse  should only be selected for smooth integrands.
				Medium  should be used as the default.
				Fine  should be selected for oscillating integrands. *)
	Coarse* = 99;  Medium* = 100;  Fine* = 101;
	(** Status of an integration, i.e., the admissible values for the returned parameter 'res'. *)
	OKay* = 0;  MaxSubDivReached* = 1;  RoundoffError* = 2;  RoughIntegrand* = 3;

VAR
	(** Upper bound on the number of subintervals of integration. *)
	MaxIntervals-: NbrInt.Integer;
	node8, wgtGauss4, wgtKronrod8, node16, wgtGauss8, wgtKronrod16, node31, wgtGauss15,
	wgtKronrod31: POINTER TO ARRAY OF NbrRe.Real;

TYPE
	(* The Read and Write methods are not required because Intervals belong to lists that are not saved to file. *)

	Interval = OBJECT (Data.Datum)
	VAR a, b, error: NbrRe.Real;

		PROCEDURE & Initialize*;
		BEGIN
			Initialize^;
			(* Intialize the local data. *)
			a := 0;  b := 0;  error := 0
		END Initialize;

		PROCEDURE Copy( VAR copy: Data.Datum );
		VAR new, obj: Interval;
		BEGIN
			(* Create object copy. *)
			IF (copy = NIL ) OR ~(copy IS Interval) THEN NEW( new );  copy := new END;
			(* Copy the lower-level data structures first. *)
			Copy^( copy );
			(* Type cast copy so that the local data structure can be copied. *)
			obj := copy( Interval );
			(* Make a deep copy of the local data to obj. *)
			obj.a := a;  obj.b := b;  obj.error := error;
			(* Reassign the copied object for returning. *)
			copy := obj
		END Copy;

	END Interval;

	ReInterval = OBJECT (Interval)
	VAR soln: NbrRe.Real;

		PROCEDURE & Initialize*;
		BEGIN
			Initialize^;
			(* Intialize the local data. *)
			soln := 0
		END Initialize;

		PROCEDURE Copy( VAR copy: Data.Datum );
		VAR new, obj: ReInterval;
		BEGIN
			(* Create object copy. *)
			IF (copy = NIL ) OR ~(copy IS ReInterval) THEN NEW( new );  copy := new END;
			(* Copy the lower-level data structures first. *)
			Copy^( copy );
			(* Type cast copy so that the local data structure can be copied. *)
			obj := copy( ReInterval );
			(* Make a deep copy of the local data to obj. *)
			obj.soln := soln;
			(* Reassign the copied object for returning. *)
			copy := obj
		END Copy;

	END ReInterval;

	CplxInterval = OBJECT (Interval)
	VAR soln: NbrCplx.Complex;

		PROCEDURE & Initialize*;
		BEGIN
			Initialize^;
			(* Intialize the local data. *)
			soln := 0
		END Initialize;

		PROCEDURE Copy( VAR copy: Data.Datum );
		VAR new, obj: CplxInterval;
		BEGIN
			(* Create object copy. *)
			IF (copy = NIL ) OR ~(copy IS CplxInterval) THEN NEW( new );  copy := new END;
			(* Copy the lower-level data structures first. *)
			Copy^( copy );
			(* Type cast copy so that the local data structure can be copied. *)
			obj := copy( CplxInterval );
			(* Make a deep copy of the local data to obj. *)
			obj.soln := soln;
			(* Reassign the copied object for returning. *)
			copy := obj
		END Copy;

	END CplxInterval;

	PROCEDURE GetKey( a, b, atX: NbrRe.Real ): Data.Key;
	VAR k: Data.Key;
	BEGIN
		k := NbrRe.Int( MaxIntervals * (atX - a)/(b - a) );  RETURN k
	END GetKey;

	PROCEDURE ReGaussKronrod( f: CalcFn.ReArg;  fromX, toX: NbrRe.Real;  integrator: NbrInt.Integer;
												  VAR result, absError, absResult: NbrRe.Real );
	(*  f :  integrand
			fromX :  lower limit of integration
			toX (> fromX) :  upper limit of integration
			integrator N  {Coarse, Medium, Fine}
			result :  approximation to the integral
			absError :  estimate of the absolute error
			absResult :  approximation to the integral of ABS(f(x)) over [fromX, toX]
		*)
	VAR i, len: NbrInt.Integer;
		abscissa, center, fCenter, fSum, halfLength, resultGauss, resultKronrod, tolerance: NbrRe.Real;
		fAbove, fBelow, node, wgtGauss, wgtKronrod: POINTER TO ARRAY OF NbrRe.Real;
	BEGIN
		IF integrator = Coarse THEN
			len := 8;  NEW( node, 8 );  NEW( wgtGauss, 4 );  NEW( wgtKronrod, 8 );
			FOR i := 0 TO 7 DO node[i] := node8[i];  wgtKronrod[i] := wgtKronrod8[i] END;
			FOR i := 0 TO 3 DO wgtGauss[i] := wgtGauss4[i]  END
		ELSIF integrator = Fine THEN
			len := 31;  NEW( node, 31 );  NEW( wgtGauss, 15 );  NEW( wgtKronrod, 31 );
			FOR i := 0 TO 30 DO node[i] := node31[i];  wgtKronrod[i] := wgtKronrod31[i] END;
			FOR i := 0 TO 14 DO wgtGauss[i] := wgtGauss15[i]  END
		ELSE  (* integrator = Medium, the default *)
			len := 16;  NEW( node, 16 );  NEW( wgtGauss, 8 );  NEW( wgtKronrod, 16 );
			FOR i := 0 TO 15 DO node[i] := node16[i];  wgtKronrod[i] := wgtKronrod16[i] END;
			FOR i := 0 TO 7 DO wgtGauss[i] := wgtGauss8[i]  END
		END;
		NEW( fAbove, len - 1 );  NEW( fBelow, len - 1 );  center := (fromX + toX) / 2;  halfLength := (toX - fromX) / 2;
		fCenter := f( center );  resultKronrod := wgtKronrod[len - 1] * fCenter;  absResult := ABS( resultKronrod );
		IF NbrInt.Odd( len ) THEN resultGauss := 0 ELSE resultGauss := wgtGauss[(len DIV 2) - 1] * fCenter END;
		FOR i := 0 TO len - 2 DO
			abscissa := halfLength * node[i];  fAbove[i] := f( center + abscissa );  fBelow[i] := f( center - abscissa );
			fSum := fAbove[i] + fBelow[i];  resultKronrod := resultKronrod + wgtKronrod[i] * fSum;
			absResult := absResult + wgtKronrod[i] * (ABS( fAbove[i] ) + ABS( fBelow[i] ));
			IF NbrInt.Odd( i ) THEN resultGauss := resultGauss + wgtGauss[i DIV 2] * fSum END
		END;
		result := halfLength * resultKronrod;  absResult := halfLength * absResult;
		absError := ABS( halfLength * (resultKronrod - resultGauss) );  tolerance := 50 * NbrRe.Epsilon;
		IF absResult > 1/NbrRe.MaxNbr*tolerance THEN absError := NbrRe.Max( absError, tolerance * absResult ) END
	END ReGaussKronrod;

(** Computes  I(f) = xab f(x) dx  to a specified error tolerance. *)
	PROCEDURE Solve*( f: CalcFn.ReArg;  a, b: NbrRe.Real;  integrator: NbrInt.Integer;  VAR error: NbrRe.Real;
						   VAR res: NbrInt.Integer ): NbrRe.Real;
	(* This algorithm is a partial port of the FORTRAN subroutine DPAG from the QUADPACK library of routines.
		R. Piessens, E. de Doncker-Kapenga, C. überhuber, and D. K. Kahaner.  QUADPACK - A Subroutine Package for
		Automatic Integration.  No. 1 in: Springer Series in Computational Mathematics, Springer, Berlin, 1983.   *)
	VAR ignor, successful: BOOLEAN;  key, maxKey: Data.Key;  sign: NbrInt.Integer;
		aa, bb, maxError, maxTol, midPoint, sumError, sumResult, tolerance: NbrRe.Real;  datum: Data.Datum;
		interval, intervalL, intervalR: ReInterval;  history: DataLists.List;

		PROCEDURE Create( withKey: Data.Key ): ReInterval;
		VAR int: ReInterval;
		BEGIN
			NEW( int );  int.SetKey( withKey );  RETURN int
		END Create;

		PROCEDURE Update( VAR int: ReInterval );
		VAR absApprox, absError, approx: NbrRe.Real;
		BEGIN
			ReGaussKronrod( f, int.a, int.b, integrator, approx, absError, absApprox );  int.soln := approx;
			int.error := absError * (int.b - int.a) / (bb - aa);
			IF (absError < 100 * NbrRe.Epsilon * absApprox) & (absError > tolerance) THEN
				res := RoundoffError
			END;
			IF NbrRe.Abs( int.b ) < ((1 + 100*NbrRe.Epsilon) * (NbrRe.Abs( int.a ) + 1000/NbrRe.MaxNbr)) THEN
				res := RoughIntegrand
			END
		END Update;

	BEGIN
		IF f # NIL THEN
			res := OKay;
			IF a = b THEN error := 0;  sign := 1;  sumResult := 0
					ELSE  (* integrate *)
				maxTol := 0.1;  tolerance := NbrRe.Max( 100 * NbrRe.Epsilon, NbrRe.Min( maxTol, error ) );
				IF a < b THEN sign := 1;  aa := a;  bb := b ELSE sign := -1;  aa := b;  bb := a END;
				key := GetKey( aa, bb, bb );  interval := Create( key );  interval.a := aa;  interval.b := bb;
				Update( interval );  sumResult := interval.soln;  sumError := interval.error;
				IF NbrRe.Abs( sumResult ) < 1 THEN error := sumError
				ELSE error := NbrRe.Abs( sumError / sumResult )
				END;
				IF error > tolerance THEN
					NEW( history );  history.Insert( interval, ignor );
					(* Refine the integration. *)
					LOOP
					(* Search for the interval with the largest error estimate. *)
						history.rider.Home;  datum := history.rider.Inspect();  interval := datum( ReInterval );
						maxError := interval.error;  interval.GetKey( maxKey );
						WHILE ~history.rider.eol DO
							history.rider.Next;  datum := history.rider.Inspect();  interval := datum( ReInterval );
							IF interval.error > maxError THEN
								maxError := interval.error;  interval.GetKey( maxKey )
							END
						END;
						(* Bisect the interval with the largest error estimate and integrate these two subintervals. *)
						datum := history.rider.Retrieve( maxKey );  interval := datum( ReInterval );
						history.Delete( maxKey, ignor );  midPoint := (interval.a + interval.b) / 2;
						key := GetKey( interval.a, interval.b, midPoint );  intervalL := Create( key );
						intervalL.a := interval.a;  intervalL.b := midPoint;  Update( intervalL );
						history.Insert( intervalL, successful );
						IF successful & (key # maxKey) THEN
							intervalR := Create( maxKey );  intervalR.a := midPoint;  intervalR.b := interval.b;
							Update( intervalR );  history.Insert( intervalR, ignor )
						ELSE
							IF successful THEN history.Delete( key, ignor ) END;
							history.Insert( interval, ignor );  res := MaxSubDivReached
						END;
						IF res # OKay THEN EXIT END;
						sumResult := sumResult + intervalL.soln + intervalR.soln - interval.soln;
						sumError := sumError + intervalL.error + intervalR.error - interval.error;
						IF NbrRe.Abs( sumResult ) < 1 THEN error := sumError
						ELSE error := NbrRe.Abs( sumError / sumResult )
						END;
						IF error < tolerance THEN EXIT END
					END
				END
			END
		ELSE sign := 1;  sumResult := 0
		END;
		RETURN sign * sumResult
	END Solve;

	PROCEDURE CplxGaussKronrod( f: CalcFn.MixedArg;  fromX, toX: NbrRe.Real;  z: NbrCplx.Complex;
													integrator: NbrInt.Integer;  VAR result: NbrCplx.Complex;
													VAR absError, absResult: NbrRe.Real );
	(*  f :  integrand
			fromX :  lower limit of integration
			toX (> fromX) :  upper limit of integration
			z  is a passed parameter to  f
			integrator N  {Coarse, Medium, Fine}
			result :  approximation to the integral
			absError :  estimate of the absolute error
			absResult :  approximation to the integral of ABS(f(x)) over [fromX, toX]
	*)
	VAR i, len: NbrInt.Integer;  abscissa, center, halfLength, tolerance: NbrRe.Real;
		fCenter, fSum, resultGauss, resultKronrod: NbrCplx.Complex;
		node, wgtGauss, wgtKronrod: POINTER TO ARRAY OF NbrRe.Real;
		fAbove, fBelow: POINTER TO ARRAY OF NbrCplx.Complex;
	BEGIN
		IF integrator = Coarse THEN
			len := 8;  NEW( node, 8 );  NEW( wgtGauss, 4 );  NEW( wgtKronrod, 8 );
			FOR i := 0 TO 7 DO node[i] := node8[i];  wgtKronrod[i] := wgtKronrod8[i] END;
			FOR i := 0 TO 3 DO wgtGauss[i] := wgtGauss4[i]  END
		ELSIF integrator = Fine THEN
			len := 31;  NEW( node, 31 );  NEW( wgtGauss, 15 );  NEW( wgtKronrod, 31 );
			FOR i := 0 TO 30 DO node[i] := node31[i];  wgtKronrod[i] := wgtKronrod31[i] END;
			FOR i := 0 TO 14 DO wgtGauss[i] := wgtGauss15[i]  END
		ELSE  (* integrator = Medium, the default *)
			len := 16;  NEW( node, 16 );  NEW( wgtGauss, 8 );  NEW( wgtKronrod, 16 );
			FOR i := 0 TO 15 DO node[i] := node16[i];  wgtKronrod[i] := wgtKronrod16[i] END;
			FOR i := 0 TO 7 DO wgtGauss[i] := wgtGauss8[i]  END
		END;
		NEW( fAbove, len - 1 );  NEW( fBelow, len - 1 );  center := (fromX + toX) / 2;  halfLength := (toX - fromX) / 2;
		fCenter := f( center, z );  resultKronrod := wgtKronrod[len - 1] * fCenter;
		absResult := NbrCplx.Abs( resultKronrod );
		IF NbrInt.Odd( len ) THEN resultGauss := 0 ELSE resultGauss := wgtGauss[(len DIV 2) - 1] * fCenter END;
		FOR i := 0 TO len - 2 DO
			abscissa := halfLength * node[i];  fAbove[i] := f( center + abscissa, z );  fBelow[i] := f( center - abscissa, z );
			fSum := fAbove[i] + fBelow[i];  resultKronrod := resultKronrod + wgtKronrod[i] * fSum;
			absResult := absResult + wgtKronrod[i] * (NbrCplx.Abs( fAbove[i] ) + NbrCplx.Abs( fBelow[i] ));
			IF NbrInt.Odd( i ) THEN resultGauss := resultGauss + wgtGauss[i DIV 2] * fSum END
		END;
		result := halfLength * resultKronrod;  absResult := halfLength * absResult;
		absError := NbrCplx.Abs( halfLength * (resultKronrod - resultGauss) );  tolerance := 50 * NbrRe.Epsilon;
		IF absResult > 1/NbrRe.MaxNbr*tolerance THEN absError := NbrRe.Max( absError, tolerance * absResult ) END
	END CplxGaussKronrod;

(** Computes  I(f) = xab f(x,z) dx  to a specified error tolerance. *)
	PROCEDURE SolveCplx*( f: CalcFn.MixedArg;  a, b: NbrRe.Real;  z: NbrCplx.Complex;  integrator: NbrInt.Integer;
								  VAR error: NbrRe.Real;  VAR res: NbrInt.Integer ): NbrCplx.Complex;
	VAR ignor, successful: BOOLEAN;  key, maxKey: Data.Key;  sign: NbrInt.Integer;
		aa, bb, maxError, maxTol, midPoint, sumError, tolerance: NbrRe.Real;  sumResult: NbrCplx.Complex;
		datum: Data.Datum;  interval, intervalL, intervalR: CplxInterval;  history: DataLists.List;

		PROCEDURE Create( withKey: Data.Key ): CplxInterval;
		VAR int: CplxInterval;
		BEGIN
			NEW( int );  int.SetKey( withKey );  RETURN int
		END Create;

		PROCEDURE Update( VAR int: CplxInterval );
		VAR absApprox, absError: NbrRe.Real;  approx: NbrCplx.Complex;
		BEGIN
			CplxGaussKronrod( f, int.a, int.b, z, integrator, approx, absError, absApprox );  int.soln := approx;
			int.error := absError * (int.b - int.a) / (bb - aa);
			IF (absError < 100 * NbrRe.Epsilon * absApprox) & (absError > tolerance) THEN
				res := RoundoffError
			END;
			IF NbrRe.Abs( int.b ) < ((1 + 100*NbrRe.Epsilon) * (NbrRe.Abs( int.a ) + 1000/NbrRe.MaxNbr)) THEN
				res := RoughIntegrand
			END
		END Update;

	BEGIN
		IF f # NIL THEN
			res := OKay;
			IF a = b THEN error := 0;  sign := 1;  sumResult := 0
					ELSE  (* integrate *)
				maxTol := 0.1;  tolerance := NbrRe.Max( 100 * NbrRe.Epsilon, NbrRe.Min( maxTol, error ) );
				IF a < b THEN sign := 1;  aa := a;  bb := b ELSE sign := -1;  aa := b;  bb := a END;
				key := GetKey( aa, bb, bb );  interval := Create( key );  interval.a := aa;  interval.b := bb;
				Update( interval );  sumResult := interval.soln;  sumError := interval.error;
				IF NbrCplx.Abs( sumResult ) < 1 THEN error := sumError
				ELSE error := NbrCplx.Abs( sumError / sumResult )
				END;
				IF error > tolerance THEN
					NEW( history );  history.Insert( interval, ignor );
					(* Refine the integration. *)
					LOOP
					(* Search for the interval with the largest error estimate. *)
						history.rider.Home;  datum := history.rider.Inspect();  interval := datum( CplxInterval );
						maxError := interval.error;  interval.GetKey( maxKey );
						WHILE ~history.rider.eol DO
							history.rider.Next;  datum := history.rider.Inspect();  interval := datum( CplxInterval );
							IF interval.error > maxError THEN
								maxError := interval.error;  interval.GetKey( maxKey )
							END
						END;
						(* Bisect the interval with the largest error estimate and integrate these two subintervals. *)
						datum := history.rider.Retrieve( maxKey );  interval := datum( CplxInterval );
						history.Delete( maxKey, ignor );  midPoint := (interval.a + interval.b) / 2;
						key := GetKey( interval.a, interval.b, midPoint );  intervalL := Create( key );
						intervalL.a := interval.a;  intervalL.b := midPoint;  Update( intervalL );
						history.Insert( intervalL, successful );
						IF successful & (key # maxKey) THEN
							intervalR := Create( maxKey );  intervalR.a := midPoint;  intervalR.b := interval.b;
							Update( intervalR );  history.Insert( intervalR, ignor )
						ELSE
							IF successful THEN history.Delete( key, ignor ) END;
							history.Insert( interval, ignor );  res := MaxSubDivReached
						END;
						IF res # OKay THEN EXIT END;
						sumResult := sumResult + intervalL.soln + intervalR.soln - interval.soln;
						sumError := sumError + intervalL.error + intervalR.error - interval.error;
						IF NbrCplx.Abs( sumResult ) < 1 THEN error := sumError
						ELSE error := NbrCplx.Abs( sumError / sumResult )
						END;
						IF error < tolerance THEN EXIT END
					END
				END
			END
		ELSE sign := 1;  sumResult := 0
		END;
		RETURN sign * sumResult
	END SolveCplx;

	PROCEDURE Quadrature;
	(* Gauss-Kronrod integration parameters for solving  I(f) = x-11 f(y) dy  via  I(f) p Si=1N wi f(ni).
			Because of symmetry over [-1, 1], only the positive nodes and their corresponding weights are given.
			Kronrod integration takes place at all the nodes.  Gauss integration only takes place at the odd nodes.
			These weights and nodes came from the FORTRAN subroutine DPAG from the QUADPACK library of routines.
			  R. Piessens, E. de Doncker-Kapenga, C. überhuber, and D. K. Kahaner.  QUADPACK - A Subroutine Package for
			  Automatic Integration.  No. 1 in: Springer Series in Computational Mathematics, Springer, Berlin, 1983.  *)
	BEGIN
		(* Whenever NbrRe.Real is a 32-bit real, use the following eonstants. *)
		 (* For Coarse integration *)
		NEW( node8, 8 );  NEW( wgtGauss4, 4 );  NEW( wgtKronrod8, 8 );  node8[0] := 0.99145537112081263920E0;
		node8[1] := 0.949107912;  node8[2] := 0.864864423;  node8[3] := 0.741531185;  node8[4] := 0.586087235;
		node8[5] := 0.405845151;  node8[6] := 0.207784955;  node8[7] := 0.0;
		wgtGauss4[0] := 0.129484966;  wgtGauss4[1] := 0.279705391;  wgtGauss4[2] := 0.381830051;
		wgtGauss4[3] := 0.417959183;
		wgtKronrod8[0] := 0.022935322;
		wgtKronrod8[1] := 0.063092093;  wgtKronrod8[2] := 0.104790010;  wgtKronrod8[3] := 0.140653260;
		wgtKronrod8[4] := 0.169004727;  wgtKronrod8[5] := 0.190350578;  wgtKronrod8[6] := 0.204432940;
		wgtKronrod8[7] := 0.209482141;
		(* For Medium integration *)
		NEW( node16, 16 );  NEW( wgtGauss8, 8 );  NEW( wgtKronrod16, 16 );
		node16[0] := 0.998002299;  node16[1] := 0.987992518;  node16[2] := 0.967739076;  node16[3] := 0.937273392;
		node16[4] := 0.897264532;  node16[5] := 0.848206583;  node16[6] := 0.790418501;  node16[7] := 0.724417731;
		node16[8] := 0.650996741;  node16[9] := 0.570972173;  node16[10] := 0.48508186;  node16[11] := 0.39415135;
		node16[12] := 0.29918001;  node16[13] := 0.20119409;  node16[14] := 0.10114207;  node16[15] := 0.0;
		wgtGauss8[0] := 0.030753242;  wgtGauss8[1] := 0.070366047;  wgtGauss8[2] := 0.107159220;
		wgtGauss8[3] := 0.139570678;  wgtGauss8[4] := 0.166269206;  wgtGauss8[5] := 0.186161000;
		wgtGauss8[6] := 0.198431485;  wgtGauss8[7] := 0.202578242;
		wgtKronrod16[0] := 0.005377480;  wgtKronrod16[1] := 0.015007947;  wgtKronrod16[2] := 0.025460847;
		wgtKronrod16[3] := 0.035346361;  wgtKronrod16[4] := 0.044589751;  wgtKronrod16[5] := 0.053481525;
		wgtKronrod16[6] := 0.062009568;  wgtKronrod16[7] := 0.069854121;  wgtKronrod16[8] := 0.076849681;
		wgtKronrod16[9] := 0.083080503;  wgtKronrod16[10] := 0.088564443;  wgtKronrod16[11] := 0.093126598;
		wgtKronrod16[12] := 0.096642727;  wgtKronrod16[13] := 0.099173599;  wgtKronrod16[14] := 0.100769846;
		wgtKronrod16[15] := 0.101330007;
		(* For Fine integration *)
		NEW( node31, 31 );  NEW( wgtGauss15, 15 );  NEW( wgtKronrod31, 31 );
		node31[0] := 0.999484410;  node31[1] := 0.996893484;  node31[2] := 0.991630997;  node31[3] := 0.983668123;
		node31[4] := 0.973116323;  node31[5] := 0.960021865;  node31[6] := 0.944374445;  node31[7] := 0.926200047;
		node31[8] := 0.905573308;  node31[9] := 0.882560536;  node31[10] := 0.857205234;  node31[11] := 0.829565762;
		node31[12] := 0.799727836;  node31[13] := 0.767777432;  node31[14] := 0.733790062;  node31[15] := 0.697850495;
		node31[16] := 0.660061064;  node31[17] := 0.620526183;  node31[18] := 0.579345236;  node31[19] := 0.536624148;
		node31[20] := 0.492480468;  node31[21] := 0.447033770;  node31[22] := 0.400401255;  node31[23] := 0.352704726;
		node31[24] := 0.304073202;  node31[25] := 0.254636926;  node31[26] := 0.204525117;  node31[27] := 0.153869914;
		node31[28] := 0.102806938;  node31[29] := 0.051471843;  node31[30] := 0.0;
		wgtGauss15[0] := 0.007968192;  wgtGauss15[1] := 0.018466468;  wgtGauss15[2] := 0.028784708;
		wgtGauss15[3] := 0.038799193;  wgtGauss15[4] := 0.048402673;  wgtGauss15[5] := 0.057493156;
		wgtGauss15[6] := 0.065974230;  wgtGauss15[7] := 0.073755975;  wgtGauss15[8] := 0.080755895;
		wgtGauss15[9] := 0.086899787;  wgtGauss15[10] := 0.092122522;  wgtGauss15[11] := 0.096368737;
		wgtGauss15[12] := 0.099593421;  wgtGauss15[13] := 0.101762390;  wgtGauss15[14] := 0.102852653;
		wgtKronrod31[0] := 0.001389014;  wgtKronrod31[1] := 0.003890461;  wgtKronrod31[2] := 0.006630704;
		wgtKronrod31[3] := 0.009273280;  wgtKronrod31[4] := 0.011823015;  wgtKronrod31[5] := 0.014369730;
		wgtKronrod31[6] := 0.016920889;  wgtKronrod31[7] := 0.019414141;  wgtKronrod31[8] := 0.021828036;
		wgtKronrod31[9] := 0.024191162;  wgtKronrod31[10] := 0.026509955;  wgtKronrod31[11] := 0.028754049;
		wgtKronrod31[12] := 0.030907258;  wgtKronrod31[13] := 0.032981447;  wgtKronrod31[14] := 0.034979338;
		wgtKronrod31[15] := 0.036882365;  wgtKronrod31[16] := 0.038678946;  wgtKronrod31[17] := 0.040374539;
		wgtKronrod31[18] := 0.041969810;  wgtKronrod31[19] := 0.043452540;  wgtKronrod31[20] := 0.044814800;
		wgtKronrod31[21] := 0.046059238;  wgtKronrod31[22] := 0.047185547;  wgtKronrod31[23] := 0.048185862;
		wgtKronrod31[24] := 0.049055435;  wgtKronrod31[25] := 0.049795683;  wgtKronrod31[26] := 0.050405921;
		wgtKronrod31[27] := 0.050881796;  wgtKronrod31[28] := 0.051221548;  wgtKronrod31[29] := 0.051426129;
		wgtKronrod31[30] := 0.051494729
		(* Or, whenever NbrRe.Real is a 64-bit real, use the following eonstants. *)
		(* (* For Coarse integration *)
		NEW( node8, 8 );  NEW( wgtGauss4, 4 );  NEW( wgtKronrod8, 8 );  node8[0] := 0.99145537112081263920D0;
		node8[1] := 0.94910791234275852452D0;  node8[2] := 0.86486442335976907278D0;
		node8[3] := 0.74153118559939443986D0;  node8[4] := 0.58608723546769113029D0;
		node8[5] := 0.40584515137739716690D0;  node8[6] := 0.20778495500789846760D0;
		node8[7] := 0.00000000000000000000D0;  wgtGauss4[0] := 0.12948496616886969327D0;
		wgtGauss4[1] := 0.27970539148927666790D0;  wgtGauss4[2] := 0.38183005050511894495D0;
		wgtGauss4[3] := 0.41795918367346938775D0;  wgtKronrod8[0] := 0.02293532201052922496D0;
		wgtKronrod8[1] := 0.06309209262997855329D0;  wgtKronrod8[2] := 0.10479001032225018383D0;
		wgtKronrod8[3] := 0.14065325971552591874D0;  wgtKronrod8[4] := 0.16900472663926790282D0;
		wgtKronrod8[5] := 0.19035057806478540991D0;  wgtKronrod8[6] := 0.20443294007529889241D0;
		wgtKronrod8[7] := 0.20948214108472782801D0;
		(* For Medium integration *)
		NEW( node16, 16 );  NEW( wgtGauss8, 8 );  NEW( wgtKronrod16, 16 );
		node16[0] := 0.99800229869339706028D0;  node16[1] := 0.98799251802048542848D0;
		node16[2] := 0.96773907567913913425D0;  node16[3] := 0.93727339240070590430D0;
		node16[4] := 0.89726453234408190088D0;  node16[5] := 0.84820658341042721620D0;
		node16[6] := 0.79041850144246593296D0;  node16[7] := 0.72441773136017004741D0;
		node16[8] := 0.65099674129741697053D0;  node16[9] := 0.57097217260853884753D0;
		node16[10] := 0.48508186364023968069D0;  node16[11] := 0.39415134707756336989D0;
		node16[12] := 0.29918000715316881216D0;  node16[13] := 0.20119409399743452230D0;
		node16[14] := 0.10114206691871749902D0;  node16[15] := 0.00000000000000000000D0;
		wgtGauss8[0] := 0.03075324199611726835D0;  wgtGauss8[1] := 0.07036604748810812470D0;
		wgtGauss8[2] := 0.10715922046717193501D0;  wgtGauss8[3] := 0.13957067792615431444D0;
		wgtGauss8[4] := 0.16626920581699393355D0;  wgtGauss8[5] := 0.18616100001556221102D0;
		wgtGauss8[6] := 0.19843148532711157645D0;  wgtGauss8[7] := 0.20257824192556127288D0;
		wgtKronrod16[0] := 0.00537747987292334898D0;  wgtKronrod16[1] := 0.01500794732931612253D0;
		wgtKronrod16[2] := 0.02546084732671532018D0;  wgtKronrod16[3] := 0.03534636079137584622D0;
		wgtKronrod16[4] := 0.04458975132476487660D0;  wgtKronrod16[5] := 0.05348152469092808726D0;
		wgtKronrod16[6] := 0.06200956780067064028D0;  wgtKronrod16[7] := 0.06985412131872825870D0;
		wgtKronrod16[8] := 0.07684968075772037889D0;  wgtKronrod16[9] := 0.08308050282313302103D0;
		wgtKronrod16[10] := 0.08856444305621177064D0;  wgtKronrod16[11] := 0.09312659817082532122D0;
		wgtKronrod16[12] := 0.09664272698362367850D0;  wgtKronrod16[13] := 0.09917359872179195933D0;
		wgtKronrod16[14] := 0.10076984552387559504D0;  wgtKronrod16[15] := 0.10133000701479154901D0;
		(* For Fine integration *)
		NEW( node31, 31 );  NEW( wgtGauss15, 15 );  NEW( wgtKronrod31, 31 );
		node31[0] := 0.99948441005049063757D0;  node31[1] := 0.99689348407464954027D0;
		node31[2] := 0.99163099687040459485D0;  node31[3] := 0.98366812327974720997D0;
		node31[4] := 0.97311632250112626837D0;  node31[5] := 0.96002186496830751221D0;
		node31[6] := 0.94437444474855997941D0;  node31[7] := 0.92620004742927432587D0;
		node31[8] := 0.90557330769990779854D0;  node31[9] := 0.88256053579205268154D0;
		node31[10] := 0.85720523354606109895D0;  node31[11] := 0.82956576238276839744D0;
		node31[12] := 0.79972783582183908301D0;  node31[13] := 0.76777743210482619491D0;
		node31[14] := 0.73379006245322680472D0;  node31[15] := 0.69785049479331579693D0;
		node31[16] := 0.66006106412662696137D0;  node31[17] := 0.62052618298924286114D0;
		node31[18] := 0.57934523582636169175D0;  node31[19] := 0.53662414814201989926D0;
		node31[20] := 0.49248046786177857499D0;  node31[21] := 0.44703376953808917678D0;
		node31[22] := 0.40040125483039439253D0;  node31[23] := 0.35270472553087811347D0;
		node31[24] := 0.30407320227362507737D0;  node31[25] := 0.25463692616788984643D0;
		node31[26] := 0.20452511668230989143D0;  node31[27] := 0.15386991360858354696D0;
		node31[28] := 0.10280693796673703014D0;  node31[29] := 0.05147184255531769583D0;
		node31[30] := 0.00000000000000000000D0;  wgtGauss15[0] := 0.00796819249616660561D0;
		wgtGauss15[1] := 0.01846646831109095914D0;  wgtGauss15[2] := 0.02878470788332336934D0;
		wgtGauss15[3] := 0.03879919256962704959D0;  wgtGauss15[4] := 0.04840267283059405290D0;
		wgtGauss15[5] := 0.05749315621761906648D0;  wgtGauss15[6] := 0.06597422988218049512D0;
		wgtGauss15[7] := 0.07375597473770520626D0;  wgtGauss15[8] := 0.08075589522942021535D0;
		wgtGauss15[9] := 0.08689978720108297980D0;  wgtGauss15[10] := 0.09212252223778612871D0;
		wgtGauss15[11] := 0.09636873717464425963D0;  wgtGauss15[12] := 0.09959342058679526706D0;
		wgtGauss15[13] := 0.10176238974840550459D0;  wgtGauss15[14] := 0.10285265289355884034D0;
		wgtKronrod31[0] := 0.00138901369867700762D0;  wgtKronrod31[1] := 0.00389046112709988405D0;
		wgtKronrod31[2] := 0.00663070391593129217D0;  wgtKronrod31[3] := 0.00927327965951776342D0;
		wgtKronrod31[4] := 0.01182301525349634174D0;  wgtKronrod31[5] := 0.01436972950704580481D0;
		wgtKronrod31[6] := 0.01692088918905327262D0;  wgtKronrod31[7] := 0.01941414119394238117D0;
		wgtKronrod31[8] := 0.02182803582160919229D0;  wgtKronrod31[9] := 0.02419116207808060136D0;
		wgtKronrod31[10] := 0.02650995488233310161D0;  wgtKronrod31[11] := 0.02875404876504129284D0;
		wgtKronrod31[12] := 0.03090725756238776247D0;  wgtKronrod31[13] := 0.03298144705748372603D0;
		wgtKronrod31[14] := 0.03497933802806002413D0;  wgtKronrod31[15] := 0.03688236465182122922D0;
		wgtKronrod31[16] := 0.03867894562472759295D0;  wgtKronrod31[17] := 0.04037453895153595911D0;
		wgtKronrod31[18] := 0.04196981021516424614D0;  wgtKronrod31[19] := 0.04345253970135606931D0;
		wgtKronrod31[20] := 0.04481480013316266319D0;  wgtKronrod31[21] := 0.04605923827100698811D0;
		wgtKronrod31[22] := 0.04718554656929915394D0;  wgtKronrod31[23] := 0.04818586175708712914D0;
		wgtKronrod31[24] := 0.04905543455502977888D0;  wgtKronrod31[25] := 0.04979568342707420635D0;
		wgtKronrod31[26] := 0.05040592140278234684D0;  wgtKronrod31[27] := 0.05088179589874960649D0;
		wgtKronrod31[28] := 0.05122154784925877217D0;  wgtKronrod31[29] := 0.05142612853745902593D0;
		wgtKronrod31[30] := 0.05149472942945156755D0 *)
	END Quadrature;

BEGIN
	Quadrature;  MaxIntervals := NbrInt.MaxNbr
END CalcGauss.