1 /** 2 Authors: [Ilya Yaroshenko](http://9il.github.io) 3 4 Copyright: © 2014-2015 [Ilya Yaroshenko](http://9il.github.io) 5 6 License: MIT 7 */ 8 module atmosphere.math; 9 10 //import core.stdc.tgmath; 11 import std.range; 12 import std.traits; 13 import std.typecons; 14 import std.math; 15 16 17 version (LDC) 18 { 19 pragma(LDC_intrinsic, "llvm.fmuladd.f#") 20 T llvm_fmuladd(T)(T vala, T valb, T valc) @safe pure nothrow @nogc; 21 } 22 23 /** 24 Computes accurate sum of binary logarithms of input range $(D r). 25 */ 26 ElementType!Range sumOfLog2s(Range)(Range r) 27 if (isInputRange!Range && isFloatingPoint!(ElementType!Range)) 28 { 29 import std.math: frexp; 30 long exp = 0; 31 Unqual!(typeof(return)) x = 1; 32 foreach (e; r) 33 { 34 if (e < 0) 35 return typeof(return).nan; 36 int lexp = void; 37 x *= frexp(e, lexp); 38 exp += lexp; 39 if (x < 0.5) 40 { 41 x *= 2; 42 exp--; 43 } 44 } 45 return exp + log2(x); 46 } 47 48 //Issue 14231 49 package T findRoot(T, DF, DT)(scope DF f, in T a, in T b, 50 scope DT tolerance) //= (T a, T b) => false) 51 if( 52 isFloatingPoint!T && 53 is(typeof(tolerance(T.init, T.init)) : bool) && 54 is(typeof(f(T.init)) == R, R) && isFloatingPoint!R 55 ) 56 { 57 import std.numeric: findRoot; 58 import std.functional; 59 immutable fa = f(a); 60 if(fa == 0) 61 return a; 62 immutable fb = f(b); 63 if(fb == 0) 64 return b; 65 immutable r = findRoot!(Unqual!T, Unqual!T)(f.toDelegate, a, b, fa, fb, tolerance.toDelegate); 66 // Return the first value if it is smaller or NaN 67 return !(fabs(r[2]) > fabs(r[3])) ? r[0] : r[1]; 68 } 69 70 //Issue 14231 71 package T findRoot(T, DF)(scope DF f, in T a, in T b) 72 { 73 return findRoot(f, a, b, (T a, T b) => false); 74 } 75 76 /** Inverse of the Log Minus Digamma function 77 * 78 * Returns x such $(D log(x) - digamma(x) == y). 79 * 80 * References: 81 * 1. Abramowitz, M., and Stegun, I. A. (1970). 82 * Handbook of mathematical functions. Dover, New York, 83 * pages 258-259, equation 6.3.18. 84 * 85 * Authors: Ilya Yaroshenko 86 */ 87 T logmdigammaInverse(T)(const T y) 88 { 89 //Issue 14231 90 //import std.numeric: findRoot; 91 enum maxY = 1 / T.min_normal; 92 static assert(maxY > 0 && maxY <= T.max); 93 if (y >= maxY) 94 return 1 / y; //lim x->0 (log(x)-digamma(x))*x == 1 95 if (y < 0) 96 return T.nan; 97 if (y < 2*T.min_normal) 98 return 0.5 / y; //6.3.18 99 if (y > 0) 100 // x/2 <= logmdigamma(1 / x) <= x, x > 0 101 // calls logmdigamma ~6 times 102 return 1 / findRoot((T x) => logmdigamma(1 / x) - y, y, 2*y); // ~6 times 103 return y; //NaN 104 } 105 106 107 unittest { 108 alias T = double; 109 import std.range : iota; 110 import std.math : approxEqual, nextDown; 111 //import std.mathspecial: logmdigamma; 112 foreach (x; iota(1.3L, 2L, 0.1L)) 113 { 114 assert(logmdigammaInverse(logmdigamma(x)).approxEqual(x)); 115 assert(logmdigammaInverse(logmdigamma(1/x)).approxEqual(1/x)); 116 } 117 //WolframAlpha, 22.02.2015 118 immutable T[2][5] testData = [ 119 [1.0L, 0.615556766479594378978099158335549201923L], 120 [1.0L/8, 4.15937801516894947161054974029150730555L], 121 [1.0L/1024, 512.166612384991507850643277924243523243L], 122 [0.000500083333325000003968249801594877323784632117L, 1000.0L], 123 [1017.644138623741168814449776695062817947092468536L, 1.0L/1024], 124 ]; 125 foreach(test; testData) 126 { 127 assert(approxEqual(logmdigammaInverse(test[0]), test[1], 1e-10, 0)); 128 } 129 assert(approxEqual(logmdigamma(logmdigammaInverse(1.0)), 1, 1e-15, 0)); 130 assert(approxEqual(logmdigamma(logmdigammaInverse(T.min_normal)), T.min_normal, 1e-15, 0)); 131 assert(approxEqual(logmdigamma(logmdigammaInverse(T.max/2)), T.max/2, 1e-15, 0)); 132 assert(approxEqual(logmdigammaInverse(logmdigamma(1.0)), 1, 1e-10, 0)); 133 assert(approxEqual(logmdigammaInverse(logmdigamma(T.min_normal)), T.min_normal, 1e-15, 0)); 134 assert(approxEqual(logmdigammaInverse(logmdigamma(T.max/2)), T.max/2, 1e-15, 0)); 135 } 136 137 T logmdigamma(T)(const T x) 138 { 139 static immutable T [7] Bn_n = [ 140 1.0L/(6*2), -1.0L/(30*4), 1.0L/(42*6), -1.0L/(30*8), 141 5.0L/(66*10), -691.0L/(2730*12), 7.0L/(6*14) ]; 142 143 if (x <= 0.0) 144 { 145 if (x == 0.0) 146 { 147 return T.infinity; 148 } 149 return T.nan; 150 } 151 152 T s = x; 153 T w = 0f; 154 while ( s < 10f ) { 155 w += 1f/s; 156 s += 1f; 157 } 158 T y; 159 if ( s < 1.0e17 ) { 160 immutable T z = 1.0/(s * s); 161 version(LDC) 162 { 163 y = z * 164 llvm_fmuladd(z, 165 llvm_fmuladd(z, 166 llvm_fmuladd(z, 167 llvm_fmuladd(z, 168 llvm_fmuladd(z, 169 llvm_fmuladd(z, 170 Bn_n[6] , 171 Bn_n[5]), 172 Bn_n[4]), 173 Bn_n[3]), 174 Bn_n[2]), 175 Bn_n[1]), 176 Bn_n[0]); 177 } 178 else 179 { 180 y = z * (Bn_n[0] 181 + z * (Bn_n[1] 182 + z * (Bn_n[2] 183 + z * (Bn_n[3] 184 + z * (Bn_n[4] 185 + z * (Bn_n[5] 186 + z * Bn_n[6])))))); 187 } 188 } else 189 y = 0f; 190 191 return x == s ? y + 0.5f/s : (log(x/s) + 0.5f/s + y + w); 192 } 193 194 unittest { 195 import std.typetuple; 196 import std.math: isNaN, isIdentical, NaN, approxEqual; 197 import std.mathspecial: digamma; 198 foreach(T; TypeTuple!(real, double, float)) 199 { 200 alias lmd = logmdigamma!T; 201 assert(lmd(-5.0).isNaN()); 202 assert(isIdentical(lmd(NaN(0xABC)), NaN(0xABC))); 203 assert(lmd(0.0) == T.infinity); 204 for(auto x = 0.01; x < 1.0; x += 0.1) 205 assert(approxEqual(digamma(x), log(x) - lmd(x))); 206 for(auto x = 1.0; x < 15.0; x += 1.0) 207 assert(approxEqual(digamma(x), log(x) - lmd(x))); 208 } 209 } 210 211 212 /++ 213 Find a real minimum of a real function $(D f(x)) via bracketing. 214 Given a function $(D f) and a range $(D (ax..bx)), 215 returns the value of $(D x) in the range which is closest to a minimum of $(D f(x)). 216 $(D f) is never evaluted at the endpoints of $(D ax) and $(D bx). 217 If $(D f(x)) has more than one minimum in the range, one will be chosen arbitrarily. 218 If $(D f(x)) returns NaN or -Infinity, $(D (x, f(x), NaN)) will be returned; 219 otherwise, this algorithm is guaranteed to succeed. 220 221 Params: 222 f = Function to be analyzed 223 ax = Left bound of initial range of f known to contain the minimum. 224 bx = Right bound of initial range of f known to contain the minimum. 225 relTolerance = Relative tolerance. 226 absTolerance = Absolute tolerance. 227 228 Preconditions: 229 $(D ax) and $(D bx) shall be finite reals. $(BR) 230 $(D relTolerance) shall be normal positive real. $(BR) 231 $(D absTolerance) shall be normal positive real no less then $(D T.epsilon*2). 232 233 Returns: 234 A tuple consisting of $(D x), $(D y = f(x)) and $(D error = 3 * (absTolerance * fabs(x) + relTolerance)). 235 236 The method used is a combination of golden section search and 237 successive parabolic interpolation. Convergence is never much slower 238 than that for a Fibonacci search. 239 240 References: 241 "Algorithms for Minimization without Derivatives", Richard Brent, Prentice-Hall, Inc. (1973) 242 243 See_Also: $(LREF findRoot), $(XREF math, isNormal) 244 +/ 245 246 Tuple!(T, "x", Unqual!(ReturnType!DF), "y", T, "error") 247 findLocalMin(T, DF)( 248 scope DF f, 249 in T ax, 250 in T bx, 251 in T relTolerance = T.epsilon^^0.25, 252 in T absTolerance = sqrt(T.epsilon), 253 ) 254 if(isFloatingPoint!T) 255 in 256 { 257 assert(isFinite(ax) && isFinite(bx), "ax and bx shall be finite reals"); 258 assert(isNormal(absTolerance) && absTolerance >= T.epsilon*2, 259 "absTolerance shall be normal positive real no less then $(D T.epsilon*2)"); 260 assert(isNormal(relTolerance) && relTolerance > 0, 261 "relTolerance shall be normal positive real"); 262 } 263 out(result) 264 { 265 assert(isFinite(result.x)); 266 } 267 body 268 { 269 alias R = Unqual!(CommonType!(ReturnType!DF, T)); 270 271 // c is the squared inverse of the golden ratio 272 // (3 - sqrt(5))/2 273 // Value obtained from Wolfram Alpha. 274 immutable T c = 0x0.61c8864680b583ea0c633f9fa31237p+0L; 275 immutable T cm1 = 0x0.9e3779b97f4a7c15f39cc0605cedc8p+0L; 276 277 R tolerance; 278 279 T a = ax > bx ? bx : ax; 280 T b = ax > bx ? ax : bx; 281 282 //sequence of declarations suitable for SIMD instructions 283 284 T v = a * cm1 + b * c; 285 assert(isFinite(v)); 286 287 R fv = f(v); 288 289 //R fv = f(v); 290 if(isNaN(fv) || fv == -T.infinity) 291 return typeof(return)(v, fv, T.init); 292 293 T w = v; 294 R fw = fv; 295 296 T x = v; 297 R fx = fv; 298 299 for(R d = 0, e = 0;;) 300 { 301 T m = (a + b) / 2; 302 303 // This fix is not part of original algorithm 304 if(!isFinite(m)) // fix infinity loop. Issue can be reproduced in R. 305 { 306 m = a / 2 + b / 2; 307 if(!isFinite(m)) // fast-math compiler switch is enabled 308 { 309 //SIMD instructions can be used by compiler, do not reduce declarations 310 int a_exp = void; 311 int b_exp = void; 312 immutable an = frexp(a, a_exp); 313 immutable bn = frexp(b, b_exp); 314 immutable am = ldexp(an, a_exp-1); 315 immutable bm = ldexp(bn, b_exp-1); 316 m = am + bm; 317 if(!isFinite(m)) // wrong input: constraints is disabled in release mode 318 { 319 return typeof(return).init; 320 } 321 } 322 } 323 assert(isFinite(m)); 324 tolerance = absTolerance * fabs(x) + relTolerance; 325 immutable t2 = tolerance * 2; 326 //assert(x + tolerance < b); 327 //assert(x - tolerance > a); 328 329 330 // check stopping criterion 331 if (!(fabs(x - m) > t2 - (b - a) / 2)) 332 break; 333 334 R p = 0; 335 R q = 0; 336 R r = 0; 337 338 // fit parabola 339 if (fabs(e) > tolerance) 340 { 341 immutable xw = x - w; 342 immutable fxw = fx - fw; 343 immutable xv = x - v; 344 immutable fxv = fx - fv; 345 immutable xwfxv = xw * fxv; 346 immutable xvfxw = xv * fxw; 347 p = xv * xvfxw - xw * xwfxv; 348 q = (xvfxw - xwfxv) * 2; 349 if (q > 0) 350 p = -p; 351 else 352 q = -q; 353 r = e; 354 e = d; 355 } 356 357 T u; 358 // a parabolic-interpolation step 359 if (fabs(p) < fabs(q * r / 2) && p > q * (a - x) && p < q * (b - x)) 360 { 361 d = p / q; 362 u = x + d; 363 // f must not be evaluated too close to a or b 364 if (u - a < t2 || b - u < t2) 365 d = x < m ? tolerance : -tolerance; 366 } 367 // a golden-section step 368 else 369 { 370 e = (x < m ? b : a) - x; 371 d = c * e; 372 } 373 374 // f must not be evaluated too close to x 375 u = x + (fabs(d) >= tolerance ? d : d > 0 ? tolerance : -tolerance); 376 immutable fu = f(u); 377 if(isNaN(fu) || fu == -T.infinity) 378 return typeof(return)(u, fu, T.init); 379 380 // update a, b, v, w, and x 381 if (fu <= fx) 382 { 383 u < x ? b : a = x; 384 v = w; fv = fw; 385 w = x; fw = fx; 386 x = u; fx = fu; 387 } 388 else 389 { 390 u < x ? a : b = u; 391 if (fu <= fw || w == x) 392 { 393 v = w; fv = fw; 394 w = u; fw = fu; 395 } 396 else if (fu <= fv || v == x || v == w) 397 { // do not remove this braces 398 v = u; fv = fu; 399 } 400 } 401 } 402 return typeof(return)(x, fx, tolerance * 3); 403 } 404 405 /// 406 unittest 407 { 408 int i; 409 double f(double x) 410 { 411 i++; 412 return fabs(x-1); 413 } 414 415 //slow 416 auto minD = findLocalMin(&f, -double.max/2, double.max/2, double.min_normal, 2*double.epsilon); 417 with(minD) 418 { 419 assert(approxEqual(x, 1)); 420 assert(approxEqual(y, 0)); 421 assert(error <= 10 * double.epsilon); 422 //assert(minD.error == 0); 423 } 424 } 425 426 unittest 427 { 428 auto ret = findLocalMin((double x) => double.init, 0.0, 1.0, double.min_normal, 2*double.epsilon); 429 assert(!ret.x.isNaN); 430 assert(ret.y.isNaN); 431 assert(ret.error.isNaN); 432 } 433 434 unittest 435 { 436 auto ret = findLocalMin((double x) => log(x), 0.0, 1.0, double.min_normal, 2*double.epsilon); 437 assert(ret.error < 3.00001 * ((2*double.epsilon)*fabs(ret.x)+ double.min_normal)); 438 assert(ret.x >= 0 && ret.x <= ret.error); 439 } 440 441 442 unittest 443 { 444 size_t i; 445 auto ret = findLocalMin((double x) {i++; return log(x);}, 0.0, double.max, double.min_normal, 2*double.epsilon); 446 assert(ret.y < -18); 447 assert(ret.error < 5e-08); 448 assert(ret.x >= 0 && ret.x <= ret.error); 449 } 450 451 452 unittest 453 { 454 size_t i; 455 auto ret = findLocalMin((double x) {i++; return -fabs(x);}, -1.0, 1.0, double.min_normal, 2*double.epsilon); 456 assert(ret.x.fabs.approxEqual(1)); 457 assert(ret.y.fabs.approxEqual(1)); 458 assert(ret.error.approxEqual(0)); 459 } 460 461 unittest 462 { 463 size_t i; 464 auto ret = findLocalMin((double x) {i++; return -fabs(x);}, -1.0, 1.0, double.min_normal, 2*double.epsilon); 465 assert(ret.x.fabs.approxEqual(1)); 466 assert(ret.y.fabs.approxEqual(1)); 467 assert(ret.error.approxEqual(0)); 468 } 469 470 471 version(none) 472 // check speed regressions and infinity loops 473 unittest 474 { 475 import std.typetuple; 476 import std.math; 477 size_t i = 0; 478 R f00(T, R)(T x) { i++; return 0; } 479 R f01(T, R)(T x) { i++; return 1; } 480 R f02(T, R)(T x) { i++; return R.min_normal/2; } //subnormal 481 R f03(T, R)(T x) { i++; return R(PI); } 482 R f04(T, R)(T x) { i++; return log2(cast(R)x); } 483 R f05(T, R)(T x) { i++; return log(cast(R)x); } 484 R f06(T, R)(T x) { i++; return exp2(cast(R)x); } 485 R f07(T, R)(T x) { i++; return exp(cast(R)x); } 486 R f08(T, R)(T x) { i++; return sqrt(cast(R)x); } 487 R f09(T, R)(T x) { i++; return cbrt(cast(R)x); } 488 R f10(T, R)(T x) { i++; return x; } 489 R f11(T, R)(T x) { i++; return cast(R)(x)^^2; } 490 R f12(T, R)(T x) { i++; return cast(R)(x)^^PI; } 491 R f13(T, R)(T x) { i++; return cast(R)(x)^^(1/PI); } 492 R f14(T, R)(T x) { i++; return sin(cast(R)x); } 493 R f15(T, R)(T x) { i++; return cos(cast(R)x); } 494 R f16(T, R)(T x) { i++; return sqrt(abs(cast(R)(x)^^2 - 1)); } 495 R f17(T, R)(T x) { i++; return sqrt(abs(cast(R)(x)^^2 - 1)); } 496 R f18(T, R)(T x) { i++; return floor(exp(cast(R)x)); } //multiminimum 497 R f19(T, R)(T x) { i++; return floor(log(cast(R)x)); } //multiminimum 498 R f20(T, R)(T x) { i++; return floor(cast(R)x); } //multiminimum 499 // vars for global checks 500 int s1, s2; 501 int c1, c2, c; 502 foreach(T; TypeTuple!( 503 float, 504 double, 505 real, 506 )) // 1 507 foreach(R; TypeTuple!( 508 float, 509 double, 510 real, 511 )) // 2 512 { 513 immutable ar1 = [ 514 &f00!(T, R), 515 &f01!(T, R), 516 &f02!(T, R), 517 &f03!(T, R), 518 &f04!(T, R), 519 &f05!(T, R), 520 &f06!(T, R), 521 &f07!(T, R), 522 &f08!(T, R), 523 &f09!(T, R), 524 &f10!(T, R), 525 &f11!(T, R), 526 &f12!(T, R), 527 &f13!(T, R), 528 &f14!(T, R), 529 &f15!(T, R), 530 &f16!(T, R), 531 &f17!(T, R), 532 &f18!(T, R), 533 &f19!(T, R), 534 &f20!(T, R), 535 ]; 536 537 foreach(relTol; [ 538 T.min_normal, 539 T.epsilon, 540 sqrt(T.epsilon), 541 T.epsilon^^0.25, 542 ]) // 3 543 foreach(absTol; [ 544 2*T.epsilon, 545 sqrt(T.epsilon), 546 ]) // 4 547 { 548 foreach(sign; [-1, 1]) // 5 549 foreach(rBound; [1, T.max]) // 6 550 foreach(shift; [0, -1, 1]) // 7 551 foreach(j, f; ar1) // 8 552 { 553 auto m2 = findLocalMin!T((T x) => sign * f(x-shift), shift, rBound+shift, relTol, absTol); 554 } 555 } 556 } 557 } 558 559 560 Unqual!(CommonType!(T1, T2)) 561 chebevReversed(T1, T2)(in T1[] c, in T2 x) 562 { 563 typeof(return) d = 0; 564 typeof(return) dd = 0; 565 foreach(e; c[0..$-1]) 566 { 567 immutable sv = d; 568 d = 2 * x * d - dd + e; 569 dd = sv; 570 } 571 return x * d - dd + c[$-1] / 2; 572 } 573 574 575 576 T modifiedBesselCF2(T)(in T nu, in T x) @nogc @safe pure nothrow 577 if(isFloatingPoint!T) 578 in { 579 assert(fabs(nu) <= 0.5f); 580 assert(x >= 2); 581 assert(isFinite(x)); 582 } 583 body { 584 T bc = 2 * (1 + x); 585 if(isInfinity(bc)) 586 return 1; 587 T d = 1 / bc; 588 T h = d; 589 T delh = d; 590 T ac = nu^^2 - 0.25f; 591 int i = 1; 592 do 593 { 594 immutable ip1 = i + 1; 595 immutable a = ac - ip1 * i; 596 immutable b = bc + i * 2; 597 i = ip1; 598 d = 1 / (b + a * d); 599 delh = (b * d - 1) * delh; 600 h += delh; 601 debug assert(!isNaN(delh / h)); 602 } 603 while(fabs(delh / h) > T.epsilon); 604 return (nu + 0.5f + x + ac * h) / x; 605 } 606 607 608 609 T[2] modifiedBesselCF2Full(T)(in T nu, in T x) @nogc @safe pure nothrow 610 if(isFloatingPoint!T) 611 in { 612 assert(fabs(nu) <= 0.5f); 613 assert(x >= 2); 614 assert(isFinite(x)); 615 } 616 body { 617 T bc = 2 * (1 + x); 618 if(isInfinity(bc)) 619 return [nu < 0.5f ? 0 : 1.2533141373155003, 1]; 620 T d = 1 / bc; 621 T h = d; 622 T delh = d; 623 T ac = nu^^2 - 0.25f; 624 T q1 = 0; 625 T q2 = 1; 626 T q = -ac; 627 T c = -ac; 628 T s = 1 + q*delh; 629 T dels = void; 630 uint i = 1; 631 T b = bc; 632 do 633 { 634 immutable ip1 = i + 1; 635 immutable a = ac - ip1 * i; 636 c = -a * c / ip1; 637 immutable qnew = (q1 - b * q2) / a; 638 q1 = q2; 639 q2 = qnew; 640 q += c * qnew; 641 b = bc + i * 2; 642 d = 1 / (b + a * d); 643 delh = (b * d - 1) * delh; 644 dels = q * delh; 645 h += delh; 646 s += dels; 647 i = ip1; 648 } 649 //Need only test convergence of sum since CF2 itself converges more quickly. 650 while(fabs(dels / s) > T.epsilon); 651 652 enum T SQRTPI_2 = sqrt(PI / 2); 653 return [SQRTPI_2 / s, (nu + 0.5 + x + ac * h) / x]; 654 } 655 656 657 unittest 658 { 659 assert(approxEqual(modifiedBesselCF2(0.4, 2.00001), 1.44212, 0.0, 1e-4)); 660 assert(approxEqual(modifiedBesselCF2(0.5, 2.00001), 1.5, 0.0, 1e-4)); 661 assert(approxEqual(modifiedBesselCF2(0.0, 2.00001), 1.22804, 0.0, 1e-4)); 662 assert(approxEqual(modifiedBesselCF2(0.4, 1000.0), 1.00100, 0.0, 1e-3)); 663 assert(approxEqual(modifiedBesselCF2(0.499, 1000.0), 1.00090, 0.0, 1e-4)); 664 assert(approxEqual(modifiedBesselCF2(0.0, 1000), 1.000499875124805092705355767307116656100795175108335495396004, 0.0, 1e-14)); 665 } 666 667 /++ 668 Returns: 669 [K(x, nu), 2 / x * K(x, nu+1)] 670 +/ 671 T[2] modifiedBesselTemmeSeriesImpl(T)(in T nu, in T x) @nogc @safe pure nothrow 672 in { 673 assert(fabs(nu) <= 0.5f); 674 assert(x <= 2); 675 } 676 body { 677 //TODO: recalculate c1 and c2 for different FP formats 678 static if(is(T == float)) 679 { 680 static immutable float[4] c1 = [ 681 -3.4706269649e-6, 682 3.087090173086e-4, 683 6.5165112670737e-3, 684 -1.142022680371168e0, 685 ]; 686 static immutable float[8] c2 = [ 687 -4.9717367042e-6, 688 1.2719271366546e-3, 689 -7.68528408447867e-2, 690 1.843740587300905e0, 691 ]; 692 } 693 else 694 { 695 static immutable double[7] c1 = [ 696 -1.356e-13, 697 3.67795e-11, 698 6.9437664e-9, 699 -3.4706269649e-6, 700 3.087090173086e-4, 701 6.5165112670737e-3, 702 -1.142022680371168e0, 703 ]; 704 static immutable double[8] c2 = [ 705 -1.49e-15, 706 -1.702e-13, 707 2.423096e-10, 708 -3.31261198e-8, 709 -4.9717367042e-6, 710 1.2719271366546e-3, 711 -7.68528408447867e-2, 712 1.843740587300905e0, 713 ]; 714 } 715 716 immutable T x2 = x / 2; 717 immutable T pimu = cast(T) PI * nu; 718 immutable T nu2 = nu^^2; 719 immutable T fact = abs(pimu) < T.epsilon ? 1 : pimu / sin(pimu); 720 T d = -log(x2); 721 T e = nu * d; 722 immutable T fact2 = abs(e) < T.epsilon ? 1 : sinh(e) / e; 723 immutable T xx = 8 * nu^^2 - 1; 724 immutable T gam1 = chebevReversed(c1, xx); 725 immutable T gam2 = chebevReversed(c2, xx); 726 immutable T gampl = gam2 - nu * gam1; 727 immutable T gammi = gam2 + nu * gam1; 728 T ff = fact * (gam1 * cosh(e) + gam2 * fact2 * d); 729 T sum = ff; 730 e = exp(e); 731 T p = 0.5f * e / gampl; 732 T q = 0.5f / (e * gammi); 733 T c = 1; 734 d = x2^^2; 735 T sum1 = p; 736 int i; 737 T del = void; 738 do { 739 i++; 740 ff = (i * ff + p + q) / (i * i - nu2); 741 c *= d / i; 742 p /= i - nu; 743 q /= i + nu; 744 del = c * ff; 745 sum += del; 746 immutable del1 = c * (p - i * ff); 747 sum1 += del1; 748 } 749 while(abs(del) >= abs(sum) * T.epsilon); 750 return [sum, sum1]; 751 } 752 753 /++ 754 Returns: 755 K(x, nu+1) / K(x, nu) 756 +/ 757 T modifiedBesselTemmeSeries(T)(in T nu, in T x) 758 in { 759 assert(fabs(nu) <= 0.5f); 760 assert(x <= 2); 761 } 762 body { 763 immutable sums = modifiedBesselTemmeSeriesImpl(nu, x); 764 return sums[1] / sums[0] / x * 2; 765 } 766 767 unittest 768 { 769 assert(approxEqual(modifiedBesselTemmeSeries(0.4, 1.0), 1.87491, 0.0, 1e-4)); 770 assert(approxEqual(modifiedBesselTemmeSeries(0.499, 1.0), 1.9987, 0.0, 1e-4)); 771 assert(approxEqual(modifiedBesselTemmeSeries(0.0, 1.0), 1.429625398260401758028108023450365630080017018192662983459034, 0.0, 1e-14)); 772 } 773 774 775 T besselKD(T)(in T nu, in T x) 776 if(isFloatingPoint!T) 777 out(r) { 778 import std.conv; 779 assert(r.isNaN || r >= 1, text("x = ", x, " nu = ", nu, " r = ", r)); 780 } 781 in { 782 assert(x >= 0); 783 assert(x < T.infinity); 784 } 785 body { 786 version(LDC) 787 { 788 import ldc.intrinsics: log = llvm_log, log2 = llvm_log2, fabs = llvm_fabs; 789 } 790 immutable anu = fabs(nu); 791 int nl = cast(int)floor(anu+0.5); 792 T mu=anu-nl; 793 //import std.stdio; 794 ////writeln(nu); 795 assert(fabs(mu) <= 0.5f); 796 T ret; 797 if(x >= 2) 798 { 799 T r = modifiedBesselCF2(mu, x); 800 mu *= 2; 801 if(nl) 802 { 803 T d; 804 do 805 { 806 d = r; 807 mu += 2; 808 r = mu / x + 1 / r; 809 } 810 while(--nl); 811 ret = r / d; 812 } 813 else 814 { 815 ret = r * (r - mu / x); 816 } 817 } 818 else 819 { 820 immutable sums = modifiedBesselTemmeSeriesImpl(mu, x); 821 T r = sums[1] / sums[0]; 822 if(nl == 0) 823 { 824 if(mu < 0) 825 { 826 if(r >= mu) 827 r = mu.nextDown; 828 } 829 else if(mu > 0) 830 { 831 if(r <= mu) 832 r = mu.nextUp; 833 } 834 else 835 { 836 if(r == 0) 837 return x ? 1 / (x * log(x))^^2 : T.infinity; 838 } 839 } 840 //import std.stdio; 841 //writeln("r = ", r); 842 //writeln("sums[1] = ", sums[1]); 843 //writeln("sums[0] = ", sums[0]); 844 if(nl) 845 { 846 immutable x2_4 = x * x / 4; 847 T d; 848 do 849 { 850 d = r; 851 mu += 1; 852 r = mu + x2_4 / r; 853 } 854 while(--nl); 855 ret = r / d; 856 } 857 else 858 { 859 // 860 //writeln("r - mu = ", r - mu); 861 ret = r / x * (r - mu) / x * 4; 862 } 863 } 864 //import std.stdio; 865 //writeln("ret = ", ret); 866 //writeln("x = ", x); 867 return fmax(ret, cast(T)1); 868 } 869 870 unittest { 871 assert(approxEqual(besselKD(0.46, 1.0), 2.006492004938732508413453526510952827555303123690945926246942, 0.0, 1e-14)); 872 assert(approxEqual(besselKD(0.46, 1e10), 1.000000000100000000000000000000019199999996160000000869529599, 0.0, 1e-14)); 873 assert(approxEqual(besselKD(0.46, 1e5), 1.000010000000000019199616008695062658208936100866055206637995, 0.0, 1e-14)); 874 //import std.stdio; 875 //writeln(besselKD(0.23, 5.20964e-116 )); 876 assert(approxEqual(besselKD(0.46, 1e-10), 5.2419685330917511351588283276691731294601340736654433005e+10, 1, 1e-14)); 877 assert(approxEqual(besselKD(0.0, 1.0), 2.043828779351212337989476332008573915738881135574432645409779, 0.0, 1e-14)); 878 assert(approxEqual(besselKD(0.0, 2.0), 1.508074700999049512999886217349628893603326842760291910336296, 0.0, 1e-14)); 879 assert(approxEqual(besselKD(0.0, 0.5), 3.210807086475177172355017867637452321623564307416114645664507, 0.0, 1e-14)); 880 assert(approxEqual(besselKD(1.0, 0.5), 2.543749846614157209231745521424755087195088446583780182048739, 0.0, 1e-14)); 881 assert(approxEqual(besselKD(1.0, 1.0), 1.888245647341297344338366806469818100073176922119421727713106, 0.0, 1e-14)); 882 assert(approxEqual(besselKD(1.0, 2.0), 1.477404884746695468820498957141015915645967793158828983150281, 0.0, 1e-14)); 883 assert(approxEqual(besselKD(0.4, 2.0), 1.502873827552600466534737445302356963995715395265386806359790, 0.0, 1e-14)); 884 assert(approxEqual(besselKD(0.4, 1.0), 2.015349430323277001160289680464116945013807398639704169522157, 0.0, 1e-14)); 885 assert(approxEqual(besselKD(0.4, 0.5), 3.071599175777718550825656123800662591905836277372864541044359, 0.0, 1e-13)); 886 assert(approxEqual(besselKD(11.1, 1.11e+10), 1.000000000090090090090090090045136443975807419926316735329998, 0.0, 1e-14)); 887 assert(approxEqual(besselKD(11.1, 1.11e-10), 1.099009900990099009900983462621096186432918126639008518272442, 0.0, 1e-14)); 888 assert(approxEqual(besselKD(2.0, 3.0), 1.296650971433360224728795947829976056477719397636727742896, 0.0, 1e-14)); 889 assert(approxEqual(besselKD(2.2, 0.8), 1.624595391806645609233071414803368115353081130470326304255669, 0.0, 1e-14)); 890 assert(approxEqual(besselKD(0.4, 3.0), 1.334349384832101337822320699631685431560403480505548447388101, 0.0, 1e-14)); 891 assert(approxEqual(besselKD(0.4, 0.8), 2.275575715445329347201695021084846610619371526255383391028726, 0.0, 1e-14)); 892 assert(approxEqual(besselKD(1.0, 1.0), 1.888245647341297344338366806469818100073176922119421727713106268163595832389436074069065151380128808, 0.0, 1e-14)); 893 assert(approxEqual(besselKD(1.0, 10.0), 1.099687904173359294358694759872440909233436542663077290833713959244496776538436835904856529604568407, 0.0, 1e-14)); 894 assert(approxEqual(besselKD(10.0, 1.0), 1.110348556190446111957216138204031855721452559868300859853180425692157659153049491662221677392988991, 0.0, 1e-14)); 895 assert(approxEqual(besselKD(10.0, 1.49011e-08), 1.11111, 0.0, 1e-4)); 896 assert(approxEqual(besselKD(1.0, 1.49011e-08), 36.2755, 0.0, 1e-4)); 897 assert(approxEqual(besselKD(1.0, 2.22044e-16), 72.3192, 0.0, 1e-4)); 898 assert(approxEqual(besselKD(1.0, 1.49166e-154), 708.628, 0.0, 1e-3)); 899 assert(approxEqual(besselKD(10.0, 1.49166e-154), 1.111111111111111, 0.0, 1e-4)); 900 assert(approxEqual(besselKD(1.1, double.min_normal), 11.0, 0.0, 1e-4)); 901 assert(isFinite(besselKD(0.1, double.epsilon))); 902 assert(isFinite(besselKD(0, double.epsilon))); 903 } 904 905 906 T besselKRM(T)(in T nu, in T x) 907 if(isFloatingPoint!T) 908 out(r){ 909 //import std.stdio; 910 //writefln("KRM(%s, %s) = %s", nu, x, r); 911 } 912 in { 913 914 } 915 body { 916 version(LDC) 917 { 918 import ldc.intrinsics: log = llvm_log, log2 = llvm_log2, fabs = llvm_fabs; 919 } 920 if(nu < 0) 921 return 1 / besselKRM(-nu, x); 922 immutable anu = fabs(nu); 923 int nl = cast(int)floor(anu+0.5f); 924 T mu=anu-nl; 925 assert(fabs(mu) <= 0.5f); 926 927 if(x >= 2) 928 { 929 T r = modifiedBesselCF2(mu, x); 930 mu *= 2; 931 if(nl) 932 { 933 T d; 934 do 935 { 936 d = r; 937 mu += 2; 938 r = mu / x + 1 / r; 939 } 940 while(--nl); 941 return sqrt(r) * sqrt(d); 942 } 943 else 944 { 945 return sqrt(r / (r - mu / x)); 946 } 947 } 948 else 949 { 950 immutable sums = modifiedBesselTemmeSeriesImpl(mu, x); 951 T r = sums[1] / sums[0]; 952 //import std.stdio; 953 //writeln(sums, " ", r); 954 if(nl) 955 { 956 immutable x2_4 = x * x / 4; 957 T d; 958 do 959 { 960 d = r; 961 mu += 1; 962 r = mu + x2_4 / r; 963 } 964 while(--nl); 965 //import std.stdio; 966 //writeln("sqrt(r) = ", sqrt(r), " sqrt(d) = ", sqrt(d)); 967 return sqrt(r) * sqrt(d) * 2 / x; 968 } 969 else 970 { 971 //writeln("r = ", r, " mu = ", mu); 972 T ret = sums[1] / (sums[1] - mu * sums[0]); 973 if(ret < 0) 974 { 975 return T.infinity; 976 } 977 //import std.stdio; 978 //writeln("sums[1] - mu * sums[0] = %s, ", sums[1], sums[1] - mu * sums[0]); 979 if(ret == T.infinity) 980 { 981 //writeln(ret); 982 ret = T.max; 983 //return T.max; 984 } 985 return sqrt(ret); 986 } 987 } 988 } 989 990 unittest { 991 assert(approxEqual(besselKRM(0.0, 1.0), 1, 0.0, 1e-14)); 992 assert(approxEqual(besselKRM(0.0, 2.0), 1, 0.0, 1e-14)); 993 assert(approxEqual(besselKRM(0.0, 0.5), 1, 0.0, 1e-14)); 994 assert(approxEqual(besselKRM(1.0, 0.5), 2.857882088842869132027932428611809106473984497664375438969650, 0.0, 1e-14)); 995 assert(approxEqual(besselKRM(1.0, 1.0), 1.964497593920848638120029967522901757996791347548236796086390, 0.0, 1e-14)); 996 assert(approxEqual(besselKRM(1.0, 2.0), 1.492661023078886446121280337304042151124577136818374559084502, 0.0, 1e-14)); 997 assert(approxEqual(besselKRM(0.4, 2.0), 1.176363556186439775048077295541564293759652878052851708370510, 0.0, 1e-14)); 998 assert(approxEqual(besselKRM(0.4, 1.0), 1.320700844681798447461890099116285783758321865242462619205560, 0.0, 1e-14)); 999 assert(approxEqual(besselKRM(0.4, 0.5), 1.555719773577105697258998484956087203635813355802430411799783, 0.0, 1e-14)); 1000 assert(approxEqual(besselKRM(11.1, 1.11e+10), 1.000000001000000000454954954912953493935881553614516153308770, 0.0, 1e-14)); 1001 assert(approxEqual(besselKRM(11.1, 1.11e-10), 1.9077839604210010339594792869174419072661582323995475082e+11, 0.0, 1e-14)); 1002 } 1003 1004 1005 T logBesselK(T)(in T nu, in T x) 1006 if(isFloatingPoint!T) 1007 in { 1008 assert(x.isFinite); 1009 assert(x >= T.min_normal); 1010 } 1011 body { 1012 version(LDC) 1013 { 1014 import ldc.intrinsics: log = llvm_log, log2 = llvm_log2, fabs = llvm_fabs; 1015 } 1016 immutable anu = fabs(nu); 1017 int nl = cast(int)floor(anu+0.5f); 1018 T mu = anu - nl; 1019 assert(fabs(mu) <= 0.5f); 1020 if(x >= 2) 1021 { 1022 immutable r2 = modifiedBesselCF2Full(mu, x); 1023 T r = r2[1]; 1024 T ret = log(r2[0]) - log(x) / 2 - x; 1025 if(nl) 1026 { 1027 mu *= 2; 1028 T l = 1; 1029 long exs; 1030 do 1031 { 1032 int ex = void; 1033 l *= frexp(r, ex); 1034 exs += ex; 1035 mu += 2; 1036 r = mu / x + 1 / r; 1037 } 1038 while(--nl); 1039 ret += cast(T)LN2 * (exs + log2(l)); 1040 } 1041 return ret; 1042 } 1043 else 1044 { 1045 immutable sums = modifiedBesselTemmeSeriesImpl(mu, x); 1046 T r = sums[1] / sums[0]; 1047 T ret = log(sums[0]); 1048 if(nl) 1049 { 1050 ret += nl * (cast(T)LN2 - log(x)); 1051 immutable x2_4 = x * x / 4; 1052 T l = 1; 1053 long exs; 1054 do 1055 { 1056 int ex = void; 1057 l *= frexp(r, ex); 1058 exs += ex; 1059 mu += 1; 1060 r = mu + x2_4 / r; 1061 } 1062 while(--nl); 1063 ret += cast(T)LN2 * (exs + log2(l)); 1064 } 1065 return ret; 1066 } 1067 } 1068 1069 unittest { 1070 assert(approxEqual(logBesselK(0.0, 1.0), -0.86506439890678809679875790803368568022489315035161867839839, 0.0, 1e-14)); 1071 assert(approxEqual(logBesselK(0.0, 2.0), -2.17248820497570993473841333643717923143973636448830725037508, 0.0, 1e-14)); 1072 assert(approxEqual(logBesselK(0.0, 0.5), -0.07858976986908141689523697453802973224104044591707028228710, 0.0, 1e-14)); 1073 assert(approxEqual(logBesselK(1.0, 0.5), 0.504671397304651177308416839874408827783443947120148152746119, 0.0, 1e-14)); 1074 assert(approxEqual(logBesselK(1.0, 1.0), -0.50765194821075233094791485120634298590875979858568077818450, 0.0, 1e-14)); 1075 assert(approxEqual(logBesselK(1.0, 2.0), -1.96707130256051389147686464265533209478058062247688850653003, 0.0, 1e-14)); 1076 assert(approxEqual(logBesselK(0.4, 2.0), -2.13936877477972262972321591624176676086808535490162854194273, 0.0, 1e-14)); 1077 assert(approxEqual(logBesselK(0.4, 1.0), -0.80679541168661951923202239125477303379665660825595791745329, 0.0, 1e-14)); 1078 assert(approxEqual(logBesselK(0.4, 0.5), 0.018456437585950072585426399714417863872587115447191398524780, 0.0, 1e-14)); 1079 assert(approxEqual(logBesselK(11.1, 1.11e+10), -1.110000001133931411444888363310129265544563412394720435e10, 0.0, 1e-14)); 1080 assert(approxEqual(logBesselK(11.1, 1.11e-10), 276.7693978383758755547170249282452519578463074349871817713218, 0.0, 1e-11)); 1081 } 1082 1083 1084 T besselK(Flag!"ExponentiallyScaled" expFlag = Flag!"ExponentiallyScaled".no, T)(in T nu, in T x) 1085 if(isFloatingPoint!T) 1086 in { 1087 assert(x.isFinite); 1088 assert(x >= T.min_normal); 1089 } 1090 body { 1091 version(LDC) 1092 { 1093 import ldc.intrinsics: log = llvm_log, log2 = llvm_log2, fabs = llvm_fabs; 1094 } 1095 immutable anu = fabs(nu); 1096 int nl = cast(int)floor(anu+0.5f); 1097 T mu = anu - nl; 1098 assert(fabs(mu) <= 0.5f); 1099 T r = void, ret = void; 1100 if(x >= 2) 1101 { 1102 immutable r2 = modifiedBesselCF2Full(mu, x); 1103 r = r2[1]; 1104 static if(expFlag) 1105 ret = r2[0] / sqrt(x); 1106 else 1107 ret = r2[0] / sqrt(x) * exp(-x); 1108 } 1109 else 1110 { 1111 immutable sums = modifiedBesselTemmeSeriesImpl(mu, x); 1112 r = sums[1] / sums[0] / x * 2; 1113 static if(expFlag) 1114 ret = sums[0] * exp(x); 1115 else 1116 ret = sums[0]; 1117 } 1118 if(nl) 1119 { 1120 mu *= 2; 1121 do 1122 { 1123 ret *= r; 1124 mu += 2; 1125 r = mu / x + 1 / r; 1126 } 1127 while(--nl); 1128 } 1129 return ret; 1130 1131 } 1132 1133 unittest { 1134 assert(approxEqual(besselK(0.0, 1.0), 0.421024438240708333335627379212609036136219748226660472298969, 0.0, 1e-14)); 1135 assert(approxEqual(besselK(0.0, 2.0), 0.113893872749533435652719574932481832998326624388808882892529, 0.0, 1e-14)); 1136 assert(approxEqual(besselK(0.0, 0.5), 0.924419071227665861781924167530216989538768311953529684815019, 0.0, 1e-14)); 1137 assert(approxEqual(besselK(1.0, 0.5), 1.656441120003300893696445403174091511534100759464077446055427, 0.0, 1e-14)); 1138 assert(approxEqual(besselK(1.0, 1.0), 0.601907230197234574737540001535617339261586889968106456017767, 0.0, 1e-14)); 1139 assert(approxEqual(besselK(1.0, 2.0), 0.139865881816522427284598807035411023887234584841515530384442, 0.0, 1e-14)); 1140 assert(approxEqual(besselK(0.4, 2.0), 0.117729133170423325690699234439848483526261145336868148793547, 0.0, 1e-14)); 1141 assert(approxEqual(besselK(0.4, 1.0), 0.446285939834668179310023409179745077844437039396299727961180, 0.0, 1e-14)); 1142 assert(approxEqual(besselK(0.4, 0.5), 1.018627810316608462220290727117637366662549451305627759999544, 0.0, 1e-14)); 1143 assert(approxEqual(besselK(11.1, 100000.0), 0.0, 0.0, 1e-14)); 1144 assert(approxEqual(besselK(11.1, 1/100000.0), 1.5940696949048155233993419471665743544335615887236248959e+65, 1e51, 1e-14)); 1145 1146 assert(approxEqual(besselK!(Flag!"ExponentiallyScaled".yes)(0.4, 2.0), 0.869907169474735192472996963053995791191543506954884340734219, 0.0, 1e-14)); 1147 assert(approxEqual(besselK!(Flag!"ExponentiallyScaled".yes)(0.4, 1.0), 1.213130960549345270517674559709580335203213288805317162444177, 0.0, 1e-14)); 1148 assert(approxEqual(besselK!(Flag!"ExponentiallyScaled".yes)(0.4, 0.5), 1.679433337795687807090050796759423855788542163267174953125487, 0.0, 1e-14)); 1149 assert(approxEqual(besselK!(Flag!"ExponentiallyScaled".yes)(11.1, 100000.0), 0.003965764688216290045701642597190265433950245246337093558273, 0.0, 1e-14)); 1150 1151 assert(approxEqual(besselK(1.0, 2.0), 0.139865881816522427284, 0.0, 1e-14)); 1152 assert(approxEqual(besselK(1.0, 20.0), 5.88305796955703817765028e-10, 0.0, 1e-14)); 1153 assert(approxEqual(besselK(1.2, 1.0), 0.701080)); 1154 assert(approxEqual(besselK(10.0, 400.0), 1.359308280937876101049800424530298700140591995078599604e-175, 0.0, 1e-14)); 1155 } 1156 1157 1158 T besselKRS(T)(in T nu, in T x) 1159 if(isFloatingPoint!T) 1160 in { 1161 assert(x.isFinite); 1162 assert(x >= T.min_normal); 1163 } 1164 body { 1165 version(LDC) 1166 { 1167 import ldc.intrinsics: log = llvm_log, log2 = llvm_log2, fabs = llvm_fabs; 1168 } 1169 immutable anu = fabs(nu); 1170 int nl = cast(int)floor(anu+0.5f); 1171 T mu=anu-nl; 1172 assert(fabs(mu) <= 0.5f); 1173 if(x >= 2) 1174 { 1175 T r = modifiedBesselCF2(mu, x); 1176 mu *= 2; 1177 if(nl) 1178 { 1179 T d = void; 1180 do 1181 { 1182 d = r; 1183 mu += 2; 1184 r = mu / x + 1 / r; 1185 } 1186 while(--nl); 1187 return r + 1 / d; 1188 } 1189 else 1190 { 1191 return r + (r - mu / x); 1192 } 1193 } 1194 else 1195 { 1196 immutable sums = modifiedBesselTemmeSeriesImpl(mu, x); 1197 T r = sums[1] / sums[0]; 1198 immutable x_2 = x / 2; 1199 if(nl) 1200 { 1201 immutable x2_4 = x_2^^2; 1202 T d = void; 1203 do 1204 { 1205 d = r; 1206 mu += 1; 1207 r = mu + x2_4 / r; 1208 } 1209 while(--nl); 1210 return r / x_2 + x_2 / d; 1211 } 1212 else 1213 { 1214 if(fabs(r) < fabs(mu)) 1215 r = mu; 1216 return r / x_2 + (r - mu) / x_2; 1217 } 1218 } 1219 } 1220 1221 unittest { 1222 assert(approxEqual(besselKRS(0.0, 1.0), 2.859250796520803516056216046900731260160034036385325966918068, 0.0, 1e-14)); 1223 assert(approxEqual(besselKRS(0.0, 2.0), 2.456073859637815951485344904163437808478733905321719388934076, 0.0, 1e-14)); 1224 assert(approxEqual(besselKRS(0.0, 0.5), 3.583745016864440467305949390266788044526362750672196202869812, 0.0, 1e-14)); 1225 assert(approxEqual(besselKRS(1.0, 0.5), 5.116150836953170679741316342708831083392598534819729276449587, 0.0, 1e-14)); 1226 assert(approxEqual(besselKRS(1.0, 1.0), 3.398967871187544687785354799542729509747041034806318935543498, 0.0, 1e-14)); 1227 assert(approxEqual(besselKRS(1.0, 2.0), 2.628615517527578979897586948721927750995112315346619061302174, 0.0, 1e-14)); 1228 assert(approxEqual(besselKRS(0.4, 2.0), 2.484249446052147531028619260526756105681146308342088119522102, 0.0, 1e-14)); 1229 assert(approxEqual(besselKRS(0.4, 1.0), 2.949813167184170666766713209833464015196615131065026837642303, 0.0, 1e-14)); 1230 assert(approxEqual(besselKRS(0.4, 0.5), 3.853102218097889263846807474862866521371874509444807669438352, 0.0, 1e-14)); 1231 assert(approxEqual(besselKRS(11.1, 1.11e+10), 2.000000000090090091088061033917072660444297152286161364522101, 0.0, 1e-14)); 1232 assert(approxEqual(besselKRS(11.1, 1.11e-10), 2.0000000000000000000001099009900990099009900953267052034e+11, 0.0, 1e-14)); 1233 } 1234 1235 1236 T besselKRX(T)(in T nu, in T x) 1237 if(isFloatingPoint!T) 1238 in { 1239 assert(nu >= 0); 1240 assert(x.isFinite); 1241 assert(x >= T.min_normal); 1242 } 1243 body { 1244 version(LDC) 1245 { 1246 import ldc.intrinsics: log = llvm_log, log2 = llvm_log2, fabs = llvm_fabs; 1247 } 1248 immutable anu = fabs(nu); 1249 int nl = cast(int)floor(anu+0.5f); 1250 T mu = anu - nl; 1251 assert(fabs(mu) <= 0.5f); 1252 if(x >= 2) 1253 { 1254 T r = modifiedBesselCF2(mu, x); 1255 if(nl) 1256 { 1257 mu *= 2; 1258 do 1259 { 1260 mu += 2; 1261 r = mu / x + 1 / r; 1262 } 1263 while(--nl); 1264 } 1265 return r * x; 1266 } 1267 else 1268 { 1269 immutable sums = modifiedBesselTemmeSeriesImpl(mu, x); 1270 T r = sums[1] / sums[0]; 1271 if(nl) 1272 { 1273 immutable x2_4 = x * x / 4; 1274 do 1275 { 1276 mu += 1; 1277 r = mu + x2_4 / r; 1278 } 1279 while(--nl); 1280 } 1281 else 1282 { 1283 if(fabs(r) < fabs(mu)) 1284 r = mu; 1285 } 1286 return r * 2; 1287 } 1288 } 1289 1290 unittest { 1291 assert(approxEqual(besselKRX(10.0, double.min_normal), 20.0, 0.0, 1e-14)); 1292 assert(approxEqual(besselKRX(0.0, 1.0), 1.429625398260401758028108023450365630080017018192662983459034, 0.0, 1e-14)); 1293 assert(approxEqual(besselKRX(0.0, 2.0), 2.456073859637815951485344904163437808478733905321719388934076, 0.0, 1e-14)); 1294 assert(approxEqual(besselKRX(0.0, 0.5), 0.895936254216110116826487347566697011131590687668049050717453, 0.0, 1e-14)); 1295 assert(approxEqual(besselKRX(1.0, 0.5), 2.279037709238292669935329085677207770848149633704932319112396, 0.0, 1e-14)); 1296 assert(approxEqual(besselKRX(1.0, 1.0), 2.699483935593772343892677399771364754873520517403159467771749, 0.0, 1e-14)); 1297 assert(approxEqual(besselKRX(1.0, 2.0), 3.628615517527578979897586948721927750995112315346619061302174, 0.0, 1e-14)); 1298 assert(approxEqual(besselKRX(0.4, 2.0), 2.884249446052147531028619260526756105681146308342088119522102, 0.0, 1e-14)); 1299 assert(approxEqual(besselKRX(0.4, 1.0), 1.874906583592085333383356604916732007598307565532513418821151, 0.0, 1e-14)); 1300 assert(approxEqual(besselKRX(0.4, 0.5), 1.363275554524472315961701868715716630342968627361201917359588, 0.0, 1e-14)); 1301 assert(approxEqual(besselKRX(11.1, 11.1), 27.05342065662651512004991741552637606461741839973809374665713, 0.0, 1e-14)); 1302 assert(approxEqual(besselKRX(11, 2.0^^30), 1.000000010710209660444967811966826255158285934025032904639974*2.0^^30, 0.0, 1e-14)); 1303 assert(approxEqual(besselKRX(11, 2.0^^30), 1.000000010710209660444967811966826255158285934025032904639974*2.0^^30, 0.0, 1e-14)); 1304 assert(approxEqual(besselKRX(11, 2.0^^40), 1.000000000010459189070438615765505792843518644341506580233109*2.0^^40, 0.0, 1e-14)); 1305 assert(approxEqual(besselKRX(11, 1e+10), 1.0000000011500000006037499999396249998267992188210915625e+10, 1, 1e-14)); 1306 assert(approxEqual(besselKRX(11.1, 1.11e+10), 1.1100000011600000005538738738239753265465849195188195573e+10, 1, 1e-14)); 1307 assert(approxEqual(besselKRX(11.1, 1.11e-10), 22.20000000000000000000060995049504950495049502906321387905301, 0.0, 1e-14)); 1308 } 1309 1310 1311 T besselKR(T)(in T nu, in T x) 1312 if(isFloatingPoint!T) 1313 in { 1314 } 1315 body { 1316 version(LDC) 1317 { 1318 import ldc.intrinsics: log = llvm_log, log2 = llvm_log2, fabs = llvm_fabs; 1319 } 1320 if(nu < 0) 1321 return 1 / besselKR(-nu-1, x); 1322 if(x.isNaN() || nu.isNaN() || x < 0) 1323 return T.init; 1324 if(x < T.min_normal) 1325 return T.infinity; 1326 immutable anu = fabs(nu); 1327 int nl = cast(int)floor(anu+0.5f); 1328 T mu = anu - nl; 1329 assert(fabs(mu) <= 0.5f); 1330 if(x >= 2) 1331 { 1332 T r = modifiedBesselCF2(mu, x); 1333 if(nl) 1334 { 1335 mu *= 2; 1336 do 1337 { 1338 mu += 2; 1339 r = mu / x + 1 / r; 1340 } 1341 while(--nl); 1342 } 1343 return r; 1344 } 1345 else 1346 { 1347 immutable sums = modifiedBesselTemmeSeriesImpl(mu, x); 1348 T r = sums[1] / sums[0]; 1349 if(nl) 1350 { 1351 immutable x2_4 = x * x / 4; 1352 do 1353 { 1354 mu += 1; 1355 r = mu + x2_4 / r; 1356 } 1357 while(--nl); 1358 } 1359 return r * 2 / x; 1360 } 1361 } 1362 1363 unittest { 1364 assert(approxEqual(besselKR(0.0, 1.0) , 1.429625398260401758028108023450365630080017018192662983459034, 0.0, 1e-14)); 1365 assert(approxEqual(besselKR(0.0, 2.0) * 2, 2.456073859637815951485344904163437808478733905321719388934076, 0.0, 1e-14)); 1366 assert(approxEqual(besselKR(0.0, 0.5) / 2, 0.895936254216110116826487347566697011131590687668049050717453, 0.0, 1e-14)); 1367 assert(approxEqual(besselKR(1.0, 0.5) / 2, 2.279037709238292669935329085677207770848149633704932319112396, 0.0, 1e-14)); 1368 assert(approxEqual(besselKR(1.0, 1.0) , 2.699483935593772343892677399771364754873520517403159467771749, 0.0, 1e-14)); 1369 assert(approxEqual(besselKR(1.0, 2.0) * 2, 3.628615517527578979897586948721927750995112315346619061302174, 0.0, 1e-14)); 1370 assert(approxEqual(besselKR(0.4, 2.0) * 2, 2.884249446052147531028619260526756105681146308342088119522102, 0.0, 1e-14)); 1371 assert(approxEqual(besselKR(0.4, 1.0) , 1.874906583592085333383356604916732007598307565532513418821151, 0.0, 1e-14)); 1372 assert(approxEqual(besselKR(0.4, 0.5) / 2, 1.363275554524472315961701868715716630342968627361201917359588, 0.0, 1e-14)); 1373 assert(approxEqual(besselKR(11.1, 1.11e+10), 1.1100000011600000005538738738239753265465849195188195573e+10 / 1.11e+10, 1, 1e-14)); 1374 assert(approxEqual(besselKR(11.1, 1.11e-10) * 1.11e-10, 22.20000000000000000000060995049504950495049502906321387905301, 0.0, 1e-14)); 1375 1376 assert(approxEqual(besselKR(-1.0, 1.0), 1 / 1.429625398260401758028108023450365630080017018192662983459034, 0.0, 1e-14)); 1377 } 1378 1379 1380 T besselKR(T)(in T nu, in T x, uint k) 1381 { 1382 T l = besselKR(nu, x); 1383 T r = l; 1384 T mu = nu * 2; 1385 while(k--) 1386 { 1387 mu += 2; 1388 r = mu / x + 1 / r; 1389 l *= r; 1390 } 1391 return l; 1392 } 1393 1394 unittest { 1395 assert(approxEqual(besselKR(0.0, 1.0, 0) , 1.429625398260401758028108023450365630080017018192662983459034, 0.0, 1e-14)); 1396 assert(approxEqual(besselKR(0.0, 2.0, 0) * 2, 2.456073859637815951485344904163437808478733905321719388934076, 0.0, 1e-14)); 1397 assert(approxEqual(besselKR(0.0, 1.0, 3), 105.0590223025824984495740493132204752844809530187891270737059, 1e-10, 1e-14)); 1398 assert(approxEqual(besselKR(0.0, 2.0, 3), 19.28036929818907975742672452081718904239366952660859694467038, 1e-10, 1e-14)); 1399 assert(approxEqual(besselKR(0.0, 0.5, 3), 813.7490033728880934611898780533576089052725501344392405739625, 1e-10, 1e-14)); 1400 assert(approxEqual(besselKR(1.0, 0.5, 3), 7303.597652823473130198226747312888245046226857159388835630678, 1e-10, 1e-14)); 1401 assert(approxEqual(besselKR(1.0, 1.0, 3), 599.6947228611295581541061895533584099941981855502445314254368, 1e-10, 1e-14)); 1402 assert(approxEqual(besselKR(1.0, 2.0, 3), 67.42923276291368469846380423082891626492668473019928591953262, 1e-10, 1e-14)); 1403 assert(approxEqual(besselKR(0.4, 2.0, 3), 32.55703150637502077170415944139304552928545885327101882556329, 1e-10, 1e-14)); 1404 assert(approxEqual(besselKR(0.4, 1.0, 3), 222.9905656901318819890519502437505989113682776582595951935857, 1e-10, 1e-14)); 1405 assert(approxEqual(besselKR(0.4, 0.5, 3), 2177.389452959348919338879066729351907090043415959389603727847, 1e-10, 1e-14)); 1406 }