{ This file is part of the Numlib package. Copyright (c) 1986-2000 by Kees van Ginneken, Wil Kortsmit and Loek van Reij of the Computational centre of the Eindhoven University of Technology FPC port Code by Marco van de Voort (marco@freepascal.org) documentation by Michael van Canneyt (Michael@freepascal.org) Integration. This routine is fit for smooth "integrand" so no singularities, sharp edges, or quickly oscillating behaviour. See the file COPYING.FPC, included in this distribution, for details about the copyright. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. **********************************************************************} Unit int; {$I DIRECT.INC} interface uses typ; Var limit : ArbInt; epsrel : ArbFloat; {calc int(x,a,b,f(x)) for a function with a nice behaviour in the interval [A,B]} Procedure int1fr(f: rfunc1r; a, b, ae: ArbFloat; Var integral, err: ArbFloat; Var term: ArbInt); implementation Function amin1(x, y: ArbFloat): ArbFloat; Begin If xy Then amax1 := x Else amax1 := y End; Procedure qk21(f: rfunc1r; a, b: ArbFloat; Var result, abserr, resabs, resasc: ArbFloat); Const xgk: array[1..11] Of ArbFloat = ( 0.9956571630258081, 0.9739065285171717, 0.9301574913557082, 0.8650633666889845, 0.7808177265864169, 0.6794095682990244, 0.5627571346686047, 0.4333953941292472, 0.2943928627014602, 0.1488743389816312, 0); wgk: array[1..11] Of ArbFloat = ( 0.1169463886737187e-1, 0.3255816230796473e-1, 0.5475589657435200e-1, 0.7503967481091995e-1, 0.9312545458369761e-1, 0.1093871588022976, 0.1234919762620659, 0.1347092173114733, 0.1427759385770601, 0.1477391049013385, 0.1494455540029169); wg: array[1..5] Of ArbFloat = ( 0.6667134430868814e-1, 0.1494513491505806, 0.2190863625159820, 0.2692667193099964, 0.2955242247147529); Var absc, centr, dhlgth, fc, fsum, fval1, fval2, hlgth, resg, resk, reskh: ArbFloat; j, jtw, jtwm1: ArbInt; fv1, fv2: ^arfloat1; Begin getmem(fv1, 10*sizeof(ArbFloat)); getmem(fv2, 10*sizeof(ArbFloat)); centr := (a+b)/2; hlgth := (b-a)/2; dhlgth := abs(hlgth); resg := 0; fc := f(centr); resk := wgk[11]*fc; resabs := abs(resk); For j:=1 To 5 Do Begin jtw := 2*j; absc := hlgth*xgk[jtw]; fval1 := f(centr-absc); fval2 := f(centr+absc); fv1^[jtw] := fval1; fv2^[jtw] := fval2; fsum := fval1+fval2; resg := resg+wg[j]*fsum; resk := resk+wgk[jtw]*fsum; resabs := resabs+wgk[jtw]*(abs(fval1)+abs(fval2)) End; For j:=1 To 5 Do Begin jtwm1 := 2*j-1; absc := hlgth*xgk[jtwm1]; fval1 := f(centr-absc); fval2 := f(centr+absc); fv1^[jtwm1] := fval1; fv2^[jtwm1] := fval2; fsum := fval1+fval2; resk := resk+wgk[jtwm1]*fsum; resabs := resabs+wgk[jtwm1]*(abs(fval1)+abs(fval2)) End; reskh := resk/2; resasc := wgk[11]*abs(fc-reskh); For j:=1 To 10 Do resasc := resasc+wgk[j]*(abs(fv1^[j]-reskh)+abs(fv2^[j]-reskh)); result := resk*hlgth; resabs := resabs*dhlgth; resasc := resasc*dhlgth; abserr := abs((resk-resg)*hlgth); If (resasc <> 0) And (abserr <> 0) Then abserr := resasc*amin1(1,exp(1.5*ln(200*abserr/resasc))); If resabs > midget/(50*macheps) Then abserr := amax1((50*macheps)*resabs, abserr); freemem(fv1, 10*sizeof(ArbFloat)); freemem(fv2, 10*sizeof(ArbFloat)); End; Procedure qpsrt(limit: ArbInt; Var last, maxerr: ArbInt; Var ermax, elist1: ArbFloat; Var iord1, nrmax: ArbInt); Var errmax, errmin: ArbFloat; i, ibeg, ido, isucc, j, jbnd, jupbn, k : ArbInt; continue : boolean; elist : arfloat1 absolute elist1; iord : arint1 absolute iord1; Begin If (last<=2) Then Begin iord[1] := 1; iord[2] := 2; maxerr := iord[nrmax]; ermax := elist[maxerr]; exit End; errmax := elist[maxerr]; ido := nrmax-1; i := 0; If ido>0 Then Repeat Inc(i); isucc := iord[nrmax-1]; If errmax>elist[isucc] Then Begin iord[nrmax] := isucc; nrmax := nrmax-1 End Else i := ido Until (i=ido); jupbn := last; If (last>(limit Div 2+2)) Then jupbn := limit+3-last; errmin := elist[last]; jbnd := jupbn-1; ibeg := nrmax+1; If (ibeg>jbnd) Then Begin iord[jbnd] := maxerr; iord[jupbn] := last; maxerr := iord[nrmax]; ermax := elist[maxerr]; exit End; i := ibeg-1; continue := true; while (ie2abs Then tol2 := e1abs*macheps Else tol2 := e2abs*macheps; delta3 := e1-e0; err3 := abs(delta3); If e1abs>e0abs Then tol3 := e1abs*macheps Else tol3 := e0abs*macheps; If (err2<=tol2) And (err3<=tol3) Then Begin result := res; abserr := err2+err3; If abserr<5*macheps*abs(result) Then abserr := 5*macheps*abs(result); exit End; e3 := epstab[k1]; epstab[k1] := e1; delta1 := e1-e3; err1 := abs(delta1); e3abs := abs(e3); If e1abs1e-4) Then Begin continue := true; res := e1+1/ss; epstab[k1] := res; k1 := k1-2; error := err2+abs(res-e2)+err3; If (error<=abserr) Then Begin abserr := error; result := res End End Else n := 2*i-1 End; Inc(i) End; If n=limexp Then n := 2*(limexp Div 2)-1; If Odd(Num) Then ib := 1 Else ib := 2; For i:=1 To newelm+1 Do Begin ib2 := ib+2; epstab[ib] := epstab[ib2]; ib := ib2 End; Move(epstab[num-n+1], epstab[1], n*SizeOf(ArbFloat)); If (nres<4) Then Begin res3la[nres] := result; abserr := giant End Else Begin abserr := abs(result-res3la[3]) + abs(result-res3la[2]) + abs(result-res3la[1]); res3la[1] := res3la[2]; res3la[2] := res3la[3]; res3la[3] := result; If abserr<5*macheps*abs(result) Then abserr := 5*macheps*abs(result) End End; Procedure qagse(f: rfunc1r; a, b, epsabs, epsrel: ArbFloat; limit: ArbInt; Var result, abserr: ArbFloat; Var neval, ier, last: ArbInt); Var abseps, area, area1, area12, area2, a1, a2, b1, b2, correc, defabs, defab1, defab2, dres, erlarg, erlast, errbnd, errmax, error1, error2, erro12, errsum, ertest, resabs, reseps, small: ArbFloat; id, ierro, iroff1, iroff2, iroff3, jupbnd, k, ksgn, ktmin, maxerr, nres, nrmax, numrl2, sr, lsr: ArbInt; extrap, noext, go_on, jump, smallers, p0, p1, p2, p3: boolean; alist, blist, elist, rlist: ^arfloat1; res3la: hulpar; rlist2: stock; iord: ^arint1; Begin sr := sizeof(ArbFloat); lsr := limit*sr; getmem(alist, lsr); getmem(blist, lsr); getmem(elist, lsr); getmem(iord, limit*sizeof(ArbInt)); getmem(rlist, lsr); ier := 0; neval := 0; last := 0; result := 0; abserr := 0; alist^[1] := a; blist^[1] := b; rlist^[1] := 0; elist^[1] := 0; If (epsabs <= 0) And (epsrel < amax1(0.5e+02*macheps, 0.5e-14)) Then Begin ier := 6; freemem(rlist, lsr); freemem(iord, limit*sizeof(ArbInt)); freemem(elist, lsr); freemem(blist, lsr); freemem(alist, lsr); exit End; ierro := 0; qk21(f, a, b, result, abserr, defabs, resabs); dres := abs(result); errbnd := amax1(epsabs, epsrel*dres); last := 1; rlist^[1] := result; elist^[1] := abserr; iord^[1] := 1; If (abserr <= 100*macheps*defabs) And (abserr>errbnd) Then ier := 2; If limit=1 Then ier := 1; If (ier <> 0) Or ((abserr <= errbnd) And (abserr <> resabs)) Or (abserr=0) Then Begin neval := 21; freemem(rlist, lsr); freemem(iord, limit*sizeof(ArbInt)); freemem(elist, lsr); freemem(blist, lsr); freemem(alist, lsr); exit End; rlist2[1] := result; errmax := abserr; maxerr := 1; area := result; errsum := abserr; abserr := giant; nrmax := 1; nres := 0; numrl2 := 2; ktmin := 0; extrap := false; noext := false; iroff1 := 0; iroff2 := 0; iroff3 := 0; ksgn := -1; If dres >= (1-50*macheps)*defabs Then ksgn := 1; go_on := limit > 1; smallers := false; while go_on Do Begin inc(last); a1 := alist^[maxerr]; b1 := (alist^[maxerr]+blist^[maxerr])/2; a2 := b1; b2 := blist^[maxerr]; erlast := errmax; qk21(f, a1, b1, area1, error1, resabs, defab1); qk21(f, a2, b2, area2, error2, resabs, defab2); area12 := area1+area2; erro12 := error1+error2; errsum := errsum+erro12-errmax; area := area+area12-rlist^[maxerr]; If (defab1 <> error1) And (defab2 <> error2) Then Begin If (abs(rlist^[maxerr]-area12) <= 1e-5*abs(area12)) And (erro12 >= 0.99*errmax) Then Begin If extrap Then inc(iroff2) Else inc(iroff1) End; If (last > 10) And (erro12 > errmax) Then inc(iroff3) End; rlist^[maxerr] := area1; rlist^[last] := area2; errbnd := amax1(epsabs, epsrel*abs(area)); If (iroff1+iroff2 >= 10) Or (iroff3>=20) Then ier := 2; If iroff2>=5 Then ierro := 3; If last=limit Then ier := 1; If amax1(abs(a1),abs(b2)) <= (1+100*macheps)*(abs(a2)+1000*midget) Then ier := 4; If error2 <= error1 Then Begin alist^[last] := a2; blist^[maxerr] := b1; blist^[last] := b2; elist^[maxerr] := error1; elist^[last] := error2 End Else Begin alist^[maxerr] := a2; alist^[last] := a1; blist^[last] := b1; rlist^[maxerr] := area2; rlist^[last] := area1; elist^[maxerr] := error2; elist^[last] := error1 End; qpsrt(limit, last, maxerr, errmax, elist^[1], iord^[1], nrmax); If errsum <= errbnd Then Begin smallers := true; go_on := false End Else Begin If ier <> 0 Then go_on := false Else Begin If (last=2) Or (Not noext) Then Begin If last <> 2 Then Begin erlarg := erlarg-erlast; If abs(b1-a1) > small Then erlarg := erlarg+erro12; If extrap Or (abs(blist^[maxerr]-alist^[maxerr]) <= small) Then Begin If Not extrap Then nrmax := 2; extrap := true; jump := false; If (ierro <> 3) And (erlarg>=ertest) Then Begin id := nrmax; jupbnd := last; If last > 2+limit/2 Then jupbnd := limit+3-last; k := id; while (k <= jupbnd) and (Not jump) Do Begin maxerr := iord^[nrmax]; errmax := elist^[maxerr]; If abs(blist^[maxerr]-alist^[maxerr]) > small Then jump := true Else Begin nrmax := nrmax+1; k := k+1 End End; End; {(ierro <> 3) and (erlarg>=ertest)} If Not jump Then Begin numrl2 := numrl2+1; rlist2[numrl2] := area; qelg(numrl2, rlist2, reseps, abseps, res3la, nres); ktmin := ktmin+1; If (ktmin > 5) And (abserr < 1e-3*errsum) Then ier := 5; If abseps < abserr Then Begin ktmin := 0; abserr := abseps; result := reseps; correc := erlarg; ertest := amax1(epsabs,epsrel*abs(reseps)); If abserr <= ertest Then go_on := false End; If go_on Then Begin If numrl2=1 Then noext := true; If ier=5 Then go_on := false Else Begin maxerr := iord^[1]; errmax := elist^[maxerr]; nrmax := 1; extrap := false; small := small/2; erlarg := errsum End; {ier <> 5} End; {go_on} End; {not jump} End; { abs(blist^[maxerr]-alist^[maxerr]) <= small } End Else {last=2} Begin small := abs(b-a)*0.375; erlarg := errsum; ertest := errbnd; rlist2[2] := area End End; {last=2 or not noext} End; {ier <> 0} End; {errsum <= errbnd} If go_on Then go_on := last < limit End; {while go_on} p0 := false; p1 := false; p2 := false; p3 := false; If (abserr=giant) Or smallers Then p0 := true Else If ier+ierro=0 Then p1 := true; If Not (p0 Or p1) Then Begin If ierro=3 Then abserr := abserr+correc; If ier=0 Then ier := 3; If (result <> 0) And (area <> 0) Then p2 := true Else If abserr > errsum Then p0 := true Else If area=0 Then p3 := true Else p1 := true End; If p2 Then Begin If abserr/abs(result) > errsum/abs(area) Then p0 := true Else p1 := true End; If p1 Then Begin If (ksgn=-1) And (amax1(abs(result),abs(area)) <= defabs*0.01) Then p3 := true Else If (0.01 > result/area) Or (result/area > 100) Or (errsum>abs(area)) Then ier := 6; p3 := true End; If p0 Then Begin result := 0; For k:=1 To last Do result := result+rlist^[k] End; If Not p3 Then abserr := errsum; If ier>2 Then ier := ier-1; neval := 42*last-21; freemem(alist, lsr); freemem(blist, lsr); freemem(elist, lsr); freemem(rlist, lsr); freemem(iord, limit*sizeof(ArbInt)); End; { single-precision machine constants r1mach(1) = b**(emin-1), the midget positive magnitude.. r1mach(2) = b**emax*(1 - b**(-t)), the largest magnitude. r1mach(3) = b**(-t), the midget relative spacing. r1mach(4) = b**(1-t), the largest relative spacing. r1mach(5) = log10(b) } Procedure qk15i(f: rfunc1r; boun: ArbFloat; inf: ArbInt; a, b: ArbFloat; Var result, abserr, resabs, resasc: ArbFloat); Const xgk : array[1..8] Of ArbFloat = ( 0.9914553711208126, 0.9491079123427585, 0.8648644233597691, 0.7415311855993944, 0.5860872354676911, 0.4058451513773972, 0.2077849550078985, 0.0000000000000000); wgk : array[1..8] Of ArbFloat = ( 0.02293532201052922,0.06309209262997855, 0.1047900103222502, 0.1406532597155259, 0.1690047266392679, 0.1903505780647854, 0.2044329400752989, 0.2094821410847278); wg : array[1..8] Of ArbFloat = ( 0, 0.1294849661688697, 0, 0.2797053914892767, 0, 0.3818300505051189, 0, 0.4179591836734694); Var absc, absc1, absc2, centr, dinf, fc, fsum, fval1, fval2, hlgth, resg, resk, reskh, tabsc1, tabsc2: ArbFloat; fv1, fv2: array[1..7] Of ArbFloat; j: ArbInt; Begin If inf<1 Then dinf := inf Else dinf := 1; centr := 0.5*(a+b); hlgth := 0.5*(b-a); tabsc1 := boun+dinf*(1-centr)/centr; fval1 := f(tabsc1); If (inf=2) Then fval1 := fval1+f(-tabsc1); fc := (fval1/centr)/centr; resg := wg[8]*fc; resk := wgk[8]*fc; resabs := abs(resk); For j:=1 To 7 Do Begin absc := hlgth*xgk[j]; absc1 := centr-absc; absc2 := centr+absc; tabsc1 := boun+dinf*(1-absc1)/absc1; tabsc2 := boun+dinf*(1-absc2)/absc2; fval1 := f(tabsc1); fval2 := f(tabsc2); If (inf=2) Then fval1 := fval1+f(-tabsc1); If (inf=2) Then fval2 := fval2+f(-tabsc2); fval1 := (fval1/absc1)/absc1; fval2 := (fval2/absc2)/absc2; fv1[j] := fval1; fv2[j] := fval2; fsum := fval1+fval2; resg := resg+wg[j]*fsum; resk := resk+wgk[j]*fsum; resabs := resabs+wgk[j]*(abs(fval1)+abs(fval2)) End; reskh := resk*0.5; resasc := wgk[8]*abs(fc-reskh); For j:=1 To 7 Do resasc := resasc+wgk[j]*(abs(fv1[j]-reskh)+abs(fv2[j]-reskh)); result := resk*hlgth; resasc := resasc*hlgth; resabs := resabs*hlgth; abserr := abs((resk-resg)*hlgth); If (resasc<>0) And (abserr<>0) Then Begin reskh := 200*abserr/resasc; If reskh<1 Then abserr := resasc*reskh*sqrt(reskh) Else abserr := resasc End; If (resabs>midget/(50*macheps)) Then Begin reskh := macheps*50*resabs; If abserrerrbnd Then errbnd := epsabs; ier := 2; If (abserr<=100*macheps*defabs) And (abserr>errbnd) Then exit; ier := 0; If ((abserr<=errbnd) And (abserr<>resabs)) Or (abserr=0) Then exit; GetMem(rlist, limit*SizeOf(ArbFloat)); GetMem(alist, limit*SizeOf(ArbFloat)); GetMem(blist, limit*SizeOf(ArbFloat)); GetMem(elist, limit*SizeOf(ArbFloat)); GetMem(iord, limit*SizeOf(ArbInt)); alist^[1] := 0; blist^[1] := 1; rlist^[1] := result; elist^[1] := abserr; iord^[1] := 1; rlist2[1] := result; errmax := abserr; maxerr := 1; area := result; errsum := abserr; abserr := giant; nrmax := 1; nres := 0; ktmin := 0; numrl2 := 2; extrap := false; noext := false; ierro := 0; iroff1 := 0; iroff2 := 0; iroff3 := 0; If dres>=(1-50*macheps)*defabs Then ksgn := 1 Else ksgn := -1; last := 1; continue := true; while (lasterror1) And (defab2<>error2) Then Begin If (abs(rlist^[maxerr]-area12)<=1e-5*abs(area12)) And (erro12>=0.99*errmax) Then If extrap Then Inc(iroff2) Else Inc(iroff1); If (last>10) And (erro12>errmax) Then Inc(iroff3) End; rlist^[maxerr] := area1; rlist^[last] := area2; errbnd := epsrel*abs(area); If errbnd=10) Or (iroff3>=20) Then ier := 2; If (iroff2>=5) Then ierro := 3; If (last=limit) Then ier := 1; h := abs(a1); If hsmall) Then erlarg := erlarg+erro12; break := false; If Not extrap Then If (abs(blist^[maxerr]-alist^[maxerr])>small) Then break := true Else Begin extrap := true; nrmax := 2 End; If Not break And (ierro<>3) And (erlarg>ertest) Then Begin id := nrmax; jupbnd := last; If (last>(2+limit Div 2)) Then jupbnd := limit+3-last; k := id-1; while (ksmall) Then break := true Else Inc(nrmax) End End; If Not break Then Begin Inc(numrl2); rlist2[numrl2] := area; qelg(numrl2, rlist2, reseps, abseps, res3la, nres); Inc(ktmin); If (ktmin>5) And (abserr<1e-3*errsum) Then ier := 4; If (absepsertest Then ertest := epsabs; If (abserr<=ertest) Then continue := false End; End; If continue And Not break Then Begin If (numrl2=1) Then noext := true; If ier<>4 Then Begin maxerr := iord^[1]; errmax := elist^[maxerr]; nrmax := 1; extrap := false; small := small*0.5; erlarg := errsum End End End End; h := 0; For k := 1 To last Do h := h+rlist^[k]; FreeMem(rlist, limit*SizeOf(ArbFloat)); FreeMem(alist, limit*SizeOf(ArbFloat)); FreeMem(blist, limit*SizeOf(ArbFloat)); FreeMem(elist, limit*SizeOf(ArbFloat)); FreeMem(iord, limit*SizeOf(ArbInt)); If (errsum<=errbnd) Or (abserr=giant) Then Begin result := h; abserr := errsum; exit End; If (ier+ierro)=0 Then Begin h := abs(result); If h-1) Or (h>defabs*0.01) Then If (0.01>result/area) Or (result/area>100) Or (errsum>abs(area)) Then ier := 5; exit End; If ierro=3 Then abserr := abserr+correc; If ier=0 Then ier := 2; If (result<>0) And (area<>0) Then If abserr/abs(result)>errsum/abs(area) Then Begin result := h; abserr := errsum; exit End Else Begin h := abs(result); If h-1) Or (h>defabs*0.01) Then If (0.01>result/area) Or (result/area>100) Or (errsum>abs(area)) Then ier := 5; exit End; If abserr>errsum Then Begin result := h; abserr := errsum; exit End; If area<>0 Then Begin h := abs(result); If h-1) Or (h>defabs*0.01) Then If (0.01>result/area) Or (result/area>100) Or (errsum>abs(area)) Then ier := 5 End End; Procedure int1fr(f: rfunc1r; a, b, ae: ArbFloat; Var integral, err: ArbFloat; Var term: ArbInt); Var neval, ier, last: ArbInt; Begin term := 3; integral := NaN; If abs(a)=infinity Then If abs(b)=infinity Then If (a=b) Then exit Else Begin qagie(f, 0, 2, ae, epsrel, integral, err, ier); If a=infinity Then integral := -integral End Else If a=-infinity Then qagie(f, b, -1, ae, epsrel, integral, err, ier) Else Begin qagie(f, b, 1, ae, epsrel, integral, err, ier); integral := -integral End Else If abs(b)=infinity Then If b=-infinity Then Begin qagie(f, a, -1, ae, epsrel, integral, err, ier); integral := -integral End Else qagie(f, a, 1, ae, epsrel, integral, err, ier) Else qagse(f, a, b, ae, epsrel, limit, integral, err, neval, ier, last); term := 4; If ier=6 Then term := 3; If ier=0 Then term := 1; If (ier=2) Or (ier=4) Then term := 2 End; Begin limit := 500; epsrel := 0; End.