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.qawo;
9 
10 
11 import std.conv;
12 import scid.ports.quadpack.qawoe;
13 
14 version(unittest)
15 {
16     import std.math;
17     import scid.core.testing;
18 }
19 
20 
21 
22 
23 ///
24 void qawo(Real, Func)(Func f, Real a, Real b, Real omega, int integr,
25     Real epsabs, Real epsrel, out Real result, out Real abserr,
26     out int neval, out int ier, int leniw, int maxp1, int lenw,
27     out int last, int* iwork, Real* work)
28 {
29 //***begin prologue  qawo
30 //***date written   800101   (yymmdd)
31 //***revision date  830518   (yymmdd)
32 //***category no.  h2a2a1
33 //***keywords  automatic integrator, special-purpose,
34 //             integrand with oscillatory cos or sin factor,
35 //             clenshaw-curtis method, (end point) singularities,
36 //             extrapolation, globally adaptive
37 //***author  piessens,robert,appl. math. & progr. div. - k.u.leuven
38 //           de doncker,elise,appl. math. & progr. div. - k.u.leuven
39 //***purpose  the routine calculates an approximation result to a given
40 //            definite integral
41 //            i = integral of f(x)*w(x) over (a,b)
42 //            where w(x) = cos(omega*x) or w(x) = sin(omega*x),
43 //            hopefully satisfying following claim for accuracy
44 //            abs(i-result).le.max(epsabs,epsrel*abs(i)).
45 //***description
46 //
47 //        computation of oscillatory integrals
48 //        standard fortran subroutine
49 //        real version
50 //
51 //        parameters
52 //         on entry
53 //            f      - real
54 //                     function subprogram defining the function
55 //                     f(x).  the actual name for f needs to be
56 //                     declared e x t e r n a l in the driver program.
57 //
58 //            a      - real
59 //                     lower limit of integration
60 //
61 //            b      - real
62 //                     upper limit of integration
63 //
64 //            omega  - real
65 //                     parameter in the integrand weight function
66 //
67 //            integr - integer
68 //                     indicates which of the weight functions is used
69 //                     integr = 1      w(x) = cos(omega*x)
70 //                     integr = 2      w(x) = sin(omega*x)
71 //                     if integr.ne.1.and.integr.ne.2, the routine will
72 //                     end with ier = 6.
73 //
74 //            epsabs - real
75 //                     absolute accuracy requested
76 //            epsrel - real
77 //                     relative accuracy requested
78 //                     if epsabs.le.0 and
79 //                     epsrel.lt.max(50*rel.mach.acc.,0.5d-28),
80 //                     the routine will end with ier = 6.
81 //
82 //         on return
83 //            result - real
84 //                     approximation to the integral
85 //
86 //            abserr - real
87 //                     estimate of the modulus of the absolute error,
88 //                     which should equal or exceed abs(i-result)
89 //
90 //            neval  - integer
91 //                     number of  integrand evaluations
92 //
93 //            ier    - integer
94 //                     ier = 0 normal and reliable termination of the
95 //                             routine. it is assumed that the requested
96 //                             accuracy has been achieved.
97 //                   - ier.gt.0 abnormal termination of the routine.
98 //                             the estimates for integral and error are
99 //                             less reliable. it is assumed that the
100 //                             requested accuracy has not been achieved.
101 //            error messages
102 //                     ier = 1 maximum number of subdivisions allowed
103 //                             (= leniw/2) has been achieved. one can
104 //                             allow more subdivisions by increasing the
105 //                             value of leniw (and taking the according
106 //                             dimension adjustments into account).
107 //                             however, if this yields no improvement it
108 //                             is advised to analyze the integrand in
109 //                             order to determine the integration
110 //                             difficulties. if the position of a local
111 //                             difficulty can be determined (e.g.
112 //                             singularity, discontinuity within the
113 //                             interval) one will probably gain from
114 //                             splitting up the interval at this point
115 //                             and calling the integrator on the
116 //                             subranges. if possible, an appropriate
117 //                             special-purpose integrator should be used
118 //                             which is designed for handling the type of
119 //                             difficulty involved.
120 //                         = 2 the occurrence of roundoff error is
121 //                             detected, which prevents the requested
122 //                             tolerance from being achieved.
123 //                             the error may be under-estimated.
124 //                         = 3 extremely bad integrand behaviour occurs
125 //                             at some interior points of the
126 //                             integration interval.
127 //                         = 4 the algorithm does not converge.
128 //                             roundoff error is detected in the
129 //                             extrapolation table. it is presumed that
130 //                             the requested tolerance cannot be achieved
131 //                             due to roundoff in the extrapolation
132 //                             table, and that the returned result is
133 //                             the best which can be obtained.
134 //                         = 5 the integral is probably divergent, or
135 //                             slowly convergent. it must be noted that
136 //                             divergence can occur with any other value
137 //                             of ier.
138 //                         = 6 the input is invalid, because
139 //                             (epsabs.le.0 and
140 //                              epsrel.lt.max(50*rel.mach.acc.,0.5d-28))
141 //                             or (integr.ne.1 and integr.ne.2),
142 //                             or leniw.lt.2 or maxp1.lt.1 or
143 //                             lenw.lt.leniw*2+maxp1*25.
144 //                             result, abserr, neval, last are set to
145 //                             zero. except when leniw, maxp1 or lenw are
146 //                             invalid, work(limit*2+1), work(limit*3+1),
147 //                             iwork(1), iwork(limit+1) are set to zero,
148 //                             work(1) is set to a and work(limit+1) to
149 //                             b.
150 //
151 //         dimensioning parameters
152 //            leniw  - integer
153 //                     dimensioning parameter for iwork.
154 //                     leniw/2 equals the maximum number of subintervals
155 //                     allowed in the partition of the given integration
156 //                     interval (a,b), leniw.ge.2.
157 //                     if leniw.lt.2, the routine will end with ier = 6.
158 //
159 //            maxp1  - integer
160 //                     gives an upper bound on the number of chebyshev
161 //                     moments which can be stored, i.e. for the
162 //                     intervals of lengths abs(b-a)*2**(-l),
163 //                     l=0,1, ..., maxp1-2, maxp1.ge.1
164 //                     if maxp1.lt.1, the routine will end with ier = 6.
165 //
166 //            lenw   - integer
167 //                     dimensioning parameter for work
168 //                     lenw must be at least leniw*2+maxp1*25.
169 //                     if lenw.lt.(leniw*2+maxp1*25), the routine will
170 //                     end with ier = 6.
171 //
172 //            last   - integer
173 //                     on return, last equals the number of subintervals
174 //                     produced in the subdivision process, which
175 //                     determines the number of significant elements
176 //                     actually in the work arrays.
177 //
178 //         work arrays
179 //            iwork  - integer
180 //                     vector of dimension at least leniw
181 //                     on return, the first k elements of which contain
182 //                     pointers to the error estimates over the
183 //                     subintervals, such that work(limit*3+iwork(1)), ..
184 //                     work(limit*3+iwork(k)) form a decreasing
185 //                     sequence, with limit = lenw/2 , and k = last
186 //                     if last.le.(limit/2+2), and k = limit+1-last
187 //                     otherwise.
188 //                     furthermore, iwork(limit+1), ..., iwork(limit+
189 //                     last) indicate the subdivision levels of the
190 //                     subintervals, such that iwork(limit+i) = l means
191 //                     that the subinterval numbered i is of length
192 //                     abs(b-a)*2**(1-l).
193 //
194 //            work   - real
195 //                     vector of dimension at least lenw
196 //                     on return
197 //                     work(1), ..., work(last) contain the left
198 //                      end points of the subintervals in the
199 //                      partition of (a,b),
200 //                     work(limit+1), ..., work(limit+last) contain
201 //                      the right end points,
202 //                     work(limit*2+1), ..., work(limit*2+last) contain
203 //                      the integral approximations over the
204 //                      subintervals,
205 //                     work(limit*3+1), ..., work(limit*3+last)
206 //                      contain the error estimates.
207 //                     work(limit*4+1), ..., work(limit*4+maxp1*25)
208 //                      provide space for storing the chebyshev moments.
209 //                     note that limit = lenw/2.
210 //
211 //***references  (none)
212 //***routines called  qawoe,xerror
213 //***end prologue  qawo
214 //
215        int lvl=1,l1=1,l2=1,l3=1,l4=1,limit=1,momcom=1;
216 //
217        //dimension iwork(leniw),work(lenw)
218 //
219 //         check validity of leniw, maxp1 and lenw.
220 //
221 //***first executable statement  qawo
222       ier = 6;
223       neval = 0;
224       last = 0;
225       result = 0.0;
226       abserr = 0.0;
227       if(leniw < 2 || maxp1 < 1 || lenw < (leniw*2+maxp1*25))
228         goto l10;
229 //
230 //         prepare call for qawoe
231 //
232       limit = leniw/2;
233       l1 = limit;
234       l2 = limit+l1;
235       l3 = limit+l2;
236       l4 = limit+l3;
237       qawoe!(Real,Func)(f,a,b,omega,integr,epsabs,epsrel,limit,1,maxp1,result,
238         abserr,neval,ier,last,work,work+l1,work+l2,work+l3,
239         iwork,iwork+l1,momcom,work+l4);
240 //
241 //         call error handler if necessary
242 //
243       lvl = 0;
244 l10:  if(ier == 6) lvl = 1;
245       if(ier != 0)
246         throw new Exception("abnormal return from  qawo: "~to!string(ier));
247       return;
248 }
249 
250 
251 unittest
252 {
253     alias qawo!(float, float delegate(float)) fqawo;
254     alias qawo!(double, double delegate(double)) dqawo;
255     alias qawo!(double, double function(double)) dfqawo;
256     alias qawo!(real, real delegate(real)) rqawo;
257 }
258 
259 
260 unittest
261 {
262     // From the QUADPACK book
263     double dlog(double x) { return x > 0.0 ? log(x) : 0.0; }
264 
265     double a = 0.0, b = 1.0, omega = 10*PI;
266     int integr = 2;
267     double epsabs = 0.0, epsrel = 1e-8;
268     double result, abserr;
269     int neval, ier, last;
270     const int leniw = 1000, maxp1=21, lenw=leniw*2+maxp1*25;
271 
272     int[leniw] iwork;
273     double[lenw] work;
274 
275     qawo(&dlog, a, b, omega, integr, epsabs, epsrel, result, abserr,
276         neval, ier, leniw, maxp1, lenw, last, iwork.ptr, work.ptr);
277 
278     double ans = -0.128136848399167;
279     assert (isAccurate!double(result, abserr, ans, epsrel, epsabs));
280 }
281 
282 
283 unittest
284 {
285     // This is integral 14 in the QUADPACK book.
286     real alpha = 3.0;
287     real f(real x) { return x <= 0.0 ? 0.0 : exp(-(2.0L^^(-alpha))*x)/sqrt(x); }
288     
289     real a = 0.0;
290     real b = 20*(2.0L^^alpha);
291     real omega = 1.0;
292     int integr = 1; // cos(x)
293     real epsabs = 0.0;
294     real epsrel = 1e-8;
295 
296     real result, abserr;
297     int neval, ier, last;
298 
299     enum
300     {
301         leniw = 1000,
302         maxp1 = 21,
303         lenw = leniw*2 + maxp1*25
304     }
305 
306     int[leniw] iwork;
307     real[lenw] work;
308 
309     qawo(&f, a, b, omega, integr, epsabs, epsrel, result, abserr,
310         neval, ier, leniw, maxp1, lenw, last, iwork.ptr, work.ptr);
311     
312     real eps = 1e-20;
313     real ans = (1 + eps)*sqrt(PI) * ((1+(4.0L^^(-alpha)))^^(-0.25L))
314         * cos(atan(2.0L^^alpha)/2);
315     assert (isAccurate(result, abserr, ans, epsrel, epsabs));
316 }
317