1 // This code has been mechanically translated from the original FORTRAN
2 // code at http://netlib.org/quadpack.
3 
4 /** Authors:    Lars Tandle Kyllingstad
5     Copyright:  Copyright (c) 2009, Lars T. Kyllingstad. All rights reserved.
6     License:    Boost License 1.0
7 */
8 module scid.ports.quadpack.qagp;
9 
10 
11 import std.conv;
12 
13 import scid.core.fortran;
14 import scid.core.testing;
15 import scid.ports.quadpack.qagpe;
16 
17 version(unittest)
18 {
19     import std.math;
20     import scid.core.testing;
21 }
22 
23 
24 
25 
26 ///
27 void qagp(Real, Func)(Func f, Real a, Real b, int npts2, const Real* points,
28     Real epsabs, Real epsrel, out Real result, out Real abserr,
29     out int neval, out int ier, int leniw, int lenw, out int last,
30     int* iwork, Real* work)
31 {
32 //***begin prologue  dqagp
33 //***date written   800101   (yymmdd)
34 //***revision date  830518   (yymmdd)
35 //***category no.  h2a2a1
36 //***keywords  automatic integrator, general-purpose,
37 //             singularities at user specified points,
38 //             extrapolation, globally adaptive
39 //***author  piessens,robert,appl. math. & progr. div - k.u.leuven
40 //           de doncker,elise,appl. math. & progr. div. - k.u.leuven
41 //***purpose  the routine calculates an approximation result to a given
42 //            definite integral i = integral of f over (a,b),
43 //            hopefully satisfying following claim for accuracy
44 //            break points of the integration interval, where local
45 //            difficulties of the integrand may occur (e.g.
46 //            singularities, discontinuities), are provided by the user.
47 //***description
48 //
49 //        computation of a definite integral
50 //        standard fortran subroutine
51 //        double precision version
52 //
53 //        parameters
54 //         on entry
55 //            f      - double precision
56 //                     function subprogram defining the integrand
57 //                     function f(x). the actual name for f needs to be
58 //                     declared e x t e r n a l in the driver program.
59 //
60 //            a      - double precision
61 //                     lower limit of integration
62 //
63 //            b      - double precision
64 //                     upper limit of integration
65 //
66 //            npts2  - integer
67 //                     number equal to two more than the number of
68 //                     user-supplied break points within the integration
69 //                     range, npts.ge.2.
70 //                     if npts2.lt.2, the routine will end with ier = 6.
71 //
72 //            points - double precision
73 //                     vector of dimension npts2, the first (npts2-2)
74 //                     elements of which are the user provided break
75 //                     points. if these points do not constitute an
76 //                     ascending sequence there will be an automatic
77 //                     sorting.
78 //
79 //            epsabs - double precision
80 //                     absolute accuracy requested
81 //            epsrel - double precision
82 //                     relative accuracy requested
83 //                     if  epsabs.le.0
84 //                     and epsrel.lt.max(50*rel.mach.acc.,0.5d-28),
85 //                     the routine will end with ier = 6.
86 //
87 //         on return
88 //            result - double precision
89 //                     approximation to the integral
90 //
91 //            abserr - double precision
92 //                     estimate of the modulus of the absolute error,
93 //                     which should equal or exceed abs(i-result)
94 //
95 //            neval  - integer
96 //                     number of integrand evaluations
97 //
98 //            ier    - integer
99 //                     ier = 0 normal and reliable termination of the
100 //                             routine. it is assumed that the requested
101 //                             accuracy has been achieved.
102 //                     ier.gt.0 abnormal termination of the routine.
103 //                             the estimates for integral and error are
104 //                             less reliable. it is assumed that the
105 //                             requested accuracy has not been achieved.
106 //            error messages
107 //                     ier = 1 maximum number of subdivisions allowed
108 //                             has been achieved. one can allow more
109 //                             subdivisions by increasing the value of
110 //                             limit (and taking the according dimension
111 //                             adjustments into account). however, if
112 //                             this yields no improvement it is advised
113 //                             to analyze the integrand in order to
114 //                             determine the integration difficulties. if
115 //                             the position of a local difficulty can be
116 //                             determined (i.e. singularity,
117 //                             discontinuity within the interval), it
118 //                             should be supplied to the routine as an
119 //                             element of the vector points. if necessary
120 //                             an appropriate special-purpose integrator
121 //                             must be used, which is designed for
122 //                             handling the type of difficulty involved.
123 //                         = 2 the occurrence of roundoff error is
124 //                             detected, which prevents the requested
125 //                             tolerance from being achieved.
126 //                             the error may be under-estimated.
127 //                         = 3 extremely bad integrand behaviour occurs
128 //                             at some points of the integration
129 //                             interval.
130 //                         = 4 the algorithm does not converge.
131 //                             roundoff error is detected in the
132 //                             extrapolation table.
133 //                             it is presumed that the requested
134 //                             tolerance cannot be achieved, and that
135 //                             the returned result is the best which
136 //                             can be obtained.
137 //                         = 5 the integral is probably divergent, or
138 //                             slowly convergent. it must be noted that
139 //                             divergence can occur with any other value
140 //                             of ier.gt.0.
141 //                         = 6 the input is invalid because
142 //                             npts2.lt.2 or
143 //                             break points are specified outside
144 //                             the integration range or
145 //                             (epsabs.le.0 and
146 //                              epsrel.lt.max(50*rel.mach.acc.,0.5d-28))
147 //                             result, abserr, neval, last are set to
148 //                             zero. exept when leniw or lenw or npts2 is
149 //                             invalid, iwork(1), iwork(limit+1),
150 //                             work(limit*2+1) and work(limit*3+1)
151 //                             are set to zero.
152 //                             work(1) is set to a and work(limit+1)
153 //                             to b (where limit = (leniw-npts2)/2).
154 //
155 //         dimensioning parameters
156 //            leniw - integer
157 //                    dimensioning parameter for iwork
158 //                    leniw determines limit = (leniw-npts2)/2,
159 //                    which is the maximum number of subintervals in the
160 //                    partition of the given integration interval (a,b),
161 //                    leniw.ge.(3*npts2-2).
162 //                    if leniw.lt.(3*npts2-2), the routine will end with
163 //                    ier = 6.
164 //
165 //            lenw  - integer
166 //                    dimensioning parameter for work
167 //                    lenw must be at least leniw*2-npts2.
168 //                    if lenw.lt.leniw*2-npts2, the routine will end
169 //                    with ier = 6.
170 //
171 //            last  - integer
172 //                    on return, last equals the number of subintervals
173 //                    produced in the subdivision process, which
174 //                    determines the number of significant elements
175 //                    actually in the work arrays.
176 //
177 //         work arrays
178 //            iwork - integer
179 //                    vector of dimension at least leniw. on return,
180 //                    the first k elements of which contain
181 //                    pointers to the error estimates over the
182 //                    subintervals, such that work(limit*3+iwork(1)),...,
183 //                    work(limit*3+iwork(k)) form a decreasing
184 //                    sequence, with k = last if last.le.(limit/2+2), and
185 //                    k = limit+1-last otherwise
186 //                    iwork(limit+1), ...,iwork(limit+last) contain the
187 //                     subdivision levels of the subintervals, i.e.
188 //                     if (aa,bb) is a subinterval of (p1,p2)
189 //                     where p1 as well as p2 is a user-provided
190 //                     break point or integration limit, then (aa,bb) has
191 //                     level l if abs(bb-aa) = abs(p2-p1)*2**(-l),
192 //                    iwork(limit*2+1), ..., iwork(limit*2+npts2) have
193 //                     no significance for the user,
194 //                    note that limit = (leniw-npts2)/2.
195 //
196 //            work  - double precision
197 //                    vector of dimension at least lenw
198 //                    on return
199 //                    work(1), ..., work(last) contain the left
200 //                     end points of the subintervals in the
201 //                     partition of (a,b),
202 //                    work(limit+1), ..., work(limit+last) contain
203 //                     the right end points,
204 //                    work(limit*2+1), ..., work(limit*2+last) contain
205 //                     the integral approximations over the subintervals,
206 //                    work(limit*3+1), ..., work(limit*3+last)
207 //                     contain the corresponding error estimates,
208 //                    work(limit*4+1), ..., work(limit*4+npts2)
209 //                     contain the integration limits and the
210 //                     break points sorted in an ascending sequence.
211 //                    note that limit = (leniw-npts2)/2.
212 //
213 //***references  (none)
214 //***routines called  dqagpe,xerror
215 //***end prologue  dqagp
216 //
217       int limit,l1,l2,l3,l4;
218 //
219 //         check validity of limit and lenw.
220 //
221 //***first executable statement  dqagp
222       ier = 6;
223       neval = 0;
224       last = 0;
225       result = 0.0;
226       abserr = 0.0;
227       if(leniw < (3*npts2-2) || lenw < (leniw*2-npts2) || npts2 < 2)
228         goto l10;
229 //
230 //         prepare call for dqagpe.
231 //
232       limit = (leniw-npts2)/2;
233       l1 = limit;
234       l2 = limit+l1;
235       l3 = limit+l2;
236       l4 = limit+l3;
237 //
238       qagpe!(Real, Func)(f,a,b,npts2,points,epsabs,epsrel,limit,result,abserr,
239         neval,ier,work,work+l1,work+l2,work+l3,work+l4,
240         iwork,iwork+l1,iwork+l2,last);
241 //
242 //         call error handler if necessary.
243 //
244 l10:  if(ier != 0)
245         throw new Exception("abnormal return from qagp: "~to!string(ier));
246       return;
247 }
248 
249 unittest
250 {
251     alias qagp!(float, float delegate(float)) fqagp;
252     alias qagp!(double, double delegate(double)) dqagp;
253     alias qagp!(double, double function(double)) dfqagp;
254     alias qagp!(real, real delegate(real)) rqagp;
255 }
256 
257 unittest
258 {
259 
260     double f(double x)
261     {
262         return (x^^3)*log(fabs(((x^^2)-1) * ((x^^2)-2)));
263     }
264 
265     enum : double
266     {
267         a = 0.0,
268         b = 3.0,
269         epsabs = 0.0,
270         epsrel = 1e-10,
271     }
272     double[] points = [1.0, sqrt(2.0)]; // Singularities
273     double result, abserr;
274     int neval, ier, last;
275     enum
276     {
277         leniw = 1000,
278         lenw = leniw*2,
279     }
280     int[leniw] iwork;
281     double[lenw] work;
282 
283     qagp(&f, a, b, toInt(points.length), points.ptr, epsabs, epsrel,
284         result, abserr, neval, ier, leniw, lenw, last, iwork.ptr, work.ptr);
285 
286     double ans = 61*log(2.0) + 77*log(7.0)/4 - 27;
287     assert (isAccurate(result, abserr, ans, epsrel, epsabs));
288 }