1 /** Special functions.
2 
3     The functions in this module are ported (with permission) to D from
4     $(LINK2 http://www.moshier.net/,Stephen L. Moshier)'s
5     $(LINK2 http://www.netlib.org/cephes/,Cephes) library.
6 
7     Authors:    Lars Tandle Kyllingstad
8     Copyright:  Copyright (c) 2010–2011, Lars T. Kyllingstad. All rights reserved.
9     License:    Boost License 1.0
10 */
11 module scid.functions;
12 
13 import std.math;
14 
15 
16 
17 version (LittleEndian)
18 {
19     static if (real.sizeof == 12)
20     {
21         version = LEReal80;
22         private enum short PAD = 0;
23     }
24 }
25 
26 
27 
28 
29 /*  Evaluate Chebyshev series.
30 
31     Coefficients are stored in reverse order, i.e. the
32     zeroth-order term is the last in the array.
33 */
34 private real chebEval(real x, const real[] coefficients) @safe pure nothrow
35 {
36     real
37         b0 = coefficients[0],
38         b1 = 0,
39         b2;
40 
41     foreach (c; coefficients[1 .. $])
42     {
43         b2 = b1;
44         b1 = b0;
45         b0 = x * b1 - b2 + c;
46     }
47 
48     return 0.5 * (b0 - b2);
49 }
50 
51 
52 
53 
54 /** Modified Bessel function of the first kind, order 0. */
55 real besselI0(real x) pure
56 {
57     // Chebyshev coefficients for exp(-x) I0(x)
58     // in the interval [0,8].
59     //
60     // lim(x->0){ exp(-x) I0(x) } = 1.
61     immutable real[30] chebCoeffsA = [
62        -4.41534164647933937950e-18L,
63         3.33079451882223809783e-17L,
64        -2.43127984654795469359e-16L,
65         1.71539128555513303061e-15L,
66        -1.16853328779934516808e-14L,
67         7.67618549860493561688e-14L,
68        -4.85644678311192946090e-13L,
69         2.95505266312963983461e-12L,
70        -1.72682629144155570723e-11L,
71         9.67580903537323691224e-11L,
72        -5.18979560163526290666e-10L,
73         2.65982372468238665035e-9L,
74        -1.30002500998624804212e-8L,
75         6.04699502254191894932e-8L,
76        -2.67079385394061173391e-7L,
77         1.11738753912010371815e-6L,
78        -4.41673835845875056359e-6L,
79         1.64484480707288970893e-5L,
80        -5.75419501008210370398e-5L,
81         1.88502885095841655729e-4L,
82        -5.76375574538582365885e-4L,
83         1.63947561694133579842e-3L,
84        -4.32430999505057594430e-3L,
85         1.05464603945949983183e-2L,
86        -2.37374148058994688156e-2L,
87         4.93052842396707084878e-2L,
88        -9.49010970480476444210e-2L,
89         1.71620901522208775349e-1L,
90        -3.04682672343198398683e-1L,
91         6.76795274409476084995e-1L  ];
92 
93     // Chebyshev coefficients for exp(-x) sqrt(x) I0(x)
94     // in the inverted interval [8,infinity].
95     //
96     // lim(x->inf){ exp(-x) sqrt(x) I0(x) } = 1/sqrt(2pi).
97     immutable real[25] chebCoeffsB = [
98        -7.23318048787475395456e-18L,
99        -4.83050448594418207126e-18L,
100         4.46562142029675999901e-17L,
101         3.46122286769746109310e-17L,
102        -2.82762398051658348494e-16L,
103        -3.42548561967721913462e-16L,
104         1.77256013305652638360e-15L,
105         3.81168066935262242075e-15L,
106        -9.55484669882830764870e-15L,
107        -4.15056934728722208663e-14L,
108         1.54008621752140982691e-14L,
109         3.85277838274214270114e-13L,
110         7.18012445138366623367e-13L,
111        -1.79417853150680611778e-12L,
112        -1.32158118404477131188e-11L,
113        -3.14991652796324136454e-11L,
114         1.18891471078464383424e-11L,
115         4.94060238822496958910e-10L,
116         3.39623202570838634515e-9L,
117         2.26666899049817806459e-8L,
118         2.04891858946906374183e-7L,
119         2.89137052083475648297e-6L,
120         6.88975834691682398426e-5L,
121         3.36911647825569408990e-3L,
122         8.04490411014108831608e-1L  ];
123 
124     auto y = fabs(x);
125 
126     if (y < 8)
127         return exp(y) * chebEval(y/2 - 2, chebCoeffsA);
128     else
129         return exp(y) * chebEval(32/y - 2, chebCoeffsB) / sqrt(y);
130 }
131 
132 
133 unittest
134 {
135     assert (approxEqual(besselI0( 1), 1.2660658777520083356e0, 8.2e-17));
136     assert (approxEqual(besselI0(10), 2.8157166284662544715e3, 8.2e-17));
137     assert (besselI0(- 1) == besselI0( 1));
138     assert (besselI0(-10) == besselI0(10));
139 }
140 
141 
142 
143 
144 /** Modified Bessel function of the first kind, order 1. */
145 real besselI1(real x) pure
146 {
147     /* Chebyshev coefficients for exp(-x) I1(x) / x
148      * in the interval [0,8].
149      *
150      * lim(x->0){ exp(-x) I1(x) / x } = 1/2.
151      */
152     immutable real[29] chebCoeffsA = [
153         2.77791411276104639959e-18L,
154        -2.11142121435816608115e-17L,
155         1.55363195773620046921e-16L,
156        -1.10559694773538630805e-15L,
157         7.60068429473540693410e-15L,
158        -5.04218550472791168711e-14L,
159         3.22379336594557470981e-13L,
160        -1.98397439776494371520e-12L,
161         1.17361862988909016308e-11L,
162        -6.66348972350202774223e-11L,
163         3.62559028155211703701e-10L,
164        -1.88724975172282928790e-9L,
165         9.38153738649577178388e-9L,
166        -4.44505912879632808065e-8L,
167         2.00329475355213526229e-7L,
168        -8.56872026469545474066e-7L,
169         3.47025130813767847674e-6L,
170        -1.32731636560394358279e-5L,
171         4.78156510755005422638e-5L,
172        -1.61760815825896745588e-4L,
173         5.12285956168575772895e-4L,
174        -1.51357245063125314899e-3L,
175         4.15642294431288815669e-3L,
176        -1.05640848946261981558e-2L,
177         2.47264490306265168283e-2L,
178        -5.29459812080949914269e-2L,
179         1.02643658689847095384e-1L,
180        -1.76416518357834055153e-1L,
181         2.52587186443633654823e-1L  ];
182 
183     /* Chebyshev coefficients for exp(-x) sqrt(x) I1(x)
184      * in the inverted interval [8,infinity].
185      *
186      * lim(x->inf){ exp(-x) sqrt(x) I1(x) } = 1/sqrt(2pi).
187      */
188     immutable real[25] chebCoeffsB = [
189         7.51729631084210481353e-18L,
190         4.41434832307170791151e-18L,
191        -4.65030536848935832153e-17L,
192        -3.20952592199342395980e-17L,
193         2.96262899764595013876e-16L,
194         3.30820231092092828324e-16L,
195        -1.88035477551078244854e-15L,
196        -3.81440307243700780478e-15L,
197         1.04202769841288027642e-14L,
198         4.27244001671195135429e-14L,
199        -2.10154184277266431302e-14L,
200        -4.08355111109219731823e-13L,
201        -7.19855177624590851209e-13L,
202         2.03562854414708950722e-12L,
203         1.41258074366137813316e-11L,
204         3.25260358301548823856e-11L,
205        -1.89749581235054123450e-11L,
206        -5.58974346219658380687e-10L,
207        -3.83538038596423702205e-9L,
208        -2.63146884688951950684e-8L,
209        -2.51223623787020892529e-7L,
210        -3.88256480887769039346e-6L,
211        -1.10588938762623716291e-4L,
212        -9.76109749136146840777e-3L,
213         7.78576235018280120474e-1L  ];
214 
215     auto z = fabs(x);
216     if (z <= 8)
217     {
218         immutable y = z/2 - 2;
219         z = chebEval(y, chebCoeffsA[]) * z * exp(z);
220     }
221     else
222     {
223         z = exp(z) * chebEval(32/z - 2, chebCoeffsB[]) / sqrt(z);
224     }
225     if (x < 0) z = -z;
226 
227     return z;
228 }
229 
230 
231 unittest
232 {
233     assert (approxEqual(besselI1( 1), 5.6515910399248502721e-1, 1.2e-16));
234     assert (approxEqual(besselI1(10), 2.6709883037012546543e3,  1.2e-16));
235     assert (besselI1(- 1) == -besselI1( 1));
236     assert (besselI1(-10) == -besselI1(10));
237 }
238 
239 
240 
241 
242 /** Modified Bessel function of the second kind, order 0. */
243 real besselK0(real x) pure
244 in { assert(x > 0, "Can only calculate K0 for positive argument"); }
245 body {
246     /* Chebyshev coefficients for K0(x) + log(x/2) I0(x)
247      * in the interval [0,2].  The odd order coefficients are all
248      * zero; only the even order coefficients are listed.
249      * 
250      * lim(x->0){ K0(x) + log(x/2) I0(x) } = -EUL.
251      */
252     immutable real[10] chebCoeffsA = [
253         1.37446543561352307156e-16L,
254         4.25981614279661018399e-14L,
255         1.03496952576338420167e-11L,
256         1.90451637722020886025e-9L,
257         2.53479107902614945675e-7L,
258         2.28621210311945178607e-5L,
259         1.26461541144692592338e-3L,
260         3.59799365153615016266e-2L,
261         3.44289899924628486886e-1L,
262        -5.35327393233902768720e-1L  ];
263 
264     /* Chebyshev coefficients for exp(x) sqrt(x) K0(x)
265      * in the inverted interval [2,infinity].
266      * 
267      * lim(x->inf){ exp(x) sqrt(x) K0(x) } = sqrt(pi/2).
268      */
269     immutable real[25] chebCoeffsB = [
270         5.30043377268626276149e-18L,
271        -1.64758043015242134646e-17L,
272         5.21039150503902756861e-17L,
273        -1.67823109680541210385e-16L,
274         5.51205597852431940784e-16L,
275        -1.84859337734377901440e-15L,
276         6.34007647740507060557e-15L,
277        -2.22751332699166985548e-14L,
278         8.03289077536357521100e-14L,
279        -2.98009692317273043925e-13L,
280         1.14034058820847496303e-12L,
281        -4.51459788337394416547e-12L,
282         1.85594911495471785253e-11L,
283        -7.95748924447710747776e-11L,
284         3.57739728140030116597e-10L,
285        -1.69753450938905987466e-9L,
286         8.57403401741422608519e-9L,
287        -4.66048989768794782956e-8L,
288         2.76681363944501510342e-7L,
289        -1.83175552271911948767e-6L,
290         1.39498137188764993662e-5L,
291        -1.28495495816278026384e-4L,
292         1.56988388573005337491e-3L,
293        -3.14481013119645005427e-2L,
294         2.44030308206595545468e0L   ];
295 
296     if (x <= 2)
297         return chebEval(x^^2 - 2, chebCoeffsA) - log(x/2) * besselI0(x);
298     else
299         return exp(-x) * chebEval(8/x - 2, chebCoeffsB) / sqrt(x);
300 }
301 
302 
303 unittest
304 {
305     assert (approxEqual(besselK0( 1), 4.2102443824070833334e-1, 1.3e-16));
306     assert (approxEqual(besselK0(10), 1.7780062316167651811e-5, 1.3e-16));
307 }
308 
309 
310 
311 
312 /** Modified Bessel function of the second kind, order 1. */
313 real besselK1(real x) pure
314 in { assert(x > 0, "Can only calculate K1 for positive argument"); }
315 body {
316     /* Chebyshev coefficients for x(K1(x) - log(x/2) I1(x))
317      * in the interval [0,2].
318      * 
319      * lim(x->0){ x(K1(x) - log(x/2) I1(x)) } = 1.
320      */
321     immutable real[11] chebCoeffsA = [
322        -7.02386347938628759343e-18L,
323        -2.42744985051936593393e-15L,
324        -6.66690169419932900609e-13L,
325        -1.41148839263352776110e-10L,
326        -2.21338763073472585583e-8L,
327        -2.43340614156596823496e-6L,
328        -1.73028895751305206302e-4L,
329        -6.97572385963986435018e-3L,
330        -1.22611180822657148235e-1L,
331        -3.53155960776544875667e-1L,
332         1.52530022733894777053e0L  ];
333 
334     /* Chebyshev coefficients for exp(x) sqrt(x) K1(x)
335      * in the interval [2,infinity].
336      *
337      * lim(x->inf){ exp(x) sqrt(x) K1(x) } = sqrt(pi/2).
338      */
339     immutable real[25] chebCoeffsB = [
340        -5.75674448366501715755e-18L,
341         1.79405087314755922667e-17L,
342        -5.68946255844285935196e-17L,
343         1.83809354436663880070e-16L,
344        -6.05704724837331885336e-16L,
345         2.03870316562433424052e-15L,
346        -7.01983709041831346144e-15L,
347         2.47715442448130437068e-14L,
348        -8.97670518232499435011e-14L,
349         3.34841966607842919884e-13L,
350        -1.28917396095102890680e-12L,
351         5.13963967348173025100e-12L,
352        -2.12996783842756842877e-11L,
353         9.21831518760500529508e-11L,
354        -4.19035475934189648750e-10L,
355         2.01504975519703286596e-9L,
356        -1.03457624656780970260e-8L,
357         5.74108412545004946722e-8L,
358        -3.50196060308781257119e-7L,
359         2.40648494783721712015e-6L,
360        -1.93619797416608296024e-5L,
361         1.95215518471351631108e-4L,
362        -2.85781685962277938680e-3L,
363         1.03923736576817238437e-1L,
364         2.72062619048444266945e0L   ];
365 
366     real z = 0.5 * x;
367     if (x <= 2.0)
368         return log(z) * besselI1(x) + chebEval(x^^2 - 2, chebCoeffsA) / x;
369     else
370         return exp(-x) * chebEval(8.0/x - 2.0, chebCoeffsB) / sqrt(x);
371 }
372 
373 
374 unittest
375 {
376     assert (approxEqual(besselK1( 1), 6.0190723019723457474e-1, 8.9e-17));
377     assert (approxEqual(besselK1(10), 1.8648773453825584597e-5, 8.9e-17));
378 }
379