1 2var defaultDecimals = 18; 3 4var pi = Math.PI; 5 6var eulerGamma = .5772156649015329; 7 8var constants = { 9 10 decimals: 50, precisionScale: 10n**50n, 11 12 e: 271828182845904523536028747135266249775724709369995n, 13 14 eulerGamma: 57721566490153286060651209008240243104215933593992n, 15 16 ln10: 230258509299404568401799145468436420760110148862877n, 17 18 pi: 314159265358979323846264338327950288419716939937510n 19 20}; 21 22function getConstant( name ) { 23 24 return constants[name] * precisionScale / constants.precisionScale; 25 26} 27 28 29// oeis.org/A000367 30bernoulli2nN = [ 1n, 1n, -1n, 1n, -1n, 5n, -691n, 7n, -3617n, 43867n, -174611n, 854513n, -236364091n, 8553103n, -23749461029n, 8615841276005n, -7709321041217n, 2577687858367n, -26315271553053477373n, 2929993913841559n, -261082718496449122051n, 1520097643918070802691n, -27833269579301024235023n, 596451111593912163277961n, -5609403368997817686249127547n, 495057205241079648212477525n, -801165718135489957347924991853n, 29149963634884862421418123812691n, -2479392929313226753685415739663229n, 84483613348880041862046775994036021n, -1215233140483755572040304994079820246041491n, 12300585434086858541953039857403386151n, -106783830147866529886385444979142647942017n, 1472600022126335654051619428551932342241899101n, -78773130858718728141909149208474606244347001n, 1505381347333367003803076567377857208511438160235n, -5827954961669944110438277244641067365282488301844260429n, 34152417289221168014330073731472635186688307783087n, -24655088825935372707687196040585199904365267828865801n, 414846365575400828295179035549542073492199375372400483487n, -4603784299479457646935574969019046849794257872751288919656867n, 1677014149185145836823154509786269900207736027570253414881613n, -2024576195935290360231131160111731009989917391198090877281083932477n, 660714619417678653573847847426261496277830686653388931761996983n, -1311426488674017507995511424019311843345750275572028644296919890574047n, 1179057279021082799884123351249215083775254949669647116231545215727922535n, -1295585948207537527989427828538576749659341483719435143023316326829946247n, 1220813806579744469607301679413201203958508415202696621436215105284649447n, -211600449597266513097597728109824233673043954389060234150638733420050668349987259n, 67908260672905495624051117546403605607342195728504487509073961249992947058239n, -94598037819122125295227433069493721872702841533066936133385696204311395415197247711n ]; 31 32// oeis.org/A002445 33bernoulli2nD = [ 1n, 6n, 30n, 42n, 30n, 66n, 2730n, 6n, 510n, 798n, 330n, 138n, 2730n, 6n, 870n, 14322n, 510n, 6n, 1919190n, 6n, 13530n, 1806n, 690n, 282n, 46410n, 66n, 1590n, 798n, 870n, 354n, 56786730n, 6n, 510n, 64722n, 30n, 4686n, 140100870n, 6n, 30n, 3318n, 230010n, 498n, 3404310n, 6n, 61410n, 272118n, 1410n, 6n, 4501770n, 6n, 33330n ]; 34 35 36var factorialCache = [ 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800 ]; 37 38 39function complex( x, y=0 ) { 40 41 if ( y === 0 && isArbitrary(x) ) y = 0n; 42 43 return { re: x, im: y }; 44 45} 46 47var C = complex; 48 49function isComplex( x ) { return typeof x === 'object' && 're' in x; } 50 51 52var decimals = [], precisionScale; 53var arb1, arb2, onePi, twoPi, halfPi, ln10; 54 55function setPrecisionScale( n ) { 56 57 if ( n === 'reset' ) decimals.shift(); 58 else decimals.unshift( n ); 59 60 precisionScale = 10n**BigInt( decimals[0] ); 61 62 // set some commonly used constants 63 arb1 = arbitrary(1); 64 arb2 = 2n * arb1; 65 onePi = getConstant( 'pi' ); 66 twoPi = 2n * onePi; 67 halfPi = onePi / 2n; 68 ln10 = getConstant( 'ln10' ); 69 70} 71 72function resetPrecisionScale() { setPrecisionScale( 'reset' ); } 73 74setPrecisionScale( defaultDecimals ); 75 76function arbitrary( x ) { 77 78 if ( isComplex(x) ) return { re: arbitrary( x.re ), im: arbitrary( x.im ) }; 79 80 if ( isArbitrary(x) ) return Number(x) / 10**decimals[0]; 81 82 // BigInt from exponential form includes wrong digits 83 // manual construction from string more accurate 84 85 var parts = x.toExponential().split( 'e' ); 86 var mantissa = parts[0].replace( '.', '' ); 87 var digits = mantissa.length - ( mantissa[0] === '-' ? 2 : 1 ) 88 var padding = +parts[1] + decimals[0] - digits; 89 90 if ( padding < 0 ) return BigInt( Math.round( x * 10**decimals[0] ) ); 91 92 return BigInt( mantissa + '0'.repeat(padding) ); 93 94} 95 96var A = arbitrary; 97 98function isArbitrary( x ) { return typeof x === 'bigint' || typeof x.re === 'bigint'; } 99 100 101function isZero( x ) { 102 103 if ( isComplex(x) ) return x.re === 0 && x.im === 0; 104 return x === 0; 105 106} 107 108function isUnity( x ) { 109 110 if ( isComplex(x) ) return x.re === 1 && x.im === 0; 111 return x === 1; 112 113} 114 115function isReal( x ) { 116 117 if ( isComplex(x) ) return x.im === 0; 118 return true; 119 120} 121 122function isInteger( x ) { 123 124 if ( isComplex(x) ) return Number.isInteger(x.re) && x.im === 0; 125 return Number.isInteger(x); 126 127} 128 129function isPositiveInteger( x ) { 130 131 if ( isComplex(x) ) return Number.isInteger(x.re) && x.re > 0 && x.im === 0; 132 return Number.isInteger(x) && x > 0;
133 134} 135 136function isPositiveIntegerOrZero( x ) { 137 138 if ( isComplex(x) ) return Number.isInteger(x.re) && x.re >= 0 && x.im === 0; 139 return Number.isInteger(x) && x >= 0; 140 141} 142 143function isNegativeInteger( x ) { 144 145 if ( isComplex(x) ) return Number.isInteger(x.re) && x.re < 0 && x.im === 0; 146 return Number.isInteger(x) && x < 0; 147 148} 149 150function isNegativeIntegerOrZero( x ) { 151 152 if ( isComplex(x) ) return Number.isInteger(x.re) && x.re <= 0 && x.im === 0; 153 return Number.isInteger(x) && x <= 0; 154 155} 156 157function isEqualTo( x, y ) { 158 159 if ( isComplex(x) || isComplex(y) ) { 160 if ( !isComplex(x) ) x = complex(x); 161 if ( !isComplex(y) ) y = complex(y); 162 return x.re === y.re && x.im === y.im; 163 } 164 165 return x === y; 166 167} 168 169 170function re( x ) { 171 172 if ( isComplex(x) ) return x.re; 173 return x; 174 175} 176 177var real = re; 178 179function im( x ) { 180 181 if ( isComplex(x) ) return x.im; 182 return 0; 183 184} 185 186var imag = im; 187 188function abs( x ) { 189 190 if ( isComplex(x) ) { 191 192 if ( isArbitrary(x) ) { 193 194 if ( x.im === 0n ) return abs(x.re); 195 196 if ( x.re === 0n ) return abs(x.im); 197 198 if ( abs(x.re) < abs(x.im) ) 199 200 return mul( abs(x.im), sqrt( arb1 + div( div( mul(x.re,x.re), x.im ), x.im ) ) ); 201 202 else 203 204 return mul( abs(x.re), sqrt( arb1 + div( div( mul(x.im,x.im), x.re ), x.re ) ) ); 205 206 } 207 208 if ( x.im === 0 ) return Math.abs(x.re); 209 210 if ( x.re === 0 ) return Math.abs(x.im); 211 212 if ( Math.abs(x.re) < Math.abs(x.im) ) 213 214 return Math.abs(x.im) * Math.sqrt( 1 + ( x.re / x.im )**2 ); 215 216 else 217 218 return Math.abs(x.re) * Math.sqrt( 1 + ( x.im / x.re )**2 ); 219 220 } 221 222 if ( isArbitrary(x) ) 223 if ( x < 0n ) return -x; 224 else return x; 225 226 return Math.abs(x); 227 228} 229 230function arg( x ) { 231 232 if ( isArbitrary(x) ) return ln(x).im; 233 234 // adding zero prevents unexpected behavior for -0 235 236 if ( isComplex(x) ) return Math.atan2( x.im + 0, x.re + 0 ); 237 238 return Math.atan2( 0, x + 0 ); 239 240} 241 242 243// JavaScript does not support operator overloading 244 245function add( x, y ) { 246 247 if ( arguments.length > 2 ) { 248 249 var z = add( x, y ); 250 for ( var i = 2 ; i < arguments.length ; i++ ) z = add( z, arguments[i] ); 251 return z; 252 253 } 254 255 if ( isComplex(x) || isComplex(y) ) { 256 257 if ( !isComplex(x) ) x = complex(x); 258 if ( !isComplex(y) ) y = complex(y); 259 260 return { re: x.re + y.re, im: x.im + y.im }; 261 262 } 263 264 return x + y; 265 266} 267 268function sub( x, y ) { 269 270 if ( isComplex(x) || isComplex(y) ) { 271 272 if ( !isComplex(x) ) x = complex(x); 273 if ( !isComplex(y) ) y = complex(y); 274 275 return { re: x.re - y.re, im: x.im - y.im }; 276 277 } 278 279 return x - y; 280 281} 282 283function mul( x, y ) { 284 285 if ( arguments.length > 2 ) { 286 287 var z = mul( x, y ); 288 for ( var i = 2 ; i < arguments.length ; i++ ) z = mul( z, arguments[i] ); 289 return z; 290 291 } 292 293 if ( isComplex(x) || isComplex(y) ) { 294 295 if ( !isComplex(x) ) x = complex(x); 296 if ( !isComplex(y) ) y = complex(y); 297 298 if ( isArbitrary(x) ) 299 300 return { re: ( x.re * y.re - x.im * y.im ) / precisionScale, 301 im: ( x.im * y.re + x.re * y.im ) / precisionScale }; 302 303 return { re: x.re * y.re - x.im * y.im, 304 im: x.im * y.re + x.re * y.im }; 305 306 } 307 308 if ( isArbitrary(x) ) return x * y / precisionScale; 309 310 return x * y; 311 312} 313 314function neg( x ) { 315 316 if ( isComplex(x) ) return { re: -x.re, im: -x.im }; 317 318 return -x; 319 320} 321 322function div( x, y ) { 323 324 if ( isComplex(x) || isComplex(y) ) { 325 326 if ( !isComplex(x) ) x = complex(x); 327 if ( !isComplex(y) ) y = complex(y); 328 329 if ( y.re === 0 && y.im === 0 || y.re === 0n && y.im === 0n ) // operator precedence 330 throw Error( 'Division by zero' ); 331 332 if ( isArbitrary(x) ) { 333 334 var N = { re: x.re * y.re + x.im * y.im, 335 im: x.im * y.re - x.re * y.im }; 336 var D = y.re * y.re + y.im * y.im; 337 338 return { re: precisionScale * N.re / D, 339 im: precisionScale * N.im / D }; 340 341 } 342 343 if ( Math.abs(y.re) < Math.abs(y.im) ) { 344 345 var f = y.re / y.im; 346 return { re: ( x.re * f + x.im ) / ( y.re * f + y.im ), 347 im: ( x.im * f - x.re ) / ( y.re * f + y.im ) }; 348 349 } else { 350 351 var f = y.im / y.re; 352 return { re: ( x.re + x.im * f ) / ( y.re + y.im * f ), 353 im: ( x.im - x.re * f ) / ( y.re + y.im * f ) }; 354 355 } 356 357 } 358 359 if ( y === 0 || y === 0n ) throw Error( 'Division by zero' ); 360 361 if ( isArbitrary(x) ) return precisionScale * x / y; 362 363 return x / y; 364 365} 366 367function inv( x ) { 368 369 if ( isArbitrary(x) ) return div( arb1, x ); 370 371 return div( 1, x ); 372 373} 374 375function pow( x, y ) { 376 377 if ( isArbitrary(x) || isArbitrary(y) ) { 378 379 if ( !isArbitrary(x) ) x = arbitrary(x); 380 if ( !isArbitrary(y) ) y = arbitrary(y); 381 382 return exp( mul( y, ln(x) ) ); 383 384 } 385 386 if ( isComplex(x) || isComplex(y) ) { 387 388 if ( !isComplex(x) ) x = complex(x); 389 if ( !isComplex(y) ) y = complex(y); 390 391 if ( x.re === 0 && x.im === 0 && y.re > 0 ) 392 return complex(0); 393 if ( x.re === 0 && x.im === 0 && y.re === 0 && y.im === 0 ) 394 return complex(1); 395 if ( x.re === 0 && x.im === 0 && y.re < 0 ) 396 throw Error( 'Power singularity' ); 397 398 return exp( mul( y, log(x) ) ); 399 400 } 401 402 if ( x === 0 && y < 0 ) throw Error( 'Power singularity' ); 403 404 if ( x < 0 && !Number.isInteger(y) ) return pow( complex(x), y ); 405 406 return x**y; 407 408} 409 410function root( x, y ) { return pow( x, div( 1, y ) ); } 411 412function surd( x, n ) { 413 414 if ( isComplex(x) || isComplex(n) ) throw Error( 'Surd requires real inputs' ); 415 416 if ( !isInteger(n) ) throw Error( 'Second parameter of surd must be integer' ); 417 418 if ( n & 1 ) { 419 var sign = Math.sign(x); // zero at origin anyway 420 return sign * root( sign*x, n ); 421 } 422 423 if ( x < 0 ) throw Error( 'First parameter of surd must be positive for even integers' ); 424
425 return root( x, n ); 426 427} 428 429function sqrt( x ) { 430 431 if ( isComplex(x) ) { 432 433 if ( isArbitrary(x) ) { 434 435 if ( x.im === 0n ) 436 if ( x.re < 0n ) return { re: 0n, im: sqrt(-x.re) }; 437 else return { re: sqrt(x.re), im: 0n }; 438 439 // need evaluation independent of natural logarithm 440 441 var c = abs(x); 442 var sign = x.im < 0n ? -1n : 1n; 443 444 return { re: sqrt( div( c + x.re, arb2 ) ), im: sign * sqrt( div( c - x.re, arb2 ) ) } 445 446 } 447 448 if ( x.im === 0 ) 449 if ( x.re < 0 ) return { re: 0, im: Math.sqrt(-x.re) }; 450 else return { re: Math.sqrt(x.re), im: 0 }; 451 452 // expression above suffers from rounding errors when not arbitrary, 453 // especially affecting elliptic integral of third kind 454 455 return exp( mul( .5, log(x) ) ); 456 457 } 458 459 if ( isArbitrary(x) ) { 460 461 if ( x === 0n ) return 0n; 462 463 if ( x < 0n ) throw Error( 'Cannot evaluate real square root of ' + x ); 464 465 // Brent, Modern Computer Arithmetic, SqrtInt algorithm 466 467 var u = x, s, t; 468 469 while ( u !== s ) { 470 s = u; 471 t = s + div( x, s ); 472 u = div( t, arb2 ); 473 } 474 475 return s; 476 477 } 478 479 if ( x < 0 ) return { re: 0, im: Math.sqrt(-x) }; 480 481 return Math.sqrt(x); 482 483} 484 485function fibonacci( n, real=false ) { 486 487 var sqrt5 = sqrt(5), r = ( 1 + sqrt5 )/2; 488 489 if ( real ) return div( sub( pow(r,n), mul( pow(1/r,n), cos(mul(n,pi)) ) ), sqrt5 ); 490 491 return div( sub( pow(r,n), pow(-1/r,n) ), sqrt5 ); 492 493} 494 495 496function complexAverage( f, x, offset=1e-5 ) { 497 498 return div( add( f(add(x,offset)), f(sub(x,offset)) ), 2 ); 499 500} 501 502 503function complexFromString( s, returnAsString=false ) { 504 505 var lead = '', real, imag; 506 507 if ( s[0] === '+' || s[0] === '-' ) { 508 lead = s[0]; 509 s = s.slice(1); 510 } 511 512 if ( s.includes('+') || s.includes('-') ) { 513 if ( s.includes('+') ) { 514 real = lead + s.slice( 0, s.indexOf('+') ); 515 imag = s.slice( s.indexOf('+') + 1, s.length - 1 ); 516 } else { 517 real = lead + s.slice( 0, s.indexOf('-') ); 518 imag = s.slice( s.indexOf('-'), s.length - 1 ); 519 } 520 } else { 521 if ( s.includes('i') ) { 522 real = '0'; 523 imag = lead + s.slice( 0, s.length - 1 ); 524 } else { 525 real = lead + s; 526 imag = '0'; 527 } 528 } 529 530 if ( imag === '' || imag === '-' ) imag += '1'; 531 532 if ( returnAsString ) return `{ re: ${real}, im: ${imag} }`; 533 534 return { re: +real, im: +imag }; 535 536} 537 538function output() { 539 540 for ( var i = 0 ; i < arguments.length ; i++ ) console.log( arguments[i] ); 541 542} 543 544 545function besselJ( n, x ) { 546 547 if ( isComplex(n) || isComplex(x) ) { 548 549 if ( isNegativeInteger(n) ) return mul( pow(-1,n), besselJ( neg(n), x ) ); 550 551 var product = div( pow( div(x,2), n ), gamma( add(n,1) ) ); 552 return mul( product, hypergeometric0F1( add(n,1), mul(-.25, pow(x,2) ) ) ); 553 554 } 555 556 if ( isNegativeInteger(n) ) return (-1)**n * besselJ( -n, x ); 557 558 if ( !Number.isInteger(n) && x < 0 ) return besselJ( n, complex(x) ); 559 560 return (x/2)**n * hypergeometric0F1( n+1, -.25*x**2 ) / gamma(n+1); 561 562} 563 564function besselJZero( n, m, derivative=false ) { 565 566 if ( isComplex(n) || isComplex(m) ) 567 throw Error( 'Complex arguments not supported in Bessel zero' ); 568 569 if ( !isPositiveInteger(m) ) throw Error( 'Unsupported index for Bessel zero' ); 570 571 // approximations from dlmf.nist.gov/10.21.vi 572 var delta = pi/4; 573 574 if ( derivative ) { 575 576 if ( n === 0 && m === 1 ) return 0; 577 578 var b = ( m + Math.abs(n)/2 - 3/4 ) * pi; 579 var e = b - ( 4*n**2 + 3 ) / ( 8*b ); 580 581 // Zeros not simple to enumerate for negative order 582 // Odd double zero for small range of n to be investigated 583 if ( n < 0 ) throw Error( 'Negative order for Bessel derivative zero not supported' ); 584 585 return findRoot( x => diff( x => besselJ(n,x), x ), [ e-delta < 0 ? 0 : e-delta, e+delta ] ); 586 587 } else { 588 589 var a = ( m + Math.abs(n)/2 - 1/4 ) * pi; 590 var e = a - ( 4*n**2 - 1 ) / ( 8*a ); 591 592 if ( n < 0 ) e += fractionalPart(n) * pi; 593 594 return findRoot( x => besselJ(n,x), [ m === 1 ? 1e-10 : e-delta, e+delta ] ); 595 596 } 597 598} 599 600function besselY( n, x ) { 601 602 if ( isComplex(n) || isComplex(x) ) { 603 604 if ( isZero(x) ) { 605 if ( !isComplex(n) ) n = complex(n); 606 if ( fractionalPart(n.re) === -.5 && n.im === 0 ) return complex(0); 607 } 608 609 // dlmf.nist.gov/10.2.4 610 if ( isInteger(n) ) 611 return div( add( diff( n => besselJ(n,x), n ), 612 mul( pow(-1,n), diff( n => besselJ(n,x), neg(n) ) ) ), pi ); 613 614 var s = sub( mul( besselJ(n,x), cos( mul(n,pi) ) ), besselJ( neg(n), x ) ); 615 return div( s, sin( mul(n,pi) ) ); 616 617 } 618 619 if ( x < 0 ) return besselY( n, complex(x) ); 620 621 if ( x === 0 ) 622 if ( fractionalPart(n) === -.5 ) return 0; 623 624 // dlmf.nist.gov/10.2.4 625 if ( Number.isInteger(n) ) 626 return ( diff( n => besselJ(n,x), n ) + (-1)**n * diff( n => besselJ(n,x), -n ) ) / pi; 627 628 return ( besselJ(n,x) * cos(n*pi) - besselJ(-n,x) ) / sin(n*pi); 629 630} 631 632function besselYZero( n, m, derivative=false ) { 633 634 if ( isComplex(n) || isComplex(m) ) 635 throw Error( 'Complex arguments not supported in Bessel zero' ); 636 637 if ( !isPositiveInteger(m) ) throw Error( 'Unsupported index for Bessel zero' ); 638 639 // approximations from dlmf.nist.gov/10.21.vi 640 var delta = pi/4; 641 642 if ( derivative ) { 643 644 var b = ( m + Math.abs(n)/2 - 1/4 ) * pi; 645 var e = b - ( 4*n**2 + 3 ) / ( 8*b ); 646 647 // Zeros not simple to enumerate for negative order 648 // Odd double zero for small range of n to be investigated 649 if ( n < 0 ) throw Error( 'Negative order for Bessel derivative zero not supported' ); 650 651 return findRoot( x => diff( x => besselY(n,x), x ), [ e-delta, e+delta ] ); 652 653 } else { 654 655 var a = ( m + Math.abs(n)/2 - 3/4 ) * pi; 656 var e = a - ( 4*n**2 - 1 ) / ( 8*a ); 657 658 if ( n < 0 ) 659 if ( fractionalPart(n) > -.5 ) e += fractionalPart(n) * pi; 660 else e += ( 1 + fractionalPart(n) ) * pi; 661 662 return findRoot( x => besselY(n,x), [ m === 1 && fractionalPart(n) > -.5 ? 1e-10 : e-delta, e+delta ] ); 663 664 } 665 666} 667 668function besselI( n, x ) { 669 670 if ( isComplex(n) || isComplex(x) ) { 671 672 if ( isNegativeInteger(n) ) return besselI( neg(n), x ); 673
674 var product = div( pow( div(x,2), n ), gamma( add(n,1) ) ); 675 return mul( product, hypergeometric0F1( add(n,1), mul(.25, pow(x,2) ) ) ); 676 677 } 678 679 if ( isNegativeInteger(n) ) return besselI( -n, x ); 680 681 if ( !Number.isInteger(n) && x < 0 ) return besselI( n, complex(x) ); 682 683 return (x/2)**n * hypergeometric0F1( n+1, .25*x**2 ) / gamma(n+1); 684 685} 686 687function besselK( n, x ) { 688 689 var useAsymptotic = 10; 690 691 if ( isComplex(n) || isComplex(x) ) { 692 693 // asymptotic form as per Johansson arxiv.org/abs/1606.06977 694 if ( abs(x) > useAsymptotic ) { 695 696 var t1 = mul( sqrt( div( pi/2, x ) ), exp( neg(x) ) ); 697 var t2 = hypergeometric2F0( add(n,.5), sub(.5,n), div(-.5,x) ); 698 699 return mul( t1, t2 ); 700 701 } 702 703 // dlmf.nist.gov/10.27.5 704 if ( isInteger(n) ) 705 return mul( pow(-1,add(n,1)), .5, 706 add( diff( n => besselI(n,x), n ), diff( n => besselI(n,x), neg(n) ) ) ); 707 708 var product = div( pi/2, sin( mul(n,pi) ) ); 709 return mul( product, sub( besselI( neg(n), x ), besselI(n,x) ) ); 710 711 } 712 713 if ( x > useAsymptotic ) 714 return sqrt(pi/2/x) * exp(-x) * hypergeometric2F0( n+.5, .5-n, -.5/x ); 715 716 if ( x < 0 ) return besselK( n, complex(x) ); 717 718 // dlmf.nist.gov/10.27.5 719 if ( Number.isInteger(n) ) 720 return (-1)**(n+1)/2 * ( diff( n => besselI(n,x), n ) + diff( n => besselI(n,x), -n ) ); 721 722 return pi/2 * ( besselI(-n,x) - besselI(n,x) ) / sin(n*pi); 723 724} 725 726function hankel1( n, x, tolerance=1e-10 ) { 727 728 var useAsymptotic = 11; 729 730 // dlmf.nist.gov/10.17 731 if ( x.im > 0 && abs(x) > useAsymptotic ) { 732 733 var w = add( x, mul(n,-pi/2), -pi/4 ); 734 735 var s = complex(1); 736 var plusI = complex(0,1); 737 var p = complex(1); 738 var i = 1; 739 740 while ( Math.abs(p.re) > tolerance || Math.abs(p.im) > tolerance ) { 741 p = mul( p, plusI, sub( mul(4,n,n), (2*i-1)**2 ), 1/i, 1/8, inv(x) ); 742 s = add( s, p ); 743 i++; 744 } 745 746 return mul( div( Math.sqrt(2/pi), sqrt(x) ), exp( mul(plusI,w) ), s ); 747 748 } 749 750 // not duplicate special case 751 if ( isInteger(n) ) 752 return add( besselJ(n,x), mul( complex(0,1), besselY(n,x) ) ); 753 754 // one less call to besselJ 755 var s = sub( mul( besselJ(n,x), exp( mul(n,complex(0,-pi)) ) ), besselJ( neg(n), x ) ); 756 return div( mul( complex(0,1), s ), sin( mul(n,pi) ) ); 757 758} 759 760function hankel2( n, x, tolerance=1e-10 ) { 761 762 var useAsymptotic = 11; 763 764 // dlmf.nist.gov/10.17 765 if ( x.im < 0 && abs(x) > useAsymptotic ) { 766 767 var w = add( x, mul(n,-pi/2), -pi/4 ); 768 769 var s = complex(1); 770 var minusI = complex(0,-1); 771 var p = complex(1); 772 var i = 1; 773 774 while ( Math.abs(p.re) > tolerance || Math.abs(p.im) > tolerance ) { 775 p = mul( p, minusI, sub( mul(4,n,n), (2*i-1)**2 ), 1/i, 1/8, inv(x) ); 776 s = add( s, p ); 777 i++; 778 } 779 780 return mul( div( Math.sqrt(2/pi), sqrt(x) ), exp( mul(minusI,w) ), s ); 781 782 } 783 784 // not duplicate special case 785 if ( isInteger(n) ) 786 return sub( besselJ(n,x), mul( complex(0,1), besselY(n,x) ) ); 787 788 // one less call to besselJ 789 var s = sub( mul( besselJ(n,x), exp( mul(n,complex(0,pi)) ) ), besselJ( neg(n), x ) ); 790 return div( mul( complex(0,-1), s ), sin( mul(n,pi) ) ); 791 792} 793 794 795// dlmf.nist.gov/9.2.ii and dlmf.nist.gov/9.6.i 796 797function airyAi( x ) { 798 799 if ( isComplex(x) ) { 800 801 if ( isZero(x) ) return complex( 1 / 3**(2/3) / gamma(2/3) ); 802 803 if ( x.re < 0 ) { 804 805 var z = mul( 2/3, pow( neg(x), 3/2 ) ); 806 return mul( 1/3, sqrt(neg(x)), add( besselJ( 1/3, z ), besselJ( -1/3, z ) ) ); 807 808 } 809 810 var z = mul( 2/3, pow( x, 3/2 ) ); 811 return mul( 1/pi, sqrt( div( x, 3 ) ), besselK( 1/3, z ) ); 812 813 } 814 815 if ( x === 0 ) return 1 / 3**(2/3) / gamma(2/3); 816 817 if ( x < 0 ) { 818 819 var z = 2/3 * (-x)**(3/2); 820 return sqrt(-x) / 3 * ( besselJ( 1/3, z ) + besselJ( -1/3, z ) ); 821 822 } 823 824 var z = 2/3 * x**(3/2); 825 return 1/pi * sqrt(x/3) * besselK( 1/3, z ); 826 827} 828 829function airyAiPrime( x ) { 830 831 if ( isComplex(x) ) { 832 833 if ( isZero(x) ) return complex( -1 / 3**(1/3) / gamma(1/3) ); 834 835 if ( x.re < 0 ) { 836 837 var z = mul( 2/3, pow( neg(x), 3/2 ) ); 838 return mul( 1/3, x, sub( besselJ( -2/3, z ), besselJ( 2/3, z ) ) ); 839 840 } 841 842 var z = mul( 2/3, pow( x, 3/2 ) ); 843 return mul( -1/pi/sqrt(3), x, besselK( 2/3, z ) ); 844 845 } 846 847 if ( x === 0 ) return -1 / 3**(1/3) / gamma(1/3); 848 849 if ( x < 0 ) { 850 851 var z = 2/3 * (-x)**(3/2); 852 return x/3 * ( besselJ( -2/3, z ) - besselJ( 2/3, z ) ); 853 854 } 855 856 var z = 2/3 * x**(3/2); 857 return -1/pi/sqrt(3) * x * besselK( 2/3, z ); 858 859} 860 861function airyBi( x ) { 862 863 if ( isComplex(x) ) { 864 865 if ( isZero(x) ) return complex( 1 / 3**(1/6) / gamma(2/3) ); 866 867 if ( x.re < 0 ) { 868 869 var z = mul( 2/3, pow( neg(x), 3/2 ) ); 870 return mul( sqrt( div(neg(x),3) ), sub( besselJ( -1/3, z ), besselJ( 1/3, z ) ) ); 871 872 } 873 874 var z = mul( 2/3, pow( x, 3/2 ) ); 875 return mul( sqrt( div( x, 3 ) ), add( besselI( 1/3, z ), besselI( -1/3, z ) ) ); 876 877 } 878 879 if ( x === 0 ) return 1 / 3**(1/6) / gamma(2/3); 880 881 if ( x < 0 ) { 882 883 var z = 2/3 * (-x)**(3/2); 884 return sqrt(-x/3) * ( besselJ( -1/3, z ) - besselJ( 1/3, z ) ); 885 886 } 887 888 var z = 2/3 * x**(3/2);
889 return sqrt(x/3) * ( besselI( 1/3, z ) + besselI( -1/3, z ) ); 890 891} 892 893function airyBiPrime( x ) { 894 895 if ( isComplex(x) ) { 896 897 if ( isZero(x) ) return complex( 3**(1/6) / gamma(1/3) ); 898 899 if ( x.re < 0 ) { 900 901 var z = mul( 2/3, pow( neg(x), 3/2 ) ); 902 return mul( 1/sqrt(3), neg(x), add( besselJ( 2/3, z ), besselJ( -2/3, z ) ) ); 903 904 } 905 906 var z = mul( 2/3, pow( x, 3/2 ) ); 907 return mul( 1/sqrt(3), x, add( besselI( 2/3, z ), besselI( -2/3, z ) ) ); 908 909 } 910 911 if ( x === 0 ) return 3**(1/6) / gamma(1/3); 912 913 if ( x < 0 ) { 914 915 var z = 2/3 * (-x)**(3/2); 916 return -x/sqrt(3) * ( besselJ( 2/3, z ) + besselJ( -2/3, z ) ); 917 918 } 919 920 var z = 2/3 * x**(3/2); 921 return x/sqrt(3) * ( besselI( 2/3, z ) + besselI( -2/3, z ) ); 922 923} 924 925 926function sphericalBesselJ( n, x ) { 927 928 return mul( div( Math.sqrt(pi/2), sqrt(x) ), besselJ( add( n, .5 ), x ) ); 929 930} 931 932function sphericalBesselY( n, x ) { 933 934 return mul( div( Math.sqrt(pi/2), sqrt(x) ), besselY( add( n, .5 ), x ) ); 935 936} 937 938function sphericalHankel1( n, x ) { 939 940 return mul( div( Math.sqrt(pi/2), sqrt(x) ), hankel1( add( n, .5 ), x ) ); 941 942} 943 944function sphericalHankel2( n, x ) { 945 946 return mul( div( Math.sqrt(pi/2), sqrt(x) ), hankel2( add( n, .5 ), x ) ); 947 948} 949 950 951function struveH( n, x ) { 952 953 return mul( pow( x, add(n,1) ), inv( mul( pow(2,n), sqrt(pi), gamma( add(n,3/2) ) ) ), 954 hypergeometric1F2( 1, 3/2, add(n,3/2), mul( -1/4, pow(x,2) ) ) ); 955 956} 957 958function struveL( n, x ) { 959 960 // one sign different from struveH 961 962 return mul( pow( x, add(n,1) ), inv( mul( pow(2,n), sqrt(pi), gamma( add(n,3/2) ) ) ), 963 hypergeometric1F2( 1, 3/2, add(n,3/2), mul( 1/4, pow(x,2) ) ) ); 964 965} 966 967 968function jacobiTheta( n, x, q, tolerance=1e-10 ) { 969 970 if ( abs(q) >= 1 ) throw Error( 'Unsupported elliptic nome' ); 971 972 if ( ![1,2,3,4].includes(n) ) throw Error( 'Undefined Jacobi theta index' ); 973 974 if ( isComplex(x) || isComplex(q) ) { 975 976 if ( !isComplex(x) ) x = complex(x); 977 978 var piTau = div( log(q), complex(0,1) ); 979 980 // dlmf.nist.gov/20.2 to reduce overflow 981 if ( Math.abs(x.im) > Math.abs(piTau.im) || Math.abs(x.re) > Math.PI ) { 982 983 // use floor for consistency with fundamentalParallelogram 984 var pt = Math.floor( x.im / piTau.im ); 985 x = sub( x, mul( pt, piTau ) ); 986 987 var p = Math.floor( x.re / Math.PI ); 988 x = sub( x, p * Math.PI ); 989 990 var qFactor = pow( q, -pt*pt ); 991 var eFactor = exp( mul( -2 * pt, x, complex(0,1) ) ); 992 993 // factors can become huge, so chop spurious parts first 994 switch( n ) { 995 996 case 1: 997 998 return mul( (-1)**(p+pt), qFactor, eFactor, chop( jacobiTheta( n, x, q ), tolerance ) ); 999 1000 case 2: 1001 1002 return mul( (-1)**p, qFactor, eFactor, chop( jacobiTheta( n, x, q ), tolerance ) ); 1003 1004 case 3: 1005 1006 return mul( qFactor, eFactor, chop( jacobiTheta( n, x, q ), tolerance ) ); 1007 1008 case 4: 1009 1010 return mul( (-1)**pt, qFactor, eFactor, chop( jacobiTheta( n, x, q ), tolerance ) ); 1011 1012 } 1013 1014 } 1015 1016 switch( n ) { 1017 1018 case 1: 1019 1020 var s = complex(0); 1021 var p = complex(1); 1022 var i = 0; 1023 1024 while ( Math.abs(p.re) > tolerance || Math.abs(p.im) > tolerance ) { 1025 p = mul( (-1)**i, pow( q, i*i+i ), sin( mul(2*i+1,x) ) ); 1026 s = add( s, p ); 1027 i++; 1028 } 1029 1030 return mul( 2, pow( q, 1/4 ), s ); 1031 1032 case 2: 1033 1034 var s = complex(0); 1035 var p = complex(1); 1036 var i = 0; 1037 1038 while ( Math.abs(p.re) > tolerance || Math.abs(p.im) > tolerance ) { 1039 p = mul( pow( q, i*i+i ), cos( mul(2*i+1,x) ) ); 1040 s = add( s, p ); 1041 i++; 1042 } 1043 1044 return mul( 2, pow( q, 1/4 ), s ); 1045 1046 case 3: 1047 1048 var s = complex(0); 1049 var p = complex(1); 1050 var i = 1; 1051 1052 while ( Math.abs(p.re) > tolerance || Math.abs(p.im) > tolerance ) { 1053 p = mul( pow( q, i*i ), cos( mul(2*i,x) ) ); 1054 s = add( s, p ); 1055 i++; 1056 } 1057 1058 return add( 1, mul(2,s) ); 1059 1060 case 4: 1061 1062 var s = complex(0); 1063 var p = complex(1); 1064 var i = 1; 1065 1066 while ( Math.abs(p.re) > tolerance || Math.abs(p.im) > tolerance ) { 1067 p = mul( pow( neg(q), i*i ), cos( mul(2*i,x) ) ); 1068 s = add( s, p ); 1069 i++; 1070 } 1071 1072 return add( 1, mul(2,s) ); 1073 1074 } 1075 1076 } else { 1077 1078 switch( n ) { 1079 1080 case 1: 1081 1082 if ( q < 0 ) return jacobiTheta( n, x, complex(q) ); 1083 1084 var s = 0; 1085 var p = 1; 1086 var i = 0; 1087 1088 while ( Math.abs(p) > tolerance ) { 1089 p = (-1)**i * q**(i*i+i) * sin( (2*i+1) * x ); 1090 s += p; 1091 i++; 1092 } 1093 1094 return 2 * q**(1/4) * s; 1095 1096 case 2: 1097 1098 if ( q < 0 ) return jacobiTheta( n, x, complex(q) ); 1099 1100 var s = 0; 1101 var p = 1; 1102 var i = 0; 1103 1104 while ( Math.abs(p) > tolerance ) { 1105 p = q**(i*i+i) * cos( (2*i+1) * x ); 1106 s += p; 1107 i++; 1108 } 1109 1110 return 2 * q**(1/4) * s; 1111 1112 case 3: 1113 1114 var s = 0; 1115 var p = 1; 1116 var i = 1; 1117 1118 while ( Math.abs(p) > tolerance ) { 1119 p = q**(i*i) * cos( 2*i * x ); 1120 s += p; 1121 i++; 1122 } 1123 1124 return 1 + 2 * s; 1125 1126 case 4: 1127 1128 var s = 0; 1129 var p = 1; 1130 var i = 1; 1131 1132 while ( Math.abs(p) > tolerance ) { 1133 p = (-q)**(i*i) * cos( 2*i * x ); 1134 s += p; 1135 i++; 1136 } 1137 1138 return 1 + 2 * s; 1139 1140 } 1141 1142 } 1143 1144} 1145 1146 1147function ellipticNome( m ) { 1148 1149 if ( typeof m === 'undefined' ) throw Error( 'Elliptic parameter is undefined' ); 1150 1151 if ( isComplex(m) ) return exp( div( mul( -pi, ellipticK( sub(1,m) ) ), ellipticK(m) ) ); 1152 1153 if ( m > 1 ) return ellipticNome( complex(m) ); 1154 1155 if ( m < 0 ) return -exp( -pi * ellipticK( 1/(1-m) ) / ellipticK( m/(m-1) ) ); 1156 1157 return exp( -pi * ellipticK(1-m) / ellipticK(m) ); 1158 1159} 1160 1161function am( x, m ) { 1162 1163 if ( m > 1 || isComplex(x) || isComplex(m) ) { 1164 1165 if ( !isComplex(x) ) x = complex(x); 1166 if ( !isComplex(m) ) m = complex(m); 1167 1168 if ( m.im === 0 && m.re <= 1 ) { 1169 1170 var K = ellipticK( m.re ); 1171 var n = Math.round( x.re / 2 / K ); 1172 x = sub( x, 2 * n * K ); 1173 1174 if ( m.re < 0 ) { 1175 1176 var Kp = ellipticK( 1 - m.re ); 1177 var p = Math.round( x.im / 2 / Kp.re ); 1178 1179 // bitwise test for odd integer 1180 if ( p & 1 ) return sub( n * pi, arcsin( sn(x,m) ) ); 1181 1182 } 1183 1184 return add( arcsin( sn(x,m) ), n * pi ); 1185 1186 } 1187 1188 return arcsin( sn(x,m) ); 1189 1190 } else { 1191 1192 var K = ellipticK(m); 1193 var n = Math.round( x / 2 / K ); 1194 x = x - 2 * n * K; 1195 1196 return Math.asin( sn(x,m) ) + n * pi; 1197 1198 } 1199 1200} 1201 1202// Jacobi theta functions grow very quickly in imaginary direction 1203// evaluating elliptic functions without reduction fails accordingly 1204 1205function fundamentalParallelogram( x, p1, p2 ) { 1206 1207 // x = m p1 + n p2, solve for m, n 1208 1209 var det = p1.re * p2.im - p1.im * p2.re; 1210 1211 var m = ( x.re * p2.im - x.im * p2.re ) / det; 1212 var n = ( x.im * p1.re - x.re * p1.im ) / det; 1213 1214 return add( x, mul( -Math.floor(m), p1 ), mul( -Math.floor(n), p2 ) ); 1215 1216} 1217 1218function sn( x, m ) { 1219 1220 if ( m > 1 || isComplex(x) || isComplex(m) ) { 1221 1222 if ( !isComplex(m) ) m = complex(m); // ensure K complex 1223 1224 // dlmf.nist.gov/22.17 1225 if ( abs(m) > 1 ) return mul( inv(sqrt(m)), sn( mul(sqrt(m),x), inv(m) ) ); 1226 1227 // periods 4K, 2iK' 1228 var K = ellipticK(m), p1 = mul( 4, K ); 1229 var p2 = mul( complex(0,2), ellipticK( sub(1,m) ) ); 1230 1231 x = fundamentalParallelogram( x, p1, p2 ); 1232 1233 var q = ellipticNome(m); 1234 var t = div( x, mul( 2/pi, K ) ); // theta(3,0,q)**2 = 2K(m)/pi 1235 1236 return mul( div( jacobiTheta(3,0,q), jacobiTheta(2,0,q) ), 1237 div( jacobiTheta(1,t,q), jacobiTheta(4,t,q) ) ); 1238 1239 } 1240 1241 // dlmf.nist.gov/22.5.ii 1242 if ( m === 0 ) return sin(x);
1243 if ( m === 1 ) return tanh(x); 1244 1245 var q = ellipticNome(m); 1246 var t = x / jacobiTheta(3,0,q)**2; 1247 1248 if ( m < 0 ) 1249 return jacobiTheta(3,0,q) / jacobiTheta(4,t,q) 1250 * div( jacobiTheta(1,t,q), jacobiTheta(2,0,q) ).re; 1251 1252 return jacobiTheta(3,0,q) / jacobiTheta(2,0,q) 1253 * jacobiTheta(1,t,q) / jacobiTheta(4,t,q); 1254 1255} 1256 1257function cn( x, m ) { 1258 1259 if ( m > 1 || isComplex(x) || isComplex(m) ) { 1260 1261 if ( !isComplex(m) ) m = complex(m); // ensure K complex 1262 1263 // dlmf.nist.gov/22.17 1264 if ( abs(m) > 1 ) return dn( mul(sqrt(m),x), inv(m) ); 1265 1266 // periods 4K, 2K + 2iK' 1267 var K = ellipticK(m), p1 = mul( 4, K ); 1268 var p2 = add( div(p1,2), mul( complex(0,2), ellipticK( sub(1,m) ) ) ); 1269 1270 x = fundamentalParallelogram( x, p1, p2 ); 1271 1272 var q = ellipticNome(m); 1273 var t = div( x, mul( 2/pi, K ) ); // theta(3,0,q)**2 = 2K(m)/pi 1274 1275 return mul( div( jacobiTheta(4,0,q), jacobiTheta(2,0,q) ), 1276 div( jacobiTheta(2,t,q), jacobiTheta(4,t,q) ) ); 1277 1278 } 1279 1280 // dlmf.nist.gov/22.5.ii 1281 if ( m === 0 ) return cos(x); 1282 if ( m === 1 ) return sech(x); 1283 1284 var q = ellipticNome(m); 1285 var t = x / jacobiTheta(3,0,q)**2; 1286 1287 if ( m < 0 ) 1288 return jacobiTheta(4,0,q) / jacobiTheta(4,t,q) 1289 * div( jacobiTheta(2,t,q), jacobiTheta(2,0,q) ).re; 1290 1291 return jacobiTheta(4,0,q) / jacobiTheta(2,0,q) 1292 * jacobiTheta(2,t,q) / jacobiTheta(4,t,q); 1293 1294} 1295 1296function dn( x, m ) { 1297 1298 if ( m > 1 || isComplex(x) || isComplex(m) ) { 1299 1300 if ( !isComplex(m) ) m = complex(m); // ensure K complex 1301 1302 // dlmf.nist.gov/22.17 1303 if ( abs(m) > 1 ) return cn( mul(sqrt(m),x), inv(m) ); 1304 1305 // periods 2K, 4iK' 1306 var K = ellipticK(m), p1 = mul( 2, K ); 1307 var p2 = mul( complex(0,4), ellipticK( sub(1,m) ) ); 1308 1309 x = fundamentalParallelogram( x, p1, p2 ); 1310 1311 var q = ellipticNome(m); 1312 var t = div( x, mul( 2/pi, K ) ); // theta(3,0,q)**2 = 2K(m)/pi 1313 1314 return mul( div( jacobiTheta(4,0,q), jacobiTheta(3,0,q) ), 1315 div( jacobiTheta(3,t,q), jacobiTheta(4,t,q) ) ); 1316 1317 } 1318 1319 // dlmf.nist.gov/22.5.ii 1320 if ( m === 0 ) return 1; 1321 if ( m === 1 ) return sech(x); 1322 1323 var q = ellipticNome(m); 1324 var t = x / jacobiTheta(3,0,q)**2; 1325 1326 return jacobiTheta(4,0,q) / jacobiTheta(3,0,q) 1327 * jacobiTheta(3,t,q) / jacobiTheta(4,t,q); 1328 1329} 1330 1331function ns( x, m ) { return inv( sn(x,m) ); } 1332 1333function nc( x, m ) { return inv( cn(x,m) ); } 1334 1335function nd( x, m ) { return inv( dn(x,m) ); } 1336 1337function sc( x, m ) { return div( sn(x,m), cn(x,m) ); } 1338 1339function cs( x, m ) { return div( cn(x,m), sn(x,m) ); } 1340 1341function sd( x, m ) { return div( sn(x,m), dn(x,m) ); } 1342 1343function ds( x, m ) { return div( dn(x,m), sn(x,m) ); } 1344 1345function cd( x, m ) { return div( cn(x,m), dn(x,m) ); } 1346 1347function dc( x, m ) { return div( dn(x,m), cn(x,m) ); } 1348 1349function lemniscateSin( x ) { return sn(x,-1); } 1350 1351function lemniscateCos( x ) { return dn(x,2); } 1352 1353 1354function weierstrassRoots( g2, g3 ) { 1355 1356 function cubicTrigSolution( p, q, n ) { 1357 1358 // p, q both negative in defining cubic 1359 1360 if ( isZero(p) ) return mul( root(q,3), exp( complex(0,2*pi*n/3) ) ) 1361 1362 return mul( 2/sqrt(3), sqrt(p), 1363 cos( sub( div( arccos( mul( 3*sqrt(3)/2, q, pow(p,-3/2) ) ), 3 ), 1364 2*pi*n/3 ) ) ); 1365 } 1366 1367 g2 = div( g2, 4 ); 1368 g3 = div( g3, 4 ); 1369 1370 var e1 = cubicTrigSolution( g2, g3, 0 ); 1371 var e2 = cubicTrigSolution( g2, g3, 1 ); 1372 var e3 = cubicTrigSolution( g2, g3, 2 ); 1373 1374 return [ e1, e2, e3 ]; 1375 1376} 1377 1378function weierstrassHalfPeriods( g2, g3 ) { 1379 1380 // Davis, Intro to Nonlinear Diff. & Integral Eqs., pp.157-8 1381 // consistent with periods of Jacobi sine in weierstrassP 1382 // not consistent with Mathematica 1383 1384 var [ e1, e2, e3 ] = weierstrassRoots( g2, g3 ); 1385 1386 var lambda = sqrt( sub(e1,e3) ); 1387 var m = div( sub(e2,e3), sub(e1,e3) ); 1388 1389 var w1 = div( ellipticK(m), lambda ); 1390 var w2 = div( mul( complex(0,1), ellipticK( sub(1,m) ) ), lambda ); 1391 1392 return [ w1, w2 ]; 1393 1394} 1395 1396function weierstrassInvariants( w1, w2 ) { 1397 1398 if ( !isComplex(w1) ) w1 = complex(w1); 1399 if ( !isComplex(w2) ) w2 = complex(w2); 1400
1401 // order half periods by complex slope 1402 if ( w2.im/w2.re < w1.im/w1.re ) [ w1, w2 ] = [ w2, w1 ]; 1403 1404 var ratio = div( w2, w1 ), conjugate; 1405 1406 if ( ratio.im < 0 ) { 1407 ratio.im = -ratio.im; 1408 conjugate = true; 1409 } 1410 1411 var q = exp( mul( complex(0,1), pi, ratio ) ); 1412 1413 // en.wikipedia.org/wiki/Weierstrass's_elliptic_functions 1414 // modified for input of half periods 1415 1416 var a = jacobiTheta( 2, 0, q ); 1417 var b = jacobiTheta( 3, 0, q ); 1418 1419 var g2 = mul( 4/3*pi**4, pow( mul(2,w1), -4 ), 1420 add( pow(a,8), mul( -1, pow(a,4), pow(b,4) ), pow(b,8) ) ); 1421 1422 var g3 = mul( 8/27*pi**6, pow( mul(2,w1), -6 ), 1423 add( pow(a,12), mul( -3/2, pow(a,8), pow(b,4) ), 1424 mul( -3/2, pow(a,4), pow(b,8) ), pow(b,12) ) ); 1425 1426 if ( conjugate ) { 1427 g2.im = -g2.im; 1428 g3.im = -g3.im; 1429 } 1430 1431 return [ g2, g3 ]; 1432 1433} 1434 1435 1436function weierstrassP( x, g2, g3 ) { 1437 1438 if ( !isComplex(x) ) x = complex(x); 1439 1440 if ( isZero(g2) && isZero(g3) ) return pow( x, -2 ); 1441 1442 var [ e1, e2, e3 ] = weierstrassRoots( g2, g3 ); 1443 1444 // Whittaker & Watson, Section 22.351 1445 1446 var m = div( sub(e2,e3), sub(e1,e3) ); 1447 1448 return add( e3, mul( sub(e1,e3), pow( sn( mul( x, sqrt(sub(e1,e3)) ), m ), -2 ) ) ); 1449 1450} 1451 1452function weierstrassPPrime( x, g2, g3 ) { 1453 1454 if ( !isComplex(x) ) x = complex(x); 1455 1456 if ( isZero(g2) && isZero(g3) ) return mul( -2, pow( x, -3 ) ); 1457 1458 var [ e1, e2, e3 ] = weierstrassRoots( g2, g3 ); 1459 1460 // Whittaker & Watson, Section 22.351 1461 1462 var m = div( sub(e2,e3), sub(e1,e3) ); 1463 1464 var argument = mul( x, sqrt(sub(e1,e3)) ); 1465 1466 return mul( -2, pow( sub(e1,e3), 3/2 ), cn( argument, m ), dn( argument, m ), 1467 pow( sn( argument, m ), -3 ) ); 1468 1469} 1470 1471function inverseWeierstrassP( x, g2, g3 ) { 1472 1473 if ( !isComplex(x) ) x = complex(x); 1474 1475 var [ e1, e2, e3 ] = weierstrassRoots( g2, g3 ); 1476 1477 // Johansson arxiv.org/pdf/1806.06725.pdf p.17 1478 // sign of imaginary part on real axis differs from Mathematica 1479 1480 return carlsonRF( sub(x,e1), sub(x,e2), sub(x,e3) ); 1481 1482} 1483 1484 1485function kleinJ( x ) { 1486 1487 // from mpmath / elliptic.py 1488 1489 var q = exp( mul( complex(0,pi), x ) ); 1490 var t2 = chop( jacobiTheta(2,0,q) ); 1491 var t3 = chop( jacobiTheta(3,0,q) ); 1492 var t4 = chop( jacobiTheta(4,0,q) ); 1493 var P = pow( add( pow(t2,8), pow(t3,8), pow(t4,8) ), 3 ); 1494 var Q = mul( 54, pow( mul(t2,t3,t4), 8 ) ); 1495 1496 return div( P, Q ); 1497 1498} 1499 1500 1501// Carlson symmetric integrals 1502 1503function carlsonRC( x, y ) { 1504 1505 if ( x < 0 || y < 0 || isComplex(x) || isComplex(y) ) { 1506 1507 if ( !isComplex(x) ) x = complex(x); 1508 if ( !isComplex(y) ) y = complex(y); 1509 1510 if ( x.re === y.re && x.im === y.im ) return inv( sqrt(x) ); 1511 1512 // return value by continuity 1513 1514 return div( arccos( div( sqrt(x), sqrt(y) ) ), mul( sqrt(y), sqrt( sub( 1, div(x,y) ) ) ) ); 1515 1516 } 1517 1518 if ( x === y ) return 1 / Math.sqrt(x); 1519 1520 if ( x < y ) return Math.acos( Math.sqrt(x/y) ) / Math.sqrt(y-x); 1521 1522 return Math.acosh( Math.sqrt(x/y) ) / Math.sqrt(x-y); 1523 1524} 1525 1526function carlsonRD( x, y, z ) { 1527 1528 return carlsonRJ( x, y, z, z ); 1529 1530} 1531 1532function carlsonRF( x, y, z, tolerance=1e-10 ) { 1533 1534 if ( isComplex(x) || isComplex(y) || isComplex(z) ) { 1535 1536 var xm = x; 1537 var ym = y; 1538 var zm = z; 1539 1540 var Am = A0 = div( add( x, y, z ), 3 ); 1541 var Q = Math.pow( 3*tolerance, -1/6 ) 1542 * Math.max( abs( sub(A0,x) ), abs( sub(A0,y) ), abs( sub(A0,z) ) ); 1543 var g = .25; 1544 var pow4 = 1; 1545 1546 while ( true ) { 1547 var xs = sqrt(xm); 1548 var ys = sqrt(ym); 1549 var zs = sqrt(zm); 1550 var lm = add( mul(xs,ys), mul(xs,zs), mul(ys,zs) ); 1551 var Am1 = mul( add(Am,lm), g ); 1552 xm = mul( add(xm,lm), g ); 1553 ym = mul( add(ym,lm), g ); 1554 zm = mul( add(zm,lm), g ); 1555 if ( pow4 * Q < abs(Am) ) break; 1556 Am = Am1; 1557 pow4 *= g; 1558 } 1559 1560 var t = div( pow4, Am ); 1561 var X = mul( sub(A0,x), t ); 1562 var Y = mul( sub(A0,y), t ); 1563 var Z = neg( add(X,Y) ); 1564 var E2 = sub( mul(X,Y), mul(Z,Z) ); 1565 var E3 = mul(X,Y,Z); 1566 1567 return mul( pow( Am, -.5 ), 1568 add( 9240, mul(-924,E2), mul(385,E2,E2), mul(660,E3), mul(-630,E2,E3) ), 1/9240 ); 1569 1570 } else { 1571 1572 if ( y === z ) return carlsonRC( x, y ); 1573 if ( x === z ) return carlsonRC( y, x ); 1574 if ( x === y ) return carlsonRC( z, x ); 1575 1576 if ( x < 0 || y < 0 || z < 0 ) return carlsonRF( complex(x), y, z ); 1577 1578 // adapted from mpmath / elliptic.py 1579 1580 var xm = x; 1581 var ym = y; 1582 var zm = z; 1583 1584 var Am = A0 = (x + y + z) / 3; 1585 var Q = Math.pow( 3*tolerance, -1/6 ) 1586 * Math.max( Math.abs(A0-x), Math.abs(A0-y), Math.abs(A0-z) ); 1587 var g = .25; 1588 var pow4 = 1; 1589 1590 while ( true ) { 1591 var xs = Math.sqrt(xm); 1592 var ys = Math.sqrt(ym); 1593 var zs = Math.sqrt(zm); 1594 var lm = xs*ys + xs*zs + ys*zs; 1595 var Am1 = (Am + lm) * g; 1596 xm = (xm + lm) * g; 1597 ym = (ym + lm) * g; 1598 zm = (zm + lm) * g; 1599 if ( pow4 * Q < Math.abs(Am) ) break; 1600 Am = Am1; 1601 pow4 *= g; 1602 } 1603 1604 var t = pow4 / Am; 1605 var X = (A0-x) * t; 1606 var Y = (A0-y) * t; 1607 var Z = -X-Y; 1608 var E2 = X*Y - Z**2; 1609 var E3 = X*Y*Z; 1610 1611 return Math.pow( Am, -.5 ) 1612 * ( 9240 - 924*E2 + 385*E2**2 + 660*E3 - 630*E2*E3 ) / 9240; 1613 1614 } 1615 1616} 1617 1618function carlsonRG( x, y, z ) { 1619 1620 var t1 = mul( z, carlsonRF(x,y,z) ); 1621 var t2 = mul( -1/3, sub(x,z), sub(y,z), carlsonRD(x,y,z) ); 1622 var t3 = sqrt( mul( x, y, inv(z) ) ); 1623 1624 return mul( .5, add( t1, t2, t3 ) ); 1625 1626} 1627 1628function carlsonRJ( x, y, z, p, tolerance=1e-10 ) { 1629 1630 if ( isComplex(x) || isComplex(y) || isComplex(z) || isComplex(p) ) { 1631 1632 var xm = x; 1633 var ym = y; 1634 var zm = z; 1635 var pm = p; 1636 1637 var A0 = Am = div( add( x, y, z, mul(2,p) ), 5 ); 1638 var delta = mul( sub(p,x), sub(p,y), sub(p,z) ); 1639 var Q = Math.pow( .25*tolerance, -1/6 ) 1640 * Math.max( abs( sub(A0,x) ), abs( sub(A0,y) ), abs( sub(A0,z) ), abs( sub(A0,p) ) ); 1641 var g = .25; 1642 var pow4 = 1; 1643 var S = complex(0); 1644 1645 while ( true ) { 1646 var sx = sqrt(xm); 1647 var sy = sqrt(ym); 1648 var sz = sqrt(zm); 1649 var sp = sqrt(pm); 1650 var lm = add( mul(sx,sy), mul(sx,sz), mul(sy,sz) ); 1651 var Am1 = mul( add(Am,lm), g ); 1652 xm = mul( add(xm,lm), g ); 1653 ym = mul( add(ym,lm), g ); 1654 zm = mul( add(zm,lm), g ); 1655 pm = mul( add(pm,lm), g ); 1656 var dm = mul( add(sp,sx), add(sp,sy), add(sp,sz) ); 1657 var em = mul( delta, pow4**3, inv(dm), inv(dm) ); 1658 if ( pow4 * Q < abs(Am) ) break; 1659 var T = mul( carlsonRC( 1, add(1,em) ), pow4, inv(dm) ); 1660 S = add( S, T ); 1661 pow4 *= g; 1662 Am = Am1; 1663 } 1664 1665 var t = div( pow4, Am ); 1666 var X = mul( sub(A0,x), t ); 1667 var Y = mul( sub(A0,y), t ); 1668 var Z = mul( sub(A0,z), t ); 1669 var P = div( add(X,Y,Z), -2 ); 1670 var E2 = add( mul(X,Y), mul(X,Z), mul(Y,Z), mul(-3,P,P) ); 1671 var E3 = add( mul(X,Y,Z), mul(2,E2,P), mul(4,P,P,P) ); 1672 var E4 = mul( add( mul(2,X,Y,Z), mul(E2,P), mul(3,P,P,P) ), P ); 1673 var E5 = mul(X,Y,Z,P,P); 1674 P = add( 24024, mul(-5148,E2), mul(2457,E2,E2), mul(4004,E3), mul(-4158,E2,E3), mul(-3276,E4), mul(2772,E5) ); 1675 var v1 = mul( pow4, pow( Am, -1.5 ), P, 1/24024 ); 1676 var v2 = mul(6,S); 1677 1678 return add( v1, v2 ); 1679 1680 } else { 1681 1682 if ( x < 0 || y < 0 || z < 0 || p < 0 ) return carlsonRJ( complex(x), y, z, p ); 1683 1684 // adapted from mpmath / elliptic.py 1685 1686 var xm = x; 1687 var ym = y; 1688 var zm = z; 1689 var pm = p; 1690
1691 var A0 = Am = (x + y + z + 2*p) / 5; 1692 var delta = (p-x) * (p-y) * (p-z); 1693 var Q = Math.pow( .25*tolerance, -1/6 ) 1694 * Math.max( Math.abs(A0-x), Math.abs(A0-y), Math.abs(A0-z), Math.abs(A0-p) ); 1695 var g = .25; 1696 var pow4 = 1; 1697 var S = 0; 1698 1699 while ( true ) { 1700 var sx = Math.sqrt(xm); 1701 var sy = Math.sqrt(ym); 1702 var sz = Math.sqrt(zm); 1703 var sp = Math.sqrt(pm); 1704 var lm = sx*sy + sx*sz + sy*sz; 1705 var Am1 = (Am + lm) * g; 1706 xm = (xm + lm) * g; 1707 ym = (ym + lm) * g; 1708 zm = (zm + lm) * g; 1709 pm = (pm + lm) * g; 1710 var dm = (sp+sx) * (sp+sy) * (sp+sz); 1711 var em = delta * pow4**3 / dm**2; 1712 if ( pow4 * Q < Math.abs(Am) ) break; 1713 var T = carlsonRC( 1, 1 + em ) * pow4 / dm; 1714 S += T; 1715 pow4 *= g; 1716 Am = Am1; 1717 } 1718 1719 var t = pow4 / Am; 1720 var X = (A0-x) * t; 1721 var Y = (A0-y) * t; 1722 var Z = (A0-z) * t; 1723 var P = (-X-Y-Z) / 2; 1724 var E2 = X*Y + X*Z + Y*Z - 3*P**2; 1725 var E3 = X*Y*Z + 2*E2*P + 4*P**3; 1726 var E4 = ( 2*X*Y*Z + E2*P + 3*P**3 ) * P; 1727 var E5 = X*Y*Z*P**2; 1728 P = 24024 - 5148*E2 + 2457*E2**2 + 4004*E3 - 4158*E2*E3 - 3276*E4 + 2772*E5; 1729 var v1 = pow4 * Math.pow( Am, -1.5 ) * P / 24024; 1730 var v2 = 6*S; 1731 1732 return v1 + v2; 1733 1734 } 1735 1736} 1737 1738 1739// elliptic integrals 1740 1741function ellipticF( x, m ) { 1742 1743 if ( arguments.length === 1 ) { 1744 m = x; 1745 x = pi/2; 1746 } 1747 1748 if ( isComplex(x) || isComplex(m) ) { 1749 1750 if ( !isComplex(x) ) x = complex(x); 1751 1752 var period = complex(0); 1753 if ( Math.abs(x.re) > pi/2 ) { 1754 var p = Math.round( x.re / pi ); 1755 x.re = x.re - p * pi; 1756 period = mul( 2 * p, ellipticK( m ) ); 1757 } 1758 1759 return add( mul( sin(x), carlsonRF( mul(cos(x),cos(x)), sub( 1, mul(m,sin(x),sin(x)) ), 1 ) ), period ); 1760 1761 } else { 1762 1763 if ( m > 1 && Math.abs(x) > Math.asin( 1 / Math.sqrt(m) ) ) return ellipticF( complex(x), m ); 1764 1765 var period = 0; 1766 if ( Math.abs(x) > pi/2 ) { 1767 var p = Math.round( x / pi ); 1768 x = x - p * pi; 1769 period = 2 * p * ellipticK( m ); 1770 } 1771 1772 return sin(x) * carlsonRF( cos(x)**2, 1 - m * sin(x)**2, 1 ) + period; 1773 1774 } 1775 1776} 1777 1778function ellipticK( m ) { 1779 1780 if ( typeof m === 'undefined' ) throw Error( 'Elliptic parameter is undefined' ); 1781 1782 return ellipticF( m ); 1783 1784} 1785 1786function ellipticE( x, m ) { 1787 1788 if ( arguments.length === 1 ) { 1789 m = x; 1790 x = pi/2; 1791 } 1792 1793 if ( isComplex(x) || isComplex(m) ) { 1794 1795 if ( !isComplex(x) ) x = complex(x); 1796 1797 var period = complex(0); 1798 if ( Math.abs(x.re) > pi/2 ) { 1799 var p = Math.round( x.re / pi ); 1800 x.re = x.re - p * pi; 1801 period = mul( 2 * p, ellipticE( m ) ); 1802 } 1803 1804 return add( mul( sin(x), carlsonRF( mul(cos(x),cos(x)), sub( 1, mul(m,sin(x),sin(x)) ), 1 ) ), 1805 mul( -1/3, m, pow(sin(x),3), carlsonRD( mul(cos(x),cos(x)), sub( 1, mul(m,sin(x),sin(x)) ), 1 ) ), 1806 period ); 1807 1808 } else { 1809 1810 if ( m > 1 && Math.abs(x) > Math.asin( 1 / Math.sqrt(m) ) ) return ellipticE( complex(x), m ); 1811 1812 var period = 0; 1813 if ( Math.abs(x) > pi/2 ) { 1814 var p = Math.round( x / pi ); 1815 x = x - p * pi; 1816 period = 2 * p * ellipticE( m ); 1817 } 1818 1819 return sin(x) * carlsonRF( cos(x)**2, 1 - m * sin(x)**2, 1 ) 1820 - m / 3 * sin(x)**3 * carlsonRD( cos(x)**2, 1 - m * sin(x)**2, 1 ) 1821 + period; 1822 1823 } 1824 1825} 1826 1827function ellipticPi( n, x, m ) { 1828 1829 if ( arguments.length === 2 ) { 1830 m = x; 1831 x = pi/2; 1832 } 1833 1834 // x outside period and abs(n)>1 agrees with mpmath, differs from Mathematica 1835 1836 if ( isComplex(n) || isComplex(x) || isComplex(m) ) { 1837 1838 if ( !isComplex(x) ) x = complex(x); 1839 1840 var period = complex(0); 1841 if ( Math.abs(x.re) > pi/2 ) { 1842 var p = Math.round( x.re / pi ); 1843 x.re = x.re - p * pi; 1844 period = mul( 2 * p, ellipticPi( n, m ) ); 1845 } 1846 1847 return add( mul( sin(x), carlsonRF( mul(cos(x),cos(x)), sub( 1, mul(m,sin(x),sin(x)) ), 1 ) ), 1848 mul( 1/3, n, pow(sin(x),3), 1849 carlsonRJ( mul(cos(x),cos(x)), sub( 1, mul(m,sin(x),sin(x)) ), 1, 1850 sub( 1, mul(n,sin(x),sin(x)) ) ) ), 1851 period ); 1852 1853 } else { 1854 1855 if ( n > 1 && Math.abs(x) > Math.asin( 1 / Math.sqrt(n) ) ) return ellipticPi( n, complex(x), m ); 1856 1857 if ( m > 1 && Math.abs(x) > Math.asin( 1 / Math.sqrt(m) ) ) return ellipticPi( n, complex(x), m ); 1858 1859 var period = 0; 1860 if ( Math.abs(x) > pi/2 ) { 1861 var p = Math.round( x / pi ); 1862 x = x - p * pi; 1863 period = 2 * p * ellipticPi( n, m ); 1864 } 1865 1866 return sin(x) * carlsonRF( cos(x)**2, 1 - m * sin(x)**2, 1 ) 1867 + n / 3 * sin(x)**3 1868 * carlsonRJ( cos(x)**2, 1 - m * sin(x)**2, 1, 1 - n * sin(x)**2 ) 1869 + period; 1870 1871 } 1872 1873} 1874 1875 1876function jacobiZeta( x, m ) { 1877 1878 // using definition matching elliptic integrals 1879 // alternate definition replaces x with am(x,m) 1880 1881 return sub( ellipticE( x, m ), mul( ellipticF(x,m), ellipticE(m), inv( ellipticK(m) ) ) ); 1882 1883} 1884 1885 1886function factorial( n ) { 1887 1888 if ( isComplex(n) ) { 1889 1890 if ( isPositiveIntegerOrZero(n) ) return complex( factorial(n.re) ); 1891 1892 return gamma( add(n,1) ); 1893 1894 } 1895 1896 if ( isPositiveIntegerOrZero(n) ) { 1897 1898 if ( factorialCache[n] ) return factorialCache[n]; 1899 1900 var last = factorialCache.length - 1; 1901 var result = factorialCache[last]; 1902 1903 for ( var i = last + 1 ; i <= n ; i++ ) { 1904 result *= i; 1905 factorialCache[i] = result; 1906 } 1907 1908 return result; 1909 1910 } 1911 1912 return gamma( n+1 ); 1913 1914} 1915 1916function factorial2( n ) { 1917 1918 if ( isComplex(n) ) { 1919 1920 if ( isPositiveIntegerOrZero(n) ) return complex( factorial2(n.re) ); 1921 1922 var p1 = pow( 2, div(n,2) ); 1923 var p2 = pow( pi/2, div( sub( cos(mul(pi,n)), 1 ), 4 ) ); 1924 var p3 = gamma( add( div(n,2) , 1 ) ); 1925 1926 return mul( p1, p2, p3 ); 1927 1928 } 1929 1930 if ( n === 0 ) return 1; 1931 1932 if ( isPositiveInteger(n) ) { 1933 1934 // bitwise test for odd integer, upward recursion for possible caching 1935 var result = n & 1 ? 1 : 2; 1936 for ( var i = result + 2 ; i <= n ; i += 2 ) result *= i; 1937 return result; 1938 1939 } 1940 1941 // github.com/sympy/sympy/pull/10362#issuecomment-170344298 1942 1943 return 2**(n/2) * (pi/2)**((cos(pi*n)-1)/4) * gamma( n/2+1 ); 1944 1945} 1946 1947function binomial( n, m ) { 1948 1949 if ( isComplex(n) || isComplex(m) ) 1950 return div( factorial(n), mul( factorial( sub(n,m) ), factorial(m) ) ); 1951 1952 if ( Number.isInteger(m) && m < 0 && n >= 0 ) return 0; 1953 1954 if ( Number.isInteger(n) && Number.isInteger(m) && n >= 0 && m > n ) return 0; 1955 1956 return factorial(n) / factorial(n-m) / factorial(m); 1957 1958} 1959 1960function multinomial( n ) { 1961 1962 if ( arguments.length === 1 ) return isComplex(n) ? complex(1) : 1; 1963 1964 var s = 0, p = 1, nNeg = 0; 1965 1966 for ( var i = 0 ; i < arguments.length ; i++ ) { 1967 1968 var ni = arguments[i]; 1969 s = add( s, ni ); 1970 1971 // catch negative integers for cancellation in ratio 1972 // reduce all to factorial(-1) divided by factor 1973 // extra overhead with pow() keeps complex in complex out 1974 1975 if ( isNegativeInteger( ni ) ) { 1976 var k = sub( -1, ni ); 1977 p = div( p, mul( chop(pow(-1,k)), factorial(k) ) ); 1978 nNeg++; 1979 } else 1980 // standard factorial 1981 p = mul( p, factorial( arguments[i] ) ); 1982 1983 } 1984 1985 if ( isNegativeInteger(s) ) { 1986 nNeg--; 1987 if ( nNeg > 0 ) return isComplex(s) ? complex(0) : 0; 1988 var k = sub( -1, s ); 1989 return inv( mul( p, chop(pow(-1,k)), factorial(k) ) ); 1990 } 1991 1992 if ( nNeg > 0 ) return isComplex(s) ? complex(0) : 0; 1993 1994 return div( factorial(s), p ); 1995 1996} 1997 1998function pochhammer( x, n ) { 1999 2000 var one = isArbitrary(x) ? arb1 : 1; 2001 2002 if ( isZero(n) ) { 2003 if ( isComplex(x) || isComplex(n) ) return complex(one); 2004 return one; 2005 } 2006 2007 if ( isComplex(n) ) { 2008 2009 if ( isPositiveInteger(n) ) { 2010 var result = pochhammer( x, n.re ); 2011 if ( isComplex(result) ) return result; 2012 return complex(result); 2013 } 2014 2015 if ( isArbitrary(x) && !isArbitrary(n) ) n = arbitrary(n); 2016 2017 return div( gamma( add(x,n) ), gamma(x) ); 2018 2019 } 2020 2021 if ( isPositiveInteger(n) ) { 2022 2023 var result = x, current = one, count = 1; 2024 2025 while ( count < n ) { 2026 result = mul( result, add( x, current ) ); 2027 current = add( current, one ); 2028 count++; 2029 } 2030 2031 return result; 2032 2033 } 2034 2035 if ( isArbitrary(x) && !isArbitrary(n) ) n = arbitrary(n); 2036 2037 return div( gamma( add(x,n) ), gamma(x) ); 2038 2039} 2040 2041function subfactorial( n ) { 2042 2043 var result = div( gamma( add(n,1), -1 ), exp(1) ); 2044 2045 if ( isPositiveInteger(n) ) return round(result); 2046 2047 return result; 2048 2049} 2050 2051 2052function logGamma( x ) { 2053 2054 if ( isArbitrary(x) ) { 2055 2056 var useAsymptotic = 10n * arb1; 2057 2058 if ( isNegativeIntegerOrZero( arbitrary(x) ) ) throw Error( 'Gamma function pole' ); 2059 2060 if ( abs(x) < useAsymptotic ) { 2061 2062 // slightly faster to get smaller integer near transition circle 2063 var xRe = isComplex(x) ? x.re : x; 2064 var n = Math.ceil( arbitrary(useAsymptotic) - arbitrary(xRe) ); 2065
2066 // ln(pochhammer(x,n)) 2067 var lnPochhammer = ln(x), current = arb1, count = 1; 2068 2069 while ( count < n ) { 2070 lnPochhammer = add( lnPochhammer, ln( add( x, current ) ) ); 2071 current = add( current, arb1 ); 2072 count++; 2073 } 2074 2075 return sub( logGamma(add(x,arbitrary(n))), lnPochhammer ); 2076 2077 } 2078 2079 if ( x < 0n ) x = complex(x); 2080 2081 // reflection formula with non-Hare correction to imaginary part 2082 if ( x.re < 0n ) { 2083 2084 // expand sine as exponentials for more accurate result 2085 var t, e, p, k = arb1; 2086 2087 if ( abs(x.im) === 0 ) { 2088 2089 t = neg( ln( sin( mul( x, onePi ) ) ) ); 2090 2091 } else if ( x.im < 0n ) { 2092 2093 e = exp( mul( x, complex(0n,-twoPi) ) ); 2094 p = complex( e.re, e.im ); 2095 t = complex( e.re, e.im ); 2096 while ( p.re !== 0n || p.im !== 0n ) { 2097 k += arb1; 2098 p = mul( p, e ); 2099 t = add( t, div( p, k ) ); 2100 } 2101 t = add( t, mul( x, complex(0n,-onePi) ), ln(arb2) ); 2102 2103 } else { 2104 2105 e = exp( mul( x, complex(0n,twoPi) ) ); 2106 p = complex( e.re, e.im ); 2107 t = complex( e.re, e.im ); 2108 while ( p.re !== 0n || p.im !== 0n ) { 2109 k += arb1; 2110 p = mul( p, e ); 2111 t = add( t, div( p, k ) ); 2112 } 2113 t = add( t, mul( x, complex(0n,onePi) ), complex(0n,-onePi), ln(arb2) ); 2114 2115 } 2116 2117 return add( t, ln(onePi), neg( logGamma( sub(arb1,x) ) ), complex(0n,halfPi) ); 2118 2119 } 2120
2121 // Johansson arxiv.org/abs/2109.08392 2122 2123 var k = 1, K = arb1, y, p = 1n, s = 0n; 2124 2125 if ( isComplex(x) ) { 2126 function compare() { return p.re !== 0n || p.im !== 0n; }; 2127 y = complex( x.re, x.im ); 2128 } else { 2129 function compare() { return p !== 0n; }; 2130 y = x; 2131 } 2132 2133 while ( compare() ) { 2134 2135 if ( k === bernoulli2nN.length ) { 2136 console.log( 'Not enough Bernoulli numbers for logGamma' ); 2137 break; 2138 } 2139 2140 p = div( div( bernoulli2nN[k], bernoulli2nD[k] ), 2141 mul( K+arb1, K, y ) ); 2142 s = add( s, p ); 2143 2144 y = mul( y, x, x ); 2145 K += arb2; 2146 k++; 2147 2148 } 2149 2150 return add( mul( sub(x,arb1/2n), ln(x) ), neg(x), div(ln(twoPi),arb2), s ); 2151 2152 } 2153 2154 // log of gamma less likely to overflow than gamma 2155 // Lanczos approximation as evaluated by Paul Godfrey 2156 2157 var c = [ 57.1562356658629235, -59.5979603554754912, 14.1360979747417471, 2158 -.491913816097620199, .339946499848118887e-4, .465236289270485756e-4, 2159 -.983744753048795646e-4, .158088703224912494e-3, -.210264441724104883e-3, 2160 .217439618115212643e-3, -.164318106536763890e-3, .844182239838527433e-4, 2161 -.261908384015814087e-4, .368991826595316234e-5 ]; 2162 2163 if ( isNegativeIntegerOrZero(x) ) throw Error( 'Gamma function pole' ); 2164 2165 if ( isComplex(x) ) { 2166 2167 // reflection formula with modified Hare correction to imaginary part 2168 if ( x.re < 0 ) { 2169 2170 var logRatio = log( div( pi, sin( mul(pi,x) ) ) ); 2171 // rounding errors can lead to wrong side of branch point 2172 if ( isNegativeIntegerOrZero( ( x.re + .5 ) / 2 ) ) 2173 logRatio.im = pi * ( x.im > 0 ? 1 : -1 ); // avoid Math.sign(0) = 0 2174 2175 var t = sub( logRatio, logGamma( sub(1,x) ) ); 2176 var s = x.im < 0 ? -1 : 1; 2177 var d = x.im === 0 ? 1/4 : 0; 2178 var k = Math.ceil( x.re/2 - 3/4 + d ); 2179 2180 return add( t, complex( 0, 2*s*k*pi ) ); 2181 2182 } 2183 2184 var t = add( x, 5.24218750000000000 ); 2185 t = sub( mul( add( x, .5 ), log(t)), t ); 2186 var s = .999999999999997092; 2187 for ( var j = 0 ; j < 14 ; j++ ) s = add( s, div( c[j], add( x, j+1 ) ) ); 2188 var u = add( t, log( mul( 2.5066282746310005, div( s, x ) ) ) ); 2189 2190 // adjustment to keep imaginary part on same sheet 2191 if ( s.re < 0 ) { 2192 if( x.im < 0 && div(s,x).im < 0 ) u = add( u, complex(0,2*pi) ); 2193 if( x.im > 0 && div(s,x).im > 0 ) u = add( u, complex(0,-2*pi) ); 2194 } 2195 2196 return u; 2197 2198 } else { 2199 2200 if ( x < 0 ) return logGamma( complex(x) ); 2201 2202 var t = x + 5.24218750000000000; 2203 t = ( x + .5 ) * log(t) - t; 2204 var s = .999999999999997092; 2205 for ( var j = 0 ; j < 14 ; j++ ) s += c[j] / (x+j+1); 2206 return t + log( 2.5066282746310005 * s / x ); 2207 2208 } 2209 2210} 2211 2212function gamma( x, y, z ) { 2213 2214 if ( arguments.length === 2 ) { 2215 2216 if ( isZero(x) ) { 2217 2218 if ( isZero(y) ) throw Error( 'Gamma function pole' ); 2219 2220 var result = neg( expIntegralEi( neg(y), true ) ); 2221 2222 // complex on negative real axis 2223 if ( y < 0 || y.re < 0 && y.im === 0 ) result = sub( result, complex(0,pi) ); 2224 2225 return result; 2226 2227 } 2228 2229 // dlmf.nist.gov/8.4.15 2230 if ( isNegativeInteger(x) ) { 2231 2232 var n = isComplex(x) ? -x.re : -x; 2233 var s = inv(y), p = s; // mul returns new object 2234 2235 for ( var k = 1 ; k < n ; k++ ) { 2236 p = mul( p, -k, inv(y) ); 2237 s = add( s, p ); 2238 } 2239 2240 var t = mul( exp(neg(y)), s ); 2241 2242 // dlmf.nist.gov/8.4.4 2243 var result = sub( neg( expIntegralEi( neg(y), true, 1e-14 ) ), t ) 2244 2245 // complex on negative real axis 2246 if ( y < 0 || y.re < 0 && y.im === 0 ) result = sub( result, complex(0,pi) ); 2247 2248 result = mul( (-1)**n/factorial(n), result ); 2249 2250 if ( isComplex(x) && !isComplex(result) ) return complex(result); // complex in, complex out 2251 2252 return result; 2253 2254 } 2255 2256 return mul( exp(neg(y)), hypergeometricU( sub(1,x), sub(1,x), y ) ); 2257 2258 } 2259 2260 if ( arguments.length === 3 ) { 2261 2262 if ( !isZero(y) ) return sub( gamma(x,0,z), gamma(x,0,y) ); 2263 2264 return mul( pow(z,x), inv(x), hypergeometric1F1( x, add(x,1), neg(z) ) ); 2265 2266 } 2267 2268 if ( isPositiveInteger(x) ) return factorial( sub(x,1) ); 2269 2270 // logGamma complex on negative axis 2271 if ( !isComplex(x) && x < 0 ) return exp( logGamma( complex(x) ) ).re; 2272 2273 return exp( logGamma(x) ); 2274 2275} 2276 2277function gammaRegularized( x, y, z ) { 2278 2279 if ( arguments.length === 3 ) return div( gamma(x,y,z), gamma(x) ); 2280 2281 return div( gamma(x,y), gamma(x) ); 2282 2283} 2284 2285function beta( x, y, z, w ) { 2286 2287 if ( arguments.length === 4 ) 2288 2289 return sub( beta(y,z,w), beta(x,z,w) ); 2290 2291 if ( arguments.length === 3 ) 2292 2293 return mul( pow(x,y), inv(y), hypergeometric2F1( y, sub(1,z), add(y,1), x ) ); 2294 2295 return div( mul( gamma(x), gamma(y) ), gamma( add(x,y) ) ); 2296 2297} 2298 2299function betaRegularized( x, y, z, w ) { 2300 2301 if ( arguments.length === 4 ) 2302 2303 return div( beta(x,y,z,w), beta(z,w) ); 2304 2305 return div( beta(x,y,z), beta(y,z) ); 2306 2307} 2308 2309function polygamma( n, x ) { 2310 2311 if ( arguments.length === 1 ) return digamma(x); 2312 2313 if ( !isPositiveInteger(n) ) throw Error( 'Unsupported polygamma index' ); 2314 2315 return mul( (-1)**(n+1) * factorial(n), hurwitzZeta( n+1, x ) ); 2316 2317} 2318 2319 2320function digamma( x ) { 2321 2322 return diff( x => logGamma(x), x ); 2323 2324} 2325 2326 2327function erf( x ) { 2328 2329 var useAsymptotic = 5; 2330 2331 var absArg = Math.abs( arg(x) ); 2332 2333 if ( abs(x) > useAsymptotic && ( absArg < pi/4 || absArg > 3*pi/4 ) ) 2334 2335 return sub( 1, erfc(x) ); 2336 2337 return mul( 2/sqrt(pi), x, hypergeometric1F1( .5, 1.5, neg(mul(x,x)) ) ); 2338 2339} 2340 2341function erfc( x ) { 2342 2343 var useAsymptotic = 5; 2344 2345 var absArg = Math.abs( arg(x) ); 2346 2347 if ( abs(x) > useAsymptotic && ( absArg < pi/4 || absArg > 3*pi/4 ) ) { 2348 2349 // as per dlmf.nist.gov/7.12.1 this could be an independent sum for minor improvement 2350 // these numbers are tiny and need to stay in this function even though 2351 // there is some code duplication with erf 2352 2353 var t = mul( 1/sqrt(pi), exp( neg(mul(x,x)) ), inv(x), 2354 hypergeometric2F0( .5, 1, neg(inv(mul(x,x))) ) ); 2355 2356 if ( x.re < 0 || x < 0 ) return add( 2, t ); 2357 2358 return t; 2359 2360 } 2361 2362 return sub( 1, erf(x) ); 2363 2364} 2365 2366function erfi( x ) { 2367 2368 return mul( complex(0,-1), erf( mul( complex(0,1), x ) ) ); 2369 2370} 2371 2372 2373function fresnelS( x ) { 2374 2375 // can also be evaluated with hypergeometric1F2 2376 2377 var m1 = hypergeometric1F1( .5, 1.5, mul( complex(0,pi/2), pow(x,2) ) ); 2378 var m2 = hypergeometric1F1( .5, 1.5, mul( complex(0,-pi/2), pow(x,2) ) ); 2379 2380 var result = mul( x, sub( m1, m2 ), complex(0,-.5) ); 2381 2382 if ( isComplex(x) ) return result; 2383 2384 return result.re; 2385 2386} 2387 2388function fresnelC( x ) { 2389 2390 // can also be evaluated with hypergeometric1F2 2391 2392 var m1 = hypergeometric1F1( .5, 1.5, mul( complex(0,pi/2), pow(x,2) ) ); 2393 var m2 = hypergeometric1F1( .5, 1.5, mul( complex(0,-pi/2), pow(x,2) ) ); 2394 2395 var result = mul( x, add( m1, m2 ), .5 ); 2396 2397 if ( isComplex(x) ) return result; 2398 2399 return result.re; 2400 2401} 2402 2403
2404function expIntegralEi( x, adjustImForGamma=false, tolerance=1e-10 ) { 2405 2406 // direct series hangs at -1 and +i/-i 2407 // complex average until investigate further 2408 2409 if ( isUnity(neg(x)) ) return complexAverage( x => expIntegralEi(x,adjustImForGamma,tolerance), x ); 2410 2411 var ix = mul( x, complex(0,1) ); 2412 if ( isUnity(ix) || isUnity(neg(ix)) ) 2413 return complexAverage( x => expIntegralEi(x,adjustImForGamma,tolerance), x, complex(0,1e-5) ); 2414 2415 var useAsymptotic = 26; 2416 2417 if ( isComplex(x) ) { 2418 2419 if ( abs(x) > useAsymptotic ) { 2420 2421 var s = complex(1); 2422 var p = complex(1), pLast = p; // mul returns new object 2423 var i = 1; 2424 2425 while ( Math.abs(p.re) > tolerance || Math.abs(p.im) > tolerance ) { 2426 2427 p = mul( p, i, inv(x) ); 2428 if ( abs(p) > abs(pLast) ) break; 2429 2430 s = add( s, p ); 2431 i++; 2432 pLast = p; 2433 2434 } 2435 2436 if ( adjustImForGamma ) 2437 2438 return mul( s, exp(x), inv(x) ); 2439 2440 else { 2441 2442 // combination of logarithms adds/subtracts complex(0,pi) 2443 var sign = x.im > 0 ? 1 : x.im < 0 ? -1 : 0; 2444 2445 return add( mul( s, exp(x), inv(x) ), complex(0,sign*pi) ); 2446 2447 } 2448 2449 } 2450 2451 // determined from pattern on test page 2452 var distanceScale = abs( sub(x,useAsymptotic) ) / useAsymptotic; 2453 var useArbitrary = distanceScale > 1; 2454 2455 // arbitrary precision series is unstable around the origin but not needed 2456 2457 if ( useArbitrary && abs(x) > 1.5 ) { 2458 2459 // use only decimals needed 2460 var n = 17 + Math.round( 10 * ( distanceScale - 1 ) ); 2461 setPrecisionScale( n ); 2462 2463 var y = arbitrary( x ); 2464 2465 var s = arbitrary( complex(0) ); 2466 var p = arbitrary( complex(1) ); 2467 var i = arb1; 2468 2469 while ( div(p.re,i) !== 0n || div(p.im,i) !== 0n ) { 2470 p = div( mul(p,y), i ); 2471 s = add( s, div(p,i) ); 2472 i = add( i, arb1 ); 2473 } 2474 2475 s = add( s, getConstant( 'eulerGamma' ), ln(y) ); 2476 2477 if ( adjustImForGamma ) { 2478 var sign = x.im > 0n ? -1n : x.im < 0n ? 1n : 0n; 2479 s = add( s, complex(0n,sign*onePi) ); 2480 } 2481 2482 s = arbitrary( s ); 2483 2484 resetPrecisionScale(); 2485 2486 // adjust phase on positive imaginary axis according to test 2487 if ( x.im > 0 && x.re === 0 ) s.im += pi; 2488 2489 } else { 2490 2491 var s = complex(0); 2492 var p = complex(1); 2493 var i = 1; 2494 2495 while ( Math.abs(p.re/i) > tolerance || Math.abs(p.im/i) > tolerance ) { 2496 p = mul( p, x, 1/i ); 2497 s = add( s, div(p,i) ); 2498 i++; 2499 } 2500 2501 s = add( s, eulerGamma, log(x) ); 2502 2503 if ( adjustImForGamma ) { 2504 var sign = x.im > 0 ? -1 : x.im < 0 ? 1 : 0; 2505 s = add( s, complex(0,sign*pi) ); 2506 } 2507 2508 } 2509 2510 // real on negative real axis, set phase explicitly rather than log combo 2511 if ( x.re < 0 && x.im === 0 ) s.im = 0; 2512 2513 return s; 2514 2515 } else { 2516 2517 if ( x < 0 ) return expIntegralEi( complex(x) ).re; 2518 2519 if ( Math.abs(x) > useAsymptotic ) { 2520 2521 var s = 1; 2522 var p = 1, pLast = p; 2523 var i = 1; 2524 2525 while ( Math.abs(p) > tolerance ) { 2526 2527 p *= i / x; 2528 if ( Math.abs(p) > Math.abs(pLast) ) break; 2529 2530 s += p; 2531 i++; 2532 pLast = p; 2533 2534 } 2535 2536 return s * Math.exp(x) / x; 2537 2538 } 2539 2540 var s = 0; 2541 var p = 1; 2542 var i = 1; 2543 2544 while ( Math.abs(p/i) > tolerance ) { 2545 p *= x / i; 2546 s += p / i; 2547 i++; 2548 } 2549 2550 return s + eulerGamma + Math.log(x); 2551 2552 } 2553 2554} 2555 2556function logIntegral( x ) { 2557 2558 return expIntegralEi( log(x) ); 2559 2560} 2561 2562function sinIntegral( x ) { 2563 2564 if ( isZero(x) ) return isComplex(x) ? complex(0) : 0; 2565 2566 var ix = mul( complex(0,1), x ); 2567 2568 var result = mul( complex(0,.5), add( gamma(0,neg(ix)), neg(gamma(0,ix)), 2569 log(neg(ix)), neg(log(ix)) ) ); 2570 2571 if ( isComplex(x) ) return result; 2572 2573 return result.re; 2574 2575} 2576 2577function cosIntegral( x ) { 2578 2579 // complex for negative real argument 2580 2581 var ix = mul( complex(0,1), x ); 2582
2583 var result = sub( log(x), mul( .5, add( gamma(0,neg(ix)), gamma(0,ix), 2584 log(neg(ix)), log(ix) ) ) ); 2585 2586 // real for positive real argument 2587 if ( x > 0 ) return result.re; 2588 if ( x.re > 0 && x.im === 0 ) result.im = chop( result.im ); 2589 2590 return result; 2591 2592} 2593 2594function sinhIntegral( x ) { 2595 2596 if ( isZero(x) ) return isComplex(x) ? complex(0) : 0; 2597 2598 var result = mul( .5, add( gamma(0,x), neg(gamma(0,neg(x))), log(x), neg(log(neg(x))) ) ); 2599 2600 if ( isComplex(x) ) return result; 2601 2602 return result.re; 2603 2604} 2605 2606function coshIntegral( x ) { 2607 2608 // complex for negative real argument 2609 2610 return mul( -.5, add( gamma(0,x), gamma(0,neg(x)), neg(log(x)), log(neg(x)) ) ); 2611 2612} 2613 2614function expIntegralE( n, x ) { 2615 2616 if ( isZero(n) ) return div( exp(neg(x)), x ); 2617 2618 if ( isZero(x) && ( n > 1 || n.re > 1 ) ) return inv( sub(n,1) ); 2619 2620 var p = pow( x, sub(n,1) ); 2621 2622 // real on negative real axis for integer powers 2623 if ( isInteger(n) && x.re < 0 ) p.im = chop( p.im ); 2624 2625 return mul( p, gamma( sub(1,n), x ) ); 2626 2627} 2628 2629 2630function hypergeometric0F1( a, x, tolerance=1e-10 ) { 2631 2632 var useAsymptotic = 100; 2633 2634 if ( isNegativeIntegerOrZero(a) ) throw Error( 'Hypergeometric function pole' ); 2635 2636 if ( isComplex(a) || isComplex(x) ) { 2637 2638 // asymptotic form as per Johansson arxiv.org/abs/1606.06977 2639 if ( abs(x) > useAsymptotic ) { 2640 2641 if ( isNegativeIntegerOrZero( sub(a,.5) ) ) { 2642 var result = complexAverage( a => hypergeometric0F1(a,x), a ); 2643 if ( isReal(x) ) result.im = 0; 2644 return result; 2645 } 2646 2647 // transform variables for convenience 2648 var b = sub( mul(2,a), 1 ); 2649 a = sub( a, .5 ); 2650 x = mul( 4, sqrt(x) ); 2651 2652 // copied from hypergeometric1F1 2653 var t1 = mul( gamma(b), pow( neg(x), neg(a) ), inv( gamma(sub(b,a)) ) ); 2654 t1 = mul( t1, hypergeometric2F0( a, add(a,neg(b),1), div(-1,x) ) ); 2655 2656 var t2 = mul( gamma(b), pow( x, sub(a,b) ), exp(x), inv( gamma(a) ) ); 2657 t2 = mul( t2, hypergeometric2F0( sub(b,a), sub(1,a), div(1,x) ) ); 2658 2659 var result = mul( exp( div(x,-2) ), add( t1, t2 ) ); 2660 2661 // real for real arguments 2662 if ( isReal(a) && isReal(x) ) result.im = 0; 2663 2664 return result; 2665 2666 } 2667 2668 var s = complex(1); 2669 var p = complex(1); 2670 var i = 1; 2671 2672 while ( Math.abs(p.re) > tolerance || Math.abs(p.im) > tolerance ) { 2673 p = mul( p, x, inv(a), 1/i ); 2674 s = add( s, p ); 2675 a = add( a, 1 ); 2676 i++; 2677 } 2678 2679 return s; 2680 2681 } else { 2682 2683 // asymptotic form is complex 2684 if ( Math.abs(x) > useAsymptotic ) return hypergeometric0F1( a, complex(x) ).re; 2685 2686 var s = 1; 2687 var p = 1; 2688 var i = 1; 2689 2690 while ( Math.abs(p) > tolerance ) { 2691 p *= x / a / i; 2692 s += p; 2693 a++; 2694 i++; 2695 } 2696 2697 return s; 2698 2699 } 2700 2701} 2702 2703 2704function hypergeometric1F1( a, b, x, tolerance=1e-10 ) { 2705 2706 if ( isEqualTo(a,b) ) { 2707 2708 if ( ( isComplex(a) || isComplex(b) ) && !isComplex(x) ) x = complex(x); 2709 2710 if ( isNegativeInteger(a) ) { 2711 var n = isComplex(a) ? -a.re : -a; 2712 return summation( k => div( pow(x,k), factorial(k) ), [0,n] ); 2713 } 2714 2715 return exp(x); 2716 2717 } 2718 2719 if ( isArbitrary(x) ) { 2720 2721 if ( !isArbitrary(a) ) a = arbitrary(a); 2722 if ( !isArbitrary(b) ) b = arbitrary(b); 2723 2724 if ( isNegativeIntegerOrZero(arbitrary(b)) ) throw Error( 'Hypergeometric function pole' ); 2725 2726 // asymptotic forms not yet implemented 2727 2728 if ( isComplex(a) || isComplex(b) || isComplex(x) ) { 2729 2730 if ( !isComplex(x) ) x = complex(x); 2731 2732 // Kummer transformation 2733 if ( x.re < 0n ) return mul( exp(x), hypergeometric1F1( sub(b,a), b, neg(x) ) ); 2734 2735 var s = complex(arb1); 2736 var p = complex(arb1); 2737 var i = arb1; 2738 2739 while ( abs(p.re) !== 0n || abs(p.im) !== 0n ) { 2740 p = mul( p, x, a, inv(b), inv(i) ); 2741 s = add( s, p ); 2742 a = add( a, arb1 ); 2743 b = add( b, arb1 ); 2744 i += arb1; 2745 } 2746 2747 return s; 2748 2749 } else { 2750 2751 // Kummer transformation 2752 if ( x < 0n ) return mul( exp(x), hypergeometric1F1( b-a, b, -x ) ); 2753 2754 var s = arb1; 2755 var p = arb1; 2756 var i = arb1; 2757 2758 while ( abs(p) !== 0n ) { 2759 p = mul( p, x, a, inv(b), inv(i) ); 2760 s += p; 2761 a += arb1; 2762 b += arb1; 2763 i += arb1; 2764 } 2765 2766 return s; 2767 2768 } 2769 2770 } 2771 2772 var useAsymptotic = 30; 2773 2774 if ( isNegativeIntegerOrZero(b) ) throw Error( 'Hypergeometric function pole' ); 2775 2776 if ( isComplex(a) || isComplex(b) || isComplex(x) ) { 2777 2778 if ( isZero(a) ) return complex(1); 2779 2780 // terminating hypergeometric series holds for entire complex plane 2781 if ( isNegativeInteger(a) ) return hypergeometricSeries( [a], [b], x, true ); 2782 2783 if ( !isComplex(x) ) x = complex(x); 2784 2785 // Kummer transformation 2786 if ( x.re < 0 ) return mul( exp(x), hypergeometric1F1( sub(b,a), b, neg(x) ) ); 2787 2788 // asymptotic form as per Johansson arxiv.org/abs/1606.06977 2789 if ( abs(x) > useAsymptotic ) { 2790 2791 if ( isNegativeInteger(sub(b,a)) ) // zero/negative a already handled, b is not a 2792 return complexAverage( a =>
2792 hypergeometric1F1(a,b,x), a ); 2793 2794 var t1 = mul( gamma(b), pow( neg(x), neg(a) ), inv( gamma(sub(b,a)) ), 2795 hypergeometric2F0( a, add(a,neg(b),1), div(-1,x) ) ); 2796 2797 var t2 = mul( gamma(b), pow( x, sub(a,b) ), exp(x), inv( gamma(a) ), 2798 hypergeometric2F0( sub(b,a), sub(1,a), div(1,x) ) ); 2799 2800 var result = add( t1, t2 ); 2801 2802 // real for real arguments 2803 if ( isReal(a) && isReal(b) && isReal(x) ) result.im = 0; 2804 2805 return result; 2806 2807 } 2808 2809 // function to describe wedges along imaginary axis inside 2810 // direct region as per test pages 2811 function wedge( x ) { 2812 var slope = Math.sqrt(3) - 1; 2813 if ( x.re > 0 ) { 2814 var test = slope*x.re + useAsymptotic/2; 2815 return x.im > test || x.im < -test; 2816 } else { 2817 var test = -slope*x.re + useAsymptotic/2; 2818 return x.im > test || x.im < -test; 2819 } 2820 } 2821 2822 if ( wedge(x) ) { 2823 setPrecisionScale( 22 ); 2824 var result = arbitrary( hypergeometric1F1( a, b, arbitrary(x) ) ); 2825 resetPrecisionScale(); 2826 return result; 2827 } 2828 2829 var s = complex(1); 2830 var p = complex(1); 2831 var i = 1; 2832 2833 while ( Math.abs(p.re) > tolerance || Math.abs(p.im) > tolerance ) { 2834 p = mul( p, x, a, inv(b), 1/i ); 2835 s = add( s, p ); 2836 a = add( a, 1 ); 2837 b = add( b, 1 ); 2838 i++; 2839 } 2840 2841 return s; 2842 2843 } else { 2844 2845 if ( a === 0 ) return 1; 2846 2847 // terminating series holds for entire axis 2848 if ( isNegativeInteger(a) ) return hypergeometricSeries( [a], [b], x ); 2849 2850 // Kummer transformation 2851 if ( x < 0 ) return exp(x) * hypergeometric1F1( b-a, b, -x ); 2852 2853 // asymptotic form is complex 2854 if ( x > useAsymptotic ) return hypergeometric1F1( a, b, complex(x) ).re; 2855 2856 var s = 1; 2857 var p = 1; 2858 var i = 1; 2859 2860 while ( Math.abs(p) > tolerance ) { 2861 p *= x * a / b / i; 2862 s += p; 2863 a++; 2864 b++; 2865 i++; 2866 } 2867 2868 return s; 2869 2870 } 2871 2872} 2873 2874 2875function hypergeometricU( a, b, x ) { 2876 2877 if ( isEqualTo( add(a,1), b ) ) return pow( x, neg(a) ); 2878 2879 if ( isZero(a) ) 2880 if ( isComplex(a) || isComplex(b) || isComplex(x) ) return complex(1); 2881 else return 1; 2882 2883 var useAsymptotic = 25; 2884 2885 // asymptotic form as per Johansson arxiv.org/abs/1606.06977 2886 if ( abs(x) > useAsymptotic ) { 2887 2888 return mul( pow( x, neg(a) ), hypergeometric2F0( a, add(a,neg(b),1), neg(inv(x)) ) ); 2889 2890 } 2891 2892 // special case through independent expIntegralEi for speed 2893 if ( isUnity(a) && isUnity(b) ) return mul( exp(x), gamma(0,x) ); 2894 2895 if ( isUnity(b) ) return complexAverage( b => hypergeometricU(a,b,x), b ); 2896 2897 // could be combined with previous special case 2898 if ( isPositiveInteger(a) && isEqualTo(a,b) ) return mul( exp(x), gamma(sub(1,a),x) ); 2899 2900 // dlmf.nist.gov/13.2.7 2901 if ( isNegativeInteger(a) ) { 2902 var m = isComplex(a) ? -a.re : -a; 2903 return mul( (-1)**m, pochhammer(b,m), hypergeometric1F1(a,b,x) ); 2904 } 2905 2906 var t1 = mul( gamma(sub(b,1)), inv( gamma(a) ), pow( x, sub(1,b) ), 2907 hypergeometric1F1( add(a,neg(b),1), sub(2,b), x ) ); 2908 2909 var t2 = mul( gamma(sub(1,b)), inv( gamma(add(a,neg(b),1)) ), hypergeometric1F1( a, b, x ) ); 2910 2911 var max = Math.max( abs(t1), abs(t2) ); 2912 if ( max < 100 ) return add( t1, t2 ); 2913 2914 var extraDecimals = +max.toExponential().split( 'e' )[1]; 2915 setPrecisionScale( defaultDecimals + extraDecimals ); 2916 2917 a = arbitrary(a); 2918 b = arbitrary(b); 2919 x = arbitrary(x); 2920 2921 t1 = mul( gamma(sub(b,arb1)), inv( gamma(a) ), pow( x, sub(arb1,b) ), 2922 hypergeometric1F1( add(a,neg(b),arb1), sub(arb2,b), x ) ); 2923 2924 t2 = mul( gamma(sub(arb1,b)), inv( gamma(add(a,neg(b),arb1)) ), hypergeometric1F1( a, b, x ) ); 2925 2926 var result = arbitrary( add( t1, t2 ) ); 2927 2928 resetPrecisionScale(); 2929 2930 return result; 2931 2932} 2933 2934function whittakerM( k, m, x ) { 2935 2936 return mul( exp( mul(-.5,x) ), pow( x, add(m,.5) ), 2937 hypergeometric1F1( add(m,neg(k),.5), add(mul(2,m),1), x ) ); 2938 2939} 2940 2941function whittakerW( k, m, x ) { 2942 2943 return mul( exp( mul(-.5,x) ), pow( x, add(m,.5) ), 2944 hypergeometricU( add(m,neg(k),.5), add(mul(2,m),1), x ) ); 2945 2946} 2947 2948 2949function hypergeometric2F0( a, b, x, tolerance=1e-10 ) { 2950 2951 var terms = 50; 2952 2953 if ( isComplex(a) || isComplex(b) || isComplex(x) ) { 2954 2955 var s = complex(1); 2956 var p = complex(1), pLast = p; // mul returns new object 2957 var converging = false; // first few terms can be larger than unity 2958 var i = 1; 2959 2960 while ( Math.abs(p.re) > tolerance || Math.abs(p.im) > tolerance ) { 2961 2962 p = mul( p, x, a, b, 1/i ); 2963 2964 if ( abs(p) > abs(pLast) && converging ) break; // prevent runaway sum 2965 if ( abs(p) < abs(pLast) ) converging = true; 2966 if ( i > terms ) throw Error( 'Not converging after ' + terms + ' terms' ); 2967 2968 s = add( s, p ); 2969 a = add( a, 1 ); 2970 b = add( b, 1 ); 2971 i++; 2972 pLast = p; 2973 2974 } 2975 2976 return s; 2977 2978 } else { 2979 2980 var s = 1; 2981 var p = 1, pLast = p; 2982 var converging = false; // first few terms can be larger than unity 2983 var i = 1; 2984 2985 while ( Math.abs(p) > tolerance ) { 2986 2987 p *= x * a * b / i; 2988 2989 if ( Math.abs(p) > Math.abs(pLast) && converging ) break; // prevent runaway sum 2990 if ( Math.abs(p) < Math.abs(pLast) ) converging = true; 2991 if ( i > terms ) throw Error( 'Not converging after ' + terms + ' terms' ); 2992 2993 s += p; 2994 a++; 2995 b++; 2996 i++; 2997 pLast = p; 2998 2999 } 3000 3001 return s; 3002 3003 } 3004 3005} 3006 3007 3008function hypergeometric2F1( a, b, c, x, tolerance=1e-10 ) { 3009 3010 if ( isEqualTo(a,c) ) return pow( sub(1,x), neg(b) ); 3011 3012 if ( isEqualTo(b,c) ) return pow( sub(1,x), neg(a) ); 3013 3014 if ( !isUnity(neg(x)) && abs(add(x,1)) < tolerance ) 3015 return hypergeometric2F1( a, b, c, isComplex(x) ? complex(-1) : -1 ); 3016 3017 if ( isNegativeIntegerOrZero(c) ) throw Error( 'Hypergeometric function pole' ); 3018 3019 if ( isComplex(a) || isComplex(b) || isComplex(c) || isComplex(x) ) { 3020 3021 if ( isZero(a) || isZero(b) ) return complex(1); 3022 3023 // terminating hypergeometric series holds for entire complex plane 3024 if ( isNegativeInteger(a) || isNegativeInteger(b) ) 3025 return hypergeometricSeries( [a,b], [c], x, true ); 3026 3027 if ( isUnity(x) ) 3028 if ( re(sub(c,add(a,b))) > 0 ) 3029 return div( mul( gamma(c), gamma(sub(c,add(a,b))) ), 3030 mul( gamma(sub(c,a)), gamma(sub(c,b)) ) ); 3031 else throw Error( 'Divergent Gauss hypergeometric function' ); 3032 3033 // choose smallest absolute value of transformed argument 3034 // transformations from Abramowitz & Stegun p.559 3035 // fewer operations compared to dlmf.nist.gov/15.8 3036 3037 var absArray = [ abs(x), abs(div(x,sub(x,1))), abs(sub(1,x)), 3038 abs(inv(x)), abs(inv(sub(1,x))), abs(sub(1,inv(x))) ]; 3039 3040 var index = absArray.indexOf( Math.min.apply( null, absArray ) ); 3041 3042 switch( index ) { 3043 3044 case 0: 3045 3046 break; 3047 3048 case 1: 3049 3050 return mul( pow( sub(1,x), neg(a) ), hypergeometric2F1( a, sub(c,b), c, div(x,sub(x,1)) ) ); 3051 3052 case 2: 3053 3054 // zero/negative a or b already handled, c is not a or b 3055 // remaining integer values hit one term or other 3056 3057 if ( isNegativeInteger(sub(c,a)) || isInteger(sub(c,add(a,b))) ) 3058 return complexAverage( a =>
3058 hypergeometric2F1(a,b,c,x), a ); 3059 3060 if ( isNegativeInteger(sub(c,b)) ) 3061 return complexAverage( b => hypergeometric2F1(a,b,c,x), b ); 3062 3063 var t1 = mul( gamma(c), gamma( sub(c,add(a,b)) ), 3064 inv( gamma(sub(c,a)) ), inv( gamma(sub(c,b)) ), 3065 hypergeometric2F1( a, b, add(a,b,neg(c),1), sub(1,x) ) ); 3066 3067 var t2 = mul( pow( sub(1,x), sub(c,add(a,b)) ), 3068 gamma(c), gamma( sub(add(a,b),c) ), inv( gamma(a) ), inv( gamma(b) ), 3069 hypergeometric2F1( sub(c,a), sub(c,b), add(c,neg(a),neg(b),1), sub(1,x) ) ); 3070 3071 return add( t1, t2 ); 3072 3073 case 3: 3074 3075 if ( isNegativeInteger(sub(c,a)) || isInteger(sub(b,a)) ) 3076 return complexAverage( a => hypergeometric2F1(a,b,c,x), a ); 3077 3078 if ( isNegativeInteger(sub(c,b)) ) 3079 return complexAverage( b => hypergeometric2F1(a,b,c,x), b ); 3080 3081 var t1 = mul( gamma(c), gamma(sub(b,a)), inv( gamma(b) ), 3082 inv( gamma(sub(c,a)) ), pow( neg(x), neg(a) ), 3083 hypergeometric2F1( a, add(1,neg(c),a), add(1,neg(b),a), inv(x) ) ); 3084 3085 var t2 = mul( gamma(c), gamma(sub(a,b)), inv( gamma(a) ), 3086 inv( gamma(sub(c,b)) ), pow( neg(x), neg(b) ), 3087 hypergeometric2F1( b, add(1,neg(c),b), add(1,neg(a),b), inv(x) ) ); 3088 3089 return add( t1, t2 ); 3090 3091 case 4: 3092 3093 if ( isNegativeInteger(sub(c,a)) || isInteger(sub(b,a)) ) 3094 return complexAverage( a => hypergeometric2F1(a,b,c,x), a ); 3095 3096 if ( isNegativeInteger(sub(c,b)) ) 3097 return complexAverage( b => hypergeometric2F1(a,b,c,x), b ); 3098 3099 var t1 = mul( pow( sub(1,x), neg(a) ), gamma(c), gamma(sub(b,a)), 3100 inv( gamma(b) ), inv( gamma(sub(c,a)) ), 3101 hypergeometric2F1( a, sub(c,b), add(a,neg(b),1), inv(sub(1,x)) ) ); 3102 3103 var t2 = mul( pow( sub(1,x), neg(b) ), gamma(c), gamma(sub(a,b)), 3104 inv( gamma(a) ), inv( gamma(sub(c,b)) ), 3105 hypergeometric2F1( b, sub(c,a), add(b,neg(a),1), inv(sub(1,x)) ) ); 3106 3107 return add( t1, t2 ); 3108 3109 case 5: 3110 3111 if ( isNegativeInteger(sub(c,a)) || isInteger(sub(c,add(a,b))) ) 3112 return complexAverage( a => hypergeometric2F1(a,b,c,x), a ); 3113 3114 if ( isNegativeInteger(sub(c,b)) ) 3115 return complexAverage( b => hypergeometric2F1(a,b,c,x), b ); 3116 3117 var t1 = mul( gamma(c), gamma( sub(c,add(a,b)) ), inv( gamma(sub(c,a)) ), 3118 inv( gamma(sub(c,b)) ), pow( x, neg(a) ), 3119 hypergeometric2F1( a, add(a,neg(c),1), add(a,b,neg(c),1), sub(1,inv(x)) ) ); 3120 3121 var t2 = mul( gamma(c), gamma( sub(add(a,b),c) ), inv( gamma(a) ), inv( gamma(b) ), 3122 pow( sub(1,x), sub(c,add(a,b)) ), pow( x, sub(a,c) ), 3123 hypergeometric2F1( sub(c,a), sub(1,a), add(c,neg(a),neg(b),1), sub(1,inv(x)) ) ); 3124 3125 return add( t1, t2 ); 3126 3127 } 3128 3129 var s = complex(1); 3130 var p = complex(1); 3131 var i = 1; 3132 3133 while ( Math.abs(p.re) > tolerance || Math.abs(p.im) > tolerance ) { 3134 p = mul( p, x, a, b, inv(c), 1/i ); 3135 s = add( s, p ); 3136 a = add( a, 1 ); 3137 b = add( b, 1 ); 3138 c = add( c, 1 ); 3139 i++; 3140 } 3141 3142 return s; 3143 3144 } else { 3145 3146 if ( a === 0 || b === 0 ) return 1; 3147 3148 // terminating series holds for entire axis 3149 if ( isNegativeInteger(a) || isNegativeInteger(b) ) 3150 return hypergeometricSeries( [a,b], [c], x ); 3151 3152 // transformation from Abramowitz & Stegun p.559 3153 if ( x < -1 ) { 3154 3155 // zero/negative a or b already handled, c is not a or b 3156 3157 if ( isNegativeInteger(c-a) ) 3158 return complexAverage( a => hypergeometric2F1(a,b,c,x), a ); 3159 3160 if ( isNegativeInteger(c-b) ) 3161 return complexAverage( b => hypergeometric2F1(a,b,c,x), b ); 3162 3163 var t1 = gamma(c) * gamma(b-a) / gamma(b) / gamma(c-a) 3164 * (-x)**(-a) * hypergeometric2F1( a, 1-c+a, 1-b+a, 1/x ); 3165 var t2 = gamma(c) * gamma(a-b) / gamma(a) / gamma(c-b) 3166 * (-x)**(-b) * hypergeometric2F1( b, 1-c+b, 1-a+b, 1/x ); 3167 3168 return t1 + t2; 3169 3170 } 3171 3172 // faster than direct sum for general case 3173 if ( x === -1 ) return hypergeometric2F1( a, b, c, complex(x) ).re; 3174 3175 if ( x === 1 ) 3176 if ( c - a - b > 0 ) return gamma(c) * gamma(c-a-b) / gamma(c-a) / gamma(c-b); 3177 else throw Error( 'Divergent Gauss hypergeometric function' ); 3178 3179 if ( x > 1 ) return hypergeometric2F1( a, b, c, complex(x) ); 3180 3181 var s = 1; 3182 var p = 1; 3183 var i = 1; 3184 3185 while ( Math.abs(p) > tolerance ) { 3186 p *= x * a * b / c / i; 3187 s += p; 3188 a++; 3189 b++; 3190 c++; 3191 i++; 3192 } 3193 3194 return s; 3195 3196 } 3197 3198} 3199 3200 3201function hypergeometric1F2( a, b, c, x, tolerance=1e-10 ) { 3202 3203 var useAsymptotic = 150; 3204 3205 if ( isComplex(a) || isComplex(b) || isComplex(c) || isComplex(x) ) { 3206 3207 // functions.wolfram.com/HypergeometricFunctions/Hypergeometric1F2/06/02/03/0002/ 3208 3209 if ( abs(x) > useAsymptotic ) { 3210 3211 var aNegbNegc = add( a, neg(b), neg(c) ); 3212 3213 var p = div( add( aNegbNegc, 1/2 ), 2 ); 3214 3215 var ck = [ 1 ]; 3216 3217 ck[1] = add( mul( add(mul(3,a),b,c,-2), aNegbNegc, 1/2 ), mul(2,b,c), -3/8 ); 3218 3219 var toSquare = add( mul(b,c), mul( aNegbNegc, add(mul(3,a),b,c,-2), 1/4 ), -3/16 ); 3220 3221 ck[2] = add( mul( 2, toSquare, toSquare ), 3222 neg( mul( sub(mul(2,a),3), b, c ) ), 3223 mul( aNegbNegc, add( mul(-8,mul(a,a)), mul(11,a), b, c, -2 ), 1/4 ), 3224 -3/16 ); 3225 3226 var plusI = complex(0,1), minusI = complex(0,-1); 3227 3228 function w( k ) { return mul( 1/2**k, ck[k], pow(neg(x),-k/2) ); } 3229 3230 var w1 = w(1), w2 = w(2); 3231 3232 var s1 = add( 1, mul(minusI,w1), neg(w2) ); 3233 var s2 = add( 1, mul(plusI,w1), neg(w2) ); 3234
3235 var k = 2, wk = w(k), powPlusI = powMinusI = -1; 3236 3237 // wk not always complex 3238 while ( abs(wk) > tolerance ) { 3239 3240 k++; 3241 powPlusI = mul( powPlusI, plusI ); 3242 powMinusI = mul( powMinusI, minusI ); 3243 3244 var t1 = mul( add( 3*k**2, mul( 2*k, add(mul(-3,a),b,c,-2) ), 3245 mul(3,mul(a,a)), neg(mul(sub(b,c),sub(b,c))), 3246 neg(mul(2,a,add(b,c,-2))), 1/4 ), 3247 ck[k-1] ); 3248 3249 var t2 = mul( add(k,neg(a),b,neg(c),-1/2), add(k,neg(a),neg(b),c,-1/2), 3250 add(k,neg(a),b,c,-5/2), ck[k-2] ); 3251 3252 ck[k] = div( sub( t1, t2 ), 2*k ); 3253 3254 wk = w(k); 3255 3256 s1 = add( s1, mul( powMinusI, wk ) ); 3257 s2 = add( s2, mul( powPlusI, wk ) ); 3258 3259 } 3260 3261 var exponent = add( mul(pi,p), mul(2,sqrt(neg(x))) ); 3262 3263 var u1 = exp( mul( plusI, exponent ) ); 3264 var u2 = exp( mul( minusI, exponent ) ); 3265 3266 var s = add( mul(u1,s1), mul(u2,s2) ); 3267 3268 var t1 = mul( 1/(2*sqrt(pi)), inv(gamma(a)), pow(neg(x),p), s ); 3269 3270 var t2 = mul( inv(gamma(sub(b,a))), inv(gamma(sub(c,a))), pow(neg(x),neg(a)), 3271 hypergeometricSeries( [ a, add(a,neg(b),1), add(a,neg(c),1) ], [], inv(x), true ) ); 3272 3273 return mul( gamma(b), gamma(c), add( t1, t2 ) ); 3274 3275 } 3276 3277 return hypergeometricSeries( [a], [b,c], x, true ); 3278 3279 } else { 3280 3281 // asymptotic form is complex 3282 if ( Math.abs(x) > useAsymptotic ) return hypergeometric1F2( a, b, c, complex(x) ).re; 3283 3284 return hypergeometricSeries( [a], [b,c], x ); 3285 3286 } 3287 3288} 3289 3290 3291// convenience function for less-used hypergeometrics 3292// accessing array slower than local variables 3293// for loops faster than forEach or reduce 3294 3295function hypergeometricSeries( A, B, x, complexArguments=false, tolerance=1e-10 ) { 3296 3297 if ( complexArguments ) { 3298 3299 var s = complex(1); 3300 var p = complex(1); 3301 var i = 1; 3302 3303 while ( Math.abs(p.re) > tolerance || Math.abs(p.im) > tolerance ) { 3304 3305 for ( var j = 0 ; j < A.length ; j++ ) { 3306 p = mul( p, A[j] ); 3307 A[j] = add( A[j], 1 ); 3308 } 3309 3310 for ( var j = 0 ; j < B.length ; j++ ) { 3311 p = div( p, B[j] ); 3312 B[j] = add( B[j], 1 ); 3313 } 3314 3315 p = mul( p, x, 1/i ); 3316 s = add( s, p ); 3317 i++; 3318 3319 } 3320 3321 return s; 3322 3323 } else { 3324 3325 var s = 1; 3326 var p = 1; 3327 var i = 1; 3328 3329 while ( Math.abs(p) > tolerance ) { 3330 3331 for ( var j = 0 ; j < A.length ; j++ ) { 3332 p *= A[j]; 3333 A[j]++; 3334 } 3335 3336 for ( var j = 0 ; j < B.length ; j++ ) { 3337 p /= B[j]; 3338 B[j]++; 3339 } 3340 3341 p *= x / i; 3342 s += p; 3343 i++; 3344 3345 } 3346 3347 return s; 3348 3349 } 3350 3351} 3352 3353 3354function hypergeometricPFQ( A, B, x ) { 3355 3356 // dlmf.nist.gov/16.11 for general transformations 3357 3358 // for B.length > A.length terms can get very large 3359 // roundoff errors even for formally convergent series 3360 3361 if ( abs(x) > 1 ) throw Error( 'General hypergeometric argument currently restricted' ); 3362
3363 // check for complex parameters 3364 var cp = false; 3365 A.forEach( a => cp = cp || isComplex(a) ); 3366 B.forEach( b => cp = cp || isComplex(b) ); 3367 3368 if ( cp || isComplex(x) ) return hypergeometricSeries( A, B, x, true ); 3369 3370 return hypergeometricSeries( A, B, x ); 3371 3372} 3373 3374 3375function exp( x ) { 3376 3377 if ( isArbitrary(x) ) { 3378 3379 if ( isComplex(x) ) { 3380 3381 var expXre = exp(x.re); 3382 3383 return { re: mul( expXre, cos(x.im) ), 3384 im: mul( expXre, sin(x.im) ) }; 3385 3386 } 3387 3388 var m = div( x, ln10 ) / precisionScale; 3389 3390 x -= m * ln10; 3391 3392 // direct sum faster than function inversion 3393 var s = arb1; 3394 var p = arb1; 3395 var i = arb1; 3396 3397 while ( p !== 0n ) { 3398 p = div( mul( p, x ), i ); 3399 s += p; 3400 i += arb1; 3401 } 3402 3403 if ( m > 0n ) s *= 10n**m; 3404 if ( m < 0n ) s /= 10n**-m; // value approaches zero for fixed decimals 3405 3406 // could also return as mantissa/exponent 3407 return s; 3408 3409 } 3410 3411 if ( isComplex(x) ) 3412 3413 return { re: Math.exp(x.re) * Math.cos(x.im), 3414 im: Math.exp(x.re) * Math.sin(x.im) }; 3415 3416 return Math.exp(x); 3417 3418} 3419 3420function logisticSigmoid( x ) { return inv( add( 1, exp(neg(x)) ) ); } 3421 3422 3423function log( x, base ) { 3424 3425 if ( isComplex(x) || isComplex(base) ) { 3426 3427 if ( base === undefined ) return { re: Math.log( abs(x) ), im: arg(x) }; 3428 3429 return div( log(x), log(base) ); 3430 3431 } 3432 3433 if ( x < 0 ) return log( complex(x), base ); 3434 3435 if ( base === undefined ) return Math.log(x); 3436 3437 return Math.log(x) / Math.log(base); 3438 3439} 3440 3441function ln( x ) { 3442 3443 // Brent, Modern Computer Arithmetic, second AGM algorithm 3444 3445 function arbitraryAGM( x, y ) { 3446 3447 var t, u; 3448 3449 if ( isComplex(x) ) { 3450 3451 var maxIter = 20, i = 0; 3452 3453 while( x.re !== y.re || x.im !== y.im ) { 3454 t = x, u = y; 3455 x = div( add( t, u ), arb2 ); 3456 y = sqrt( mul( t, u ) ); 3457 i++; 3458 if ( i > maxIter ) break; // convergence on complex plane not assured... 3459 } 3460 3461 } else { 3462 3463 while( x !== y ) { 3464 t = x, u = y; 3465 x = div( t + u, arb2 ); 3466 y = sqrt( mul( t, u ) ); 3467 } 3468 3469 } 3470 3471 return x; 3472 3473 } 3474 3475 function arbitraryTheta2( x ) { 3476 3477 var p = arb1; 3478 var s = arb1; 3479 var i = 1; 3480 3481 if ( isComplex(x) ) { 3482 3483 while ( p.re !== 0n || p.im !== 0n ) { 3484 for ( var j = 0 ; j < 8*i ; j++ ) p = mul( p, x ); 3485 s = add( s, p ); 3486 i++; 3487 } 3488 3489 } else { 3490 3491 while ( p !== 0n ) { 3492 for ( var j = 0 ; j < 8*i ; j++ ) p = mul( p, x ); 3493 s = s + p; 3494 i++; 3495 } 3496 3497 } 3498 3499 return mul( arb2, x, s ); 3500 3501 } 3502 3503 function arbitraryTheta3( x ) { 3504 3505 var p = arb1; 3506 var s = 0n; 3507 var i = 1; 3508 3509 if ( isComplex(x) ) { 3510 3511 while ( p.re !== 0n || p.im !== 0n ) { 3512 for ( var j = 0 ; j < 4*(2*i-1) ; j++ ) p = mul( p, x ); 3513 s = add( s, p ); 3514 i++; 3515 } 3516 3517 } else { 3518 3519 while ( p !== 0n ) { 3520 for ( var j = 0 ; j < 4*(2*i-1) ; j++ ) p = mul( p, x ); 3521 s = s + p; 3522 i++; 3523 } 3524 3525 } 3526 3527 return add( arb1, mul( arb2, s ) ); 3528 3529 } 3530 3531 function arbitraryLn( x) { 3532 3533 var arb4 = 4n*arb1, arb10 = 10n*arb1; 3534 3535 // convergence near unit circle problematic 3536 // scale argument radially and subtract scaling 3537 // any number will work, convergence faster further out 3538 3539 if ( abs(x) < arb10 ) return sub( arbitraryLn(mul(arb10,x)), ln10 ); 3540 3541 x = div( arb1, x ); 3542 3543 var t2 = arbitraryTheta2(x); 3544 var t3 = arbitraryTheta3(x); 3545 3546 return div( onePi, mul( arb4, arbitraryAGM( mul(t2,t2), mul(t3,t3) ) ) ); 3547 3548 } 3549 3550 if ( isArbitrary(x) ) { 3551 3552 if ( isComplex(x) ) { 3553 3554 if ( x.re === arb1 && x.im === 0n ) return { re: 0n, im: 0n }; 3555 3556 if ( x.re === 0n && x.im === 0n ) 3557 throw Error( 'Arbitrary natural logarithm singularity' ); 3558 3559 // directly set phase on complex axis 3560 if ( x.re === 0n ) 3561 if ( x.im > 0n ) return { re: arbitraryLn(x.im), im: halfPi }; 3562 else return { re: arbitraryLn(-x.im), im: -halfPi }; 3563 3564 var result = arbitraryLn(x); 3565 3566 // adjust imaginary part on left half-plane 3567 if ( x.re < 0n ) { 3568 if ( result.im > 0n ) result.im -= onePi; 3569 else result.im += onePi; 3570 } 3571 3572 return result; 3573 3574 } 3575 3576 if ( x === arb1 ) return 0n; 3577 if ( x === 0n ) throw Error( 'Arbitrary natural logarithm singularity' ); 3578 3579 if ( x < 0n ) return { re: arbitraryLn( -x ), im: onePi }; 3580 3581 return arbitraryLn(x); 3582 3583 } 3584 3585 return log(x); 3586 3587} 3588 3589 3590function lambertW( k, x, tolerance=1e-10 ) { 3591 3592 if ( arguments.length === 1 ) { 3593 x = k; 3594 k = 0; 3595 } 3596 3597 // restrict to real integers for convenience 3598 if ( !Number.isInteger(k) ) throw Error( 'Unsupported Lambert W index' ); 3599 3600 if ( isZero(x) ) 3601 if ( k === 0 ) return x; 3602 else throw Error( 'Lambert W pole' ); 3603 3604 var expMinusOne = Math.exp(-1); 3605 3606 if ( ( k === 0 || k === -1 ) && abs( add( x, expMinusOne ) ) < tolerance*tolerance ) 3607 if ( isComplex(x) ) return complex(-1); 3608 else return -1; 3609 3610 // handle real cases separately, complex otherwise 3611 3612 if ( !isComplex(x) ) { 3613 if ( k === 0 && x > -expMinusOne ) 3614 return findRoot( w => w * Math.exp(w) - x, [-1,1000], { tolerance: tolerance } ); 3615 if ( k === -1 && x > -expMinusOne && x < 0 ) 3616 return findRoot( w => w * Math.exp(w) - x, [-1000,-1], { tolerance: tolerance } ); 3617 x = complex(x); 3618 } 3619 3620 // inversion by complex root finding with custom Newton's method 3621 var maxIter = 100; 3622 3623 if ( k === 0 && abs(x) < 1.25 ) { 3624 // unstable along branch cut, add small complex part to avoid error 3625 if ( x.im === 0 && x.re < -expMinusOne ) x.im = tolerance; 3626 // based on test page: unstable region jumps between sheets
3627 var w = x.re < .5 ? complex( 0, .5*Math.sign(x.im) ) : complex(0); 3628 } else { 3629 var L = add( log(x), complex(0,2*pi*k) ); 3630 var w = sub( L, log(L) ); 3631 } 3632 3633 for ( var i = 0; i < maxIter ; i++ ) { 3634 var delta = div( sub( mul(w,exp(w)), x ), mul( exp(w), add(w,1) ) ); 3635 w = sub( w, delta ); 3636 if ( abs(delta) < tolerance ) return w; 3637 } 3638 3639 throw Error( 'No Lambert W root found at ' + JSON.stringify(x) ); 3640 3641} 3642 3643function inverseLambertW( x ) { return mul( x, exp(x) ); } 3644 3645function wrightOmega( x ) { 3646 3647 var unwinding = Math.ceil( ( im(x)/pi - 1 ) / 2 ); 3648 3649 return lambertW( unwinding, exp(x) ); 3650 3651} 3652 3653 3654function chop( x, tolerance=1e-10 ) { 3655 3656 if ( Array.isArray(x) ) { 3657 var v = vector( x.length ); 3658 for ( var i = 0 ; i < x.length ; i++ ) v[i] = chop( x[i] ); 3659 return v; 3660 } 3661 3662 if ( isComplex(x) ) return complex( chop(x.re), chop(x.im) ); 3663 3664 if ( Math.abs(x) < tolerance ) x = 0; 3665 3666 return x; 3667 3668} 3669 3670function round( x, y ) { 3671 3672 if ( arguments.length === 2 ) return mul( y, round( div(x,y) ) ); 3673 3674 if ( isComplex(x) ) return complex( Math.round(x.re), Math.round(x.im) ); 3675 3676 return Math.round(x); 3677 3678} 3679 3680function ceiling( x ) { 3681 3682 if ( isComplex(x) ) return complex( Math.ceil(x.re), Math.ceil(x.im) ); 3683 3684 return Math.ceil(x); 3685 3686} 3687 3688function floor( x ) { 3689 3690 if ( isComplex(x) ) return complex( Math.floor(x.re), Math.floor(x.im) ); 3691 3692 return Math.floor(x); 3693 3694} 3695 3696function sign( x ) { 3697 3698 if ( isZero(x) ) return x; 3699 3700 return div( x, abs(x) ); 3701 3702} 3703 3704function integerPart( x ) { 3705 3706 if ( isComplex(x) ) return complex( Math.trunc(x.re), Math.trunc(x.im) ); 3707 3708 return Math.trunc(x); 3709 3710} 3711 3712function fractionalPart( x ) { return sub( x, integerPart(x) ); } 3713 3714 3715function random( x, y ) { 3716 3717 if ( arguments.length === 0 ) return Math.random(); 3718 3719 if ( arguments.length === 1 ) 3720 if ( isComplex(x) ) 3721 return { re: x.re * Math.random(), im: x.im * Math.random() }; 3722 else return x * Math.random(); 3723 3724 if ( isComplex(x) || isComplex(y) ) { 3725 if ( !isComplex(x) ) x = complex(x); 3726 if ( !isComplex(y) ) y = complex(y); 3727 return { re: random( x.re, y.re ), im: random( x.im, y.im ) }; 3728 } 3729 3730 return Math.abs(x-y) * Math.random() + ( x > y ? y : x ); 3731 3732} 3733 3734 3735function kronecker( i, j ) { 3736 3737 if ( arguments.length === 2 ) { 3738 3739 if ( isComplex(i) || isComplex(j) ) { 3740 3741 if ( !isComplex(i) ) i = complex(i); 3742 if ( !isComplex(j) ) j = complex(j); 3743 3744 return kronecker( i.re, j.re) * kronecker( i.im, j.im ); 3745 3746 } 3747 3748 return i === j ? 1 : 0; 3749 3750 } 3751 3752 var result = kronecker( i, j ); 3753 3754 for ( var k = 2 ; k < arguments.length ; k++ ) 3755 result *= kronecker( i, arguments[k] ); 3756 3757 return result; 3758 3759} 3760 3761 3762function piecewise() { 3763 3764 var pieces = arguments; 3765 3766 return function( x ) { 3767 3768 for ( var i = 0 ; i < pieces.length ; i++ ) { 3769 var domain = pieces[i][1]; 3770 if ( x >= domain[0] && x <= domain[1] ) return pieces[i][0](x); 3771 } 3772 3773 return 0; 3774 3775 } 3776 3777} 3778 3779 3780function hermite( n, x ) { 3781 3782 function coefficients( n ) { 3783 3784 if ( isComplex(n) ) var returnComplex = true, n = n.re; 3785 3786 var minus2 = [ 1 ]; 3787 var minus1 = [ 2, 0 ]; 3788 var t, current; 3789
3790 if ( n === 0 ) return minus2; 3791 if ( n === 1 ) return minus1; 3792 3793 for ( var i = 2 ; i <= n ; i++ ) { 3794 current = []; 3795 t = minus1.slice(); 3796 t.push( 0 ); 3797 minus2.unshift( 0, 0 ); 3798 for ( var k = 0 ; k < t.length ; k++ ) 3799 current.push( 2*t[k] - 2*(i-1)*minus2[k] ); 3800 minus2 = minus1; 3801 minus1 = current; 3802 } 3803 3804 if ( returnComplex ) { 3805 current.forEach( (e,i,a) => a[i] = complex(e) ); 3806 return current; 3807 } 3808 3809 return current; 3810 3811 } 3812 3813 if ( isComplex(n) || isComplex(x) ) { 3814 3815 if ( isPositiveIntegerOrZero(n) ) return polynomial( x, coefficients(n) ); 3816 3817 var a = div( n, -2 ); 3818 var b = div( sub(1,n), 2 ); 3819 3820 var s = sub( div( hypergeometric1F1( a, .5, pow(x,2) ), gamma( b ) ), 3821 mul( 2, x, div( hypergeometric1F1( b, 1.5, pow(x,2) ), gamma( a ) ) ) ); 3822 3823 return mul( pow(2,n), sqrt(pi), s ); 3824 3825 } 3826 3827 if ( isPositiveIntegerOrZero(n) ) return polynomial( x, coefficients(n) ); 3828 3829 var s = hypergeometric1F1( -n/2, .5, x**2 ) / gamma( (1-n)/2 ) 3830 - 2 * x * hypergeometric1F1( (1-n)/2, 1.5, x**2 ) / gamma( -n/2 ); 3831 3832 return 2**n * sqrt(pi) * s; 3833 3834} 3835 3836 3837function laguerre( n, a, x ) { 3838 3839 // explict recursion unnecessary: hypergeometric series handles integers 3840 3841 if ( arguments.length === 2 ) { 3842 x = a; 3843 return hypergeometric1F1( neg(n), 1, x ); 3844 } 3845 3846 return mul( binomial( add(n,a), n ), hypergeometric1F1( neg(n), add(a,1), x ) ); 3847 3848} 3849 3850 3851function chebyshevT( n, x ) { 3852 3853 return cos( mul( n, arccos(x) ) ); 3854 3855} 3856 3857function chebyshevU( n, x ) { 3858 3859 return div( sin( mul( add(n,1), arccos(x) ) ), sin( arccos(x) ) ); 3860 3861} 3862 3863 3864function legendreP( l, m, x, renormalized=false ) { 3865 3866 if ( arguments.length === 2 ) { 3867 x = m; 3868 return hypergeometric2F1( neg(l), add(l,1), 1, div(sub(1,x),2) ); 3869 } 3870 3871 if ( isZero(m) ) return legendreP( l, x ); 3872 3873 if ( Number.isInteger(l) && Number.isInteger(m) && Math.abs(x) <= 1 ) { 3874 3875 var mm = Math.abs(m); 3876 if ( mm > l ) throw Error( 'Invalid spherical harmonic indices' ); 3877 3878 if ( !renormalized ) { 3879 var norm = 1; 3880 for ( var i = l-m+1 ; i <= l+m ; i++ ) norm *= i; 3881 norm = Math.sqrt( 4 * pi * norm / (2*l+1) ); 3882 } 3883 3884 var legendre1 = (-1)**mm * Math.sqrt( (2*mm+1) / 4 / pi / factorial(2*mm) ) 3885 * factorial2( 2*mm-1 ) * ( 1 - x*x )**(mm/2); 3886 3887 if ( mm === l ) 3888 if ( renormalized ) return legendre1; 3889 else return norm * legendre1; 3890 3891 var ll = mm + 1; 3892 var factor1 = Math.sqrt( 2*mm+3 ); 3893 var legendre2 = factor1 * x * legendre1; 3894 3895 if ( ll === l ) 3896 if ( renormalized ) return legendre2; 3897 else return norm * legendre2; 3898 3899 while ( ll < l ) { 3900 ll++ 3901 var factor2 = Math.sqrt( ( 4*ll*ll - 1 ) / ( ll*ll - mm*mm ) ); 3902 var legendre3 = factor2 * ( x*legendre2 - legendre1/factor1 ); 3903 legendre1 = legendre2; 3904 legendre2 = legendre3; 3905 factor1 = factor2; 3906 } 3907 3908 if ( renormalized ) return legendre3; 3909 else return norm * legendre3; 3910 3911 } 3912 3913 // dlmf.nist.gov/14.3.5 3914 if ( isPositiveInteger(m) ) 3915 return mul( pow(-1,m), inv( gamma( add(m,1) ) ), 3916 gamma( add(l,m,1) ), inv( gamma( add(l,neg(m),1) ) ), 3917 pow( add(1,x), div(m,-2) ), pow( sub(1,x), div(m,2) ), 3918 hypergeometric2F1( neg(l), add(l,1), add(m,1), div(sub(1,x),2) ) ); 3919 3920 return mul( inv( gamma( sub(1,m) ) ), 3921 pow( add(1,x), div(m,2) ), pow( sub(1,x), div(m,-2) ), 3922 hypergeometric2F1( neg(l), add(l,1), sub(1,m), div(sub(1,x),2) ) ); 3923 3924} 3925 3926function sphericalHarmonic( l, m, theta, phi, condonShortley=true ) { 3927 3928 var norm = sqrt( mul( div( add(mul(2,l),1), 4*pi ), 3929 div( factorial(sub(l,m)), factorial(add(l,m)) ) ) ); 3930 3931 var cs = condonShortley ? pow(-1,m) : 1; 3932 3933 return mul( cs, norm, legendreP( l, m, cos(theta) ), exp( complex(0,m*phi) ) ); 3934 3935} 3936 3937function legendreQ( l, m, x ) { 3938 3939 if ( arguments.length === 2 ) { 3940 x = m; 3941 m = 0; 3942 } 3943 3944 function difference( t ) { 3945 3946 var t1 = mul( cos( mul(pi,t) ), legendreP(l,t,x) ); 3947 3948 var t2 = mul( gamma( add(l,t,1) ), inv( gamma( add(l,neg(t),1) ) ), legendreP(l,neg(t),x) ); 3949 3950 return sub( t1, t2 ); 3951 3952 } 3953 3954 // l'Hopital's rule decent for small m, more accurate might be 3955 // functions.wolfram.com/HypergeometricFunctions/LegendreQ2General/26/01/02/0005/ 3956 3957 if ( isInteger(m) ) { 3958 3959 // legendreP is pure real outside unit circle for even integers 3960 if ( abs(x) > 1 && !isComplex(m) && !( m & 1 ) ) m = complex(m); 3961 3962 return mul( .5, pow(-1,m), diff( t => difference(t), m ) ); 3963 3964 } 3965 3966 return mul( pi/2, inv( sin(mul(pi,m)) ), difference(m) ); 3967 3968} 3969 3970 3971function jacobiP( n, a, b, x ) { 3972 3973 return mul( binomial( add(n,a), n ), 3974 hypergeometric2F1( neg(n), add(n,a,b,1), add(a,1), div(sub(1,x),2) ) ); 3975 3976} 3977 3978 3979// This file contains proprietary functions defined on analyticphysics.com 3980// Before each is the title of a presentation describing the function 3981 3982 3983// A Generalized Lambert Function of Two Arguments 3984 3985function doubleLambert( n, x, y, tolerance=1e-10 ) { 3986 3987 if ( arguments.length === 2 ) { 3988 y = x; 3989 x = n; 3990 n = 0; 3991 } 3992 3993 if ( isZero(y) ) return lambertW(n,x); 3994 if ( isZero(x) ) return neg( lambertW( -n, neg(y) ) ); 3995 3996 function asymptotic( n, x, y ) { 3997 3998 return add( log(sqrt(x)), neg(log(sqrt(neg(y)))), complex(0,n*pi) ); 3999 4000 } 4001 4002 function test( n, x, y ) { 4003 4004 return div( asymptotic(n,x,y), add( 1, mul(2*(-1)**n,sqrt(x),sqrt(neg(y))) ) ); 4005 4006 } 4007 4008 function start( n, x, y, tolerance=1e-3 ) { 4009 4010 var a = asymptotic(n,x,y), w1, w2; 4011 4012 var testValue = .9; 4013 4014 if ( abs(test(n,x,y)) < testValue ) return a; 4015 4016 if ( n === 0 ) console.log( 'Using Lambert W on principal branch' ); 4017 4018 if ( n & 1 ) { 4019 4020 if ( n > 0 ) { 4021 w1 = lambertW( (n+1)/2, x, tolerance ); 4022 w2 = neg( lambertW( -(n+1)/2, neg(y), tolerance ) ); 4023 } else { 4024 w1 = lambertW( (n-1)/2, x, tolerance ); 4025 w2 = neg( lambertW( -(n-1)/2, neg(y), tolerance ) ); 4026 } 4027 4028 } else { 4029 4030 w1 = lambertW( n/2, x, tolerance ); 4031 w2 = neg( lambertW( -n/2, neg(y), tolerance ) ); 4032 4033 } 4034 4035 if ( Math.abs( w1.im - a.im ) < Math.abs( w2.im - a.im ) ) 4036 return w1; 4037 else 4038 return w2; 4039 4040 } 4041 4042 var maxIter = 100; 4043 var root = start(n,x,y); 4044 4045 for ( var i = 0; i < maxIter ; i++ ) { 4046 var N = add( mul(x,exp(neg(root))), mul(y,exp(root)), neg(root) ); 4047 var D = add( mul(-1,x,exp(neg(root))), mul(y,exp(root)), -1 ); 4048 var delta = div( N, D ); 4049 root = sub( root, delta ); 4050 if ( abs(delta) < tolerance ) return root; 4051 } 4052 4053 throw Error( 'No double Lambert root found for x = ' + JSON.stringify(x) 4054 + ' and y = ' + JSON.stringify(y) ); 4055 4056} 4057 4058 4059// complex circular functions 4060 4061function sin( x ) { 4062 4063 if ( isArbitrary(x) ) { 4064 4065 if ( isComplex(x) ) 4066 4067 return { re: mul( sin(x.re), cosh(x.im) ), 4068 im: mul( cos(x.re), sinh(x.im) ) }; 4069 4070 x = x % twoPi; 4071 4072 // reduce to [-pi/2,pi/2] with successive reductions 4073 if ( x > halfPi ) return sin( onePi - x ); 4074 if ( x < -halfPi ) return sin( -onePi - x ); 4075 4076 var s = x; 4077 var p = x; 4078 var i = arb2; 4079 4080 while ( p !== 0n ) { 4081 p = div( mul( p, -arb1, x, x ), mul( i, i + arb1 ) ); 4082 s += p; 4083 i += arb2; 4084 } 4085 4086 return s; 4087 4088 } 4089 4090 if ( isComplex(x) ) 4091 4092 return { re: Math.sin(x.re) * Math.cosh(x.im), 4093 im: Math.cos(x.re) * Math.sinh(x.im) }; 4094 4095 return Math.sin(x); 4096 4097} 4098 4099function cos( x ) { 4100 4101 if ( isArbitrary(x) ) { 4102 4103 if ( isComplex(x) ) 4104 4105 return { re: mul( cos(x.re), cosh(x.im) ), 4106 im: -mul( sin(x.re), sinh(x.im) ) }; 4107 4108 x = x % twoPi; 4109 4110 // reduce to [-pi/2,pi/2] with successive reductions 4111 if ( x > halfPi ) return -cos( onePi - x ); 4112 if ( x < -halfPi ) return -cos( -onePi - x ); 4113 4114 var s = arb1; 4115 var p = arb1; 4116 var i = arb1; 4117 4118 while ( p !== 0n ) { 4119 p = div( mul( p, -arb1, x, x ), mul( i, i + arb1 ) ); 4120 s += p; 4121 i += arb2; 4122 } 4123 4124 return s; 4125 4126 } 4127 4128 if ( isComplex(x) ) 4129
4130 return { re: Math.cos(x.re) * Math.cosh(x.im), 4131 im: -Math.sin(x.re) * Math.sinh(x.im) }; 4132 4133 return Math.cos(x); 4134 4135} 4136 4137function tan( x ) { 4138 4139 if ( isComplex(x) ) return div( sin(x), cos(x) ); 4140 4141 return Math.tan(x); 4142 4143 } 4144 4145function cot( x ) { 4146 4147 if ( isComplex(x) ) return div( cos(x), sin(x) ); 4148 4149 return 1 / Math.tan(x); 4150 4151} 4152 4153function sec( x ) { 4154 4155 if ( isComplex(x) ) return div( 1, cos(x) ); 4156 4157 return 1 / Math.cos(x); 4158 4159} 4160 4161function csc( x ) { 4162 4163 if ( isComplex(x) ) return div( 1, sin(x) ); 4164 4165 return 1 / Math.sin(x); 4166 4167} 4168 4169 4170// inverse circular functions 4171 4172function arcsin( x ) { 4173 4174 if ( isComplex(x) ) { 4175 4176 var s = sqrt( sub( 1, mul( x, x ) ) ); 4177 s = add( mul( complex(0,1), x ), s ); 4178 return mul( complex(0,-1), log( s ) ); 4179 4180 } 4181 4182 if ( Math.abs(x) <= 1 ) return Math.asin(x); 4183 4184 return arcsin( complex(x) ); 4185 4186} 4187 4188function arccos( x ) { 4189 4190 if ( isComplex(x) ) { 4191 4192 return sub( pi/2, arcsin(x) ); 4193 4194 } 4195 4196 if ( Math.abs(x) <= 1 ) return Math.acos(x); 4197 4198 return arccos( complex(x) ); 4199 4200} 4201 4202function arctan( x ) { 4203 4204 if ( isComplex(x) ) { 4205 4206 var s = sub( log( sub( 1, mul( complex(0,1), x ) ) ), 4207 log( add( 1, mul( complex(0,1), x ) ) ) ); 4208 return mul( complex(0,.5), s ); 4209 4210 } 4211 4212 return Math.atan(x); 4213 4214} 4215 4216function arccot( x ) { 4217 4218 if ( isComplex(x) ) return arctan( div( 1, x ) ); 4219 4220 return Math.atan( 1/x ); 4221 4222} 4223 4224function arcsec( x ) { 4225 4226 if ( isComplex(x) ) return arccos( div( 1, x ) ); 4227 4228 if ( Math.abs(x) >= 1 ) return Math.acos( 1/x ); 4229 4230 return arcsec( complex(x) ); 4231 4232} 4233 4234function arccsc( x ) { 4235 4236 if ( isComplex(x) ) return arcsin( div( 1, x ) ); 4237 4238 if ( Math.abs(x) >= 1 ) return Math.asin( 1/x ); 4239 4240 return arccsc( complex(x) ); 4241 4242} 4243 4244 4245// complex hyperbolic functions 4246 4247function sinh( x ) { 4248 4249 if ( isArbitrary(x) ) return div( sub( exp(x), exp(neg(x)) ), arb2 ); 4250 4251 if ( isComplex(x) ) 4252 4253 return { re: Math.sinh(x.re) * Math.cos(x.im), 4254 im: Math.cosh(x.re) * Math.sin(x.im) }; 4255 4256 return Math.sinh(x); 4257 4258} 4259 4260function cosh( x ) { 4261 4262 if ( isArbitrary(x) ) return div( add( exp(x), exp(neg(x)) ), arb2 ); 4263 4264 if ( isComplex(x) ) 4265
4266 return { re: Math.cosh(x.re) * Math.cos(x.im), 4267 im: Math.sinh(x.re) * Math.sin(x.im) }; 4268 4269 return Math.cosh(x); 4270 4271} 4272 4273function tanh( x ) { 4274 4275 if ( isComplex(x) ) return div( sinh(x), cosh(x) ); 4276 4277 return Math.tanh(x); 4278 4279} 4280 4281function coth( x ) { 4282 4283 if ( isComplex(x) ) return div( cosh(x), sinh(x) ); 4284 4285 return 1 / Math.tanh(x); 4286 4287} 4288 4289function sech( x ) { 4290 4291 if ( isComplex(x) ) return div( 1, cosh(x) ); 4292 4293 return 1 / Math.cosh(x); 4294 4295} 4296 4297function csch( x ) { 4298 4299 if ( isComplex(x) ) return div( 1, sinh(x) ); 4300 4301 return 1 / Math.sinh(x); 4302 4303} 4304 4305 4306// inverse hyperbolic functions 4307 4308function arcsinh( x ) { 4309 4310 if ( isComplex(x) ) { 4311 4312 var s = sqrt( add( mul( x, x ), 1 ) ); 4313 s = add( x, s ); 4314 return log( s ); 4315 4316 } 4317 4318 return Math.asinh(x); 4319 4320} 4321 4322function arccosh( x ) { 4323 4324 if ( isComplex(x) ) { 4325 4326 var s = mul( sqrt( add( x, 1 ) ), sqrt( sub( x, 1 ) ) ); 4327 s = add( x, s ); 4328 return log( s ); 4329 4330 } 4331 4332 if ( x >= 1 ) return Math.acosh(x); 4333 4334 return arccosh( complex(x) ); 4335 4336} 4337 4338function arctanh( x ) { 4339 4340 if ( isComplex(x) ) { 4341 4342 var s = sub( log( add( 1, x ) ), log( sub( 1, x ) ) ); 4343 return mul( .5, s ); 4344 4345 } 4346 4347 if ( Math.abs(x) <= 1 ) return Math.atanh(x); 4348 4349 return arctanh( complex(x) ); 4350 4351} 4352 4353function arccoth( x ) { 4354 4355 if ( isComplex(x) ) { 4356 4357 if ( x.re === 0 && x.im === 0 ) throw Error( 'Indeterminate arccoth value' ); 4358 4359 return arctanh( div( 1, x ) ); 4360 4361 } 4362 4363 if ( Math.abs(x) > 1 ) return Math.atanh( 1/x ); 4364 4365 return arccoth( complex(x) ); 4366 4367} 4368 4369function arcsech( x ) { 4370 4371 if ( isComplex(x) ) { 4372 4373 if ( x.re === 0 && x.im === 0 ) throw Error( 'Indeterminate arcsech value' ); 4374 4375 // adjust for branch cut along negative axis 4376 if ( x.im === 0 ) x.im = -Number.MIN_VALUE; 4377 4378 return arccosh( div( 1, x ) ); 4379 4380 } 4381 4382 if ( x > 0 && x < 1 ) return Math.acosh( 1/x ); 4383 4384 return arcsech( complex(x) ); 4385 4386} 4387 4388function arccsch( x ) { 4389 4390 if ( isComplex(x) ) { 4391 4392 return arcsinh( div( 1, x ) ); 4393 4394 } 4395 4396 return Math.asinh( 1/x ); 4397 4398} 4399 4400 4401// miscellaneous 4402 4403function sinc( x ) { 4404 4405 if ( isComplex(x) ) { 4406 4407 if ( x.re === 0 && x.im === 0 ) return complex(1); 4408 4409 return div( sin(x), x ); 4410 4411 } 4412 4413 if ( x === 0 ) return 1; 4414 4415 return Math.sin(x) / x; 4416 4417} 4418 4419function haversine( x ) { return div( sub( 1, cos(x) ), 2 ); } 4420 4421function inverseHaversine( x ) { return arccos( sub( 1, mul(2,x) ) ); } 4422 4423 4424// analyticphysics.com / The Complex Gudermannian Function 4425 4426function gudermannian( x ) { return mul( 2, arctan( tanh( div(x,2) ) ) ); } 4427 4428function inverseGudermannian( x ) { return mul( 2, arctanh( tan( div(x,2) ) ) ); } 4429 4430 4431function zeta( x, tolerance=1e-10 ) { 4432 4433 if ( isUnity(x) ) throw Error( 'Riemann zeta pole' ); 4434 4435 // functional equation dlmf.nist.gov/25.4.2 - connects both half planes 4436 if ( x < 0 || x.re < 0 ) 4437 return mul( pow(2,x), pow(pi,sub(x,1)), sin( mul(pi/2,x) ), gamma( sub(1,x) ), zeta( sub(1,x) ) ); 4438 4439 // direct summation more accurate in right-hand plane 4440 var directSummation = 5; 4441 4442 if ( x > directSummation || x.re > directSummation ) { 4443 4444 if ( isComplex(x) ) { 4445 4446 var s = complex(1); 4447 var p = complex(1); 4448 var i = 2; 4449 4450 while ( Math.abs(p.re) > tolerance || Math.abs(p.im) > tolerance*tolerance ) { 4451 p = pow( i, neg(x) ); 4452 s = add( s, p ); 4453 i++; 4454 } 4455 4456 return s; 4457 4458 } else { 4459 4460 var s = 1; 4461 var p = 1; 4462 var i = 2; 4463 4464 while ( Math.abs(p) > tolerance ) { 4465 p = 1 / i**x; 4466 s += p; 4467 i++; 4468 } 4469 4470 return s; 4471 4472 } 4473 4474 } 4475 4476 // Borwein, Efficient Algorithm 4477 4478 var n = 14; // from error bound for tolerance 4479 4480 if ( isComplex(x) && x.im !== 0 ) 4481 n = Math.max( n, Math.ceil( log( 2 / abs(gamma(x)) / tolerance ) / log( 3 + sqrt(8) ) ) ); 4482 4483 var d = [ 1 ], prod = n; 4484 for ( var i = 1 ; i <= n ; i++ ) { 4485 d.push( d[i-1] + n * prod / factorial( 2*i ) * 4**i ); 4486 // manually evaluate factorial( n+i-1 ) / factorial( n-i ) to avoid overflow 4487 prod *= (n+i)*(n-i); 4488 } 4489 4490 if ( isComplex(x) ) { 4491 4492 var s = summation( k => div( (-1)**k * ( d[k] - d[n] ), pow( k+1, x ) ), [0,n-1] ); 4493 4494 return div( div( s, -d[n] ), sub( 1, pow( 2, sub(1,x) ) ) ); 4495 4496 } else { 4497 4498 var s = summation( k => (-1)**k * ( d[k] - d[n] ) / (k+1)**x, [0,n-1] ); 4499 4500 return -s / d[n] / ( 1 - 2**(1-x) ); 4501 4502 } 4503 4504} 4505 4506function dirichletEta( x ) { return mul( zeta(x), sub( 1, pow( 2, sub(1,x) ) ) ); } 4507 4508function riemannXi( x ) { 4509 4510 if ( isZero(x) || isUnity(x) ) return isComplex(x) ? complex(.5) : .5; 4511 4512 var half = div( x, 2 ); 4513 4514 return mul( half, sub(x,1), pow( pi, neg(half) ), gamma(half), zeta(x) ); 4515 4516} 4517 4518 4519function bernoulli( n, x ) { 4520 4521 if ( arguments.length === 2 ) { 4522 4523 if ( isZero(n) ) return isComplex(n) || isComplex(x) ? complex(1) : 1; 4524 4525 // avoid Hurwitz zeta parameter poles 4526 if ( isNegativeIntegerOrZero(x) ) return complexAverage( x
4526=> bernoulli(n,x), x ); 4527 4528 return mul( neg(n), hurwitzZeta( sub(1,n), x ) ); 4529 4530 } 4531 4532 if ( Number.isInteger(n) && n >= 0 ) { 4533 4534 if ( n === 0 ) return 1; 4535 4536 if ( n === 1 ) return -.5; 4537 4538 if ( n & 1 ) return 0; 4539 4540 var m = n/2; 4541 if ( m <= bernoulli2nN.length ) 4542 return arbitrary( div( bernoulli2nN[m], bernoulli2nD[m] ) ); 4543 4544 return -n * zeta(1-n); 4545 4546 } 4547 4548 if ( isPositiveIntegerOrZero(n) ) return complex( bernoulli( n.re ) ); 4549 4550 // generalized Bernoulli number 4551 console.log( 'Returning generalized Bernoulli number' ); 4552 return bernoulli( n, 0 ); 4553 4554} 4555 4556function harmonic( n, r ) { 4557 4558 if ( arguments.length === 2 ) { 4559 4560 if ( !Number.isInteger(n) || isComplex(r) ) 4561 return sub( zeta(r), hurwitzZeta( r, add(n,1) ) ); 4562 4563 return summation( i => 1/i**r, [1,n] ); 4564 4565 } 4566 4567 if ( !Number.isInteger(n) ) return add( digamma(add(n,1)), eulerGamma ); 4568 4569 if ( n > 1e3 ) return log(n) + eulerGamma + 1/2/n - 1/12/n**2; 4570 4571 return summation( i => 1/i, [1,n] ); 4572 4573} 4574 4575 4576function hurwitzZeta( x, a, tolerance=1e-10 ) { 4577 4578 if ( isUnity(x) ) throw Error( 'Hurwitz zeta pole' ); 4579 4580 if ( isNegativeIntegerOrZero(a) ) throw Error( 'Hurwitz zeta parameter pole' ); 4581 4582 if ( isComplex(x) || isComplex(a) ) { 4583 4584 if ( !isComplex(x) ) x = complex(x); 4585 if ( !isComplex(a) ) a = complex(a); 4586 4587 // direct summation more accurate than dlmf.nist.gov/25.11.4 for positive a 4588 4589 if ( a.re < 0 ) { 4590 var m = -Math.floor(a.re); 4591 return add( hurwitzZeta(x,add(a,m)), summation( i => pow( add(a,i), neg(x) ), [0,m-1] ) ); 4592 } 4593
4594 // Johansson arxiv.org/abs/1309.2877 4595 4596 var n = 15; // recommendation of Vepstas, Efficient Algorithm, p.12 4597 4598 // Euler-Maclaurin has differences of large values in left-hand plane 4599 var useArbitrary = x.re < 0; 4600 4601 if ( useArbitrary ) { 4602 4603 setPrecisionScale( 20 - Math.round(x.re) ); 4604 4605 x = arbitrary(x), a = arbitrary(a); 4606 var arbN = arbitrary(n), arb3 = arbitrary(3); 4607 4608 // at first sight including the factor (a+N)**x in this sum 4609 // appears to be unnecessary calculational overhead 4610 // turns out to be more accurate by keeping it on a comparable 4611 // level of numerical precision as the other two terms 4612 4613 var S = 0n; 4614 for ( var i = 0 ; i < n ; i++ ) 4615 S = add( S, pow( div( add(a,arbN), add(a,arbitrary(i)) ), x ) ); 4616 4617 var I = div( add(a,arbN), sub(x,arb1) ); 4618 4619 var p = div( mul( arb1/2n, x ), add(a,arbN) ); 4620 var b = div( bernoulli2nN[1], bernoulli2nD[1] ); 4621 var t = mul( b, p ); 4622 var i = arb2; 4623 var j = 2; 4624 4625 while ( p.re !== 0n || p.im !== 0n ) { 4626 if ( j === bernoulli2nN.length ) break; 4627 p = div( mul( p, add( x, 2n*i - arb2 ), add( x, 2n*i - arb3 ) ), 4628 mul( 2n*i, 2n*i - arb1, add(a,arbN), add(a,arbN) ) ); 4629 b = div( bernoulli2nN[j], bernoulli2nD[j] ); 4630 t = add( t, mul( b, p ) ); 4631 i += arb1; 4632 j++; 4633 } 4634 4635 var T = add( arb1/2n, t ); 4636 4637 var result = arbitrary( mul( add( S, I, T ), pow( add(a,arbN), mul(-arb1,x) ) ) ); 4638 4639 resetPrecisionScale(); 4640 4641 return result; 4642 4643 } 4644 4645 var S = summation( i => pow( add(a,i), neg(x) ), [0,n-1] ); 4646 4647 var I = div( pow( add(a,n), sub(1,x) ), sub(x,1) ); 4648 4649 var p = mul( .5, x, inv(add(a,n)) ); 4650 var t = mul( bernoulli(2), p ); 4651 var i = 2; 4652 4653 // converges rather quickly 4654 while ( Math.abs(p.re) > tolerance || Math.abs(p.im) > tolerance ) { 4655 p = div( mul( p, add( x, 2*i - 2 ), add( x, 2*i - 3 ) ), 4656 mul( 2*i * (2*i-1), add(a,n), add(a,n) ) ); 4657 t = add( t, mul( bernoulli(2*i), p ) ); 4658 i++; 4659 } 4660 4661 var T = div( add( .5, t ), pow( add(a,n), x ) ); 4662 4663 return add( S, I, T ); 4664 4665 } else { 4666 4667 if ( a < 0 ) return hurwitzZeta( x, complex(a) ); 4668 4669 // direct summation more accurate than dlmf.nist.gov/25.11.4 4670 4671 // Euler-Maclaurin has differences of large values in left-hand plane 4672 // switch to different summation: dlmf.nist.gov/25.11.9 4673 4674 var switchForms = -5; 4675 4676 if ( x < switchForms ) { 4677 4678 x = 1 - x; 4679 var t = Math.cos( pi*x/2 - 2*pi*a ); 4680 var s = t; 4681 var i = 1; 4682 4683 while ( Math.abs(t) > tolerance ) { 4684 i++; 4685 t = Math.cos( pi*x/2 - 2*i*pi*a ) / i**x; 4686 s += t; 4687 } 4688 4689 return 2 * gamma(x) / (2*pi)**x * s; 4690 4691 } 4692
4693 // Johansson arxiv.org/abs/1309.2877 4694 4695 var n = 15; // recommendation of Vepstas, Efficient Algorithm, p.12 4696 4697 var S = summation( i => 1 / (a+i)**x, [0,n-1] ); 4698 4699 var I = (a+n)**(1-x) / (x-1); 4700 4701 var p = x / 2 / (a+n); 4702 var t = bernoulli(2) * p; 4703 var i = 2; 4704 4705 // converges rather quickly 4706 while ( Math.abs(p) > tolerance ) { 4707 p *= ( x + 2*i - 2 ) * ( x + 2*i - 3 ) / ( 2*i * (2*i-1) * (a+n)**2 ); 4708 t += bernoulli(2*i) * p; 4709 i++; 4710 } 4711 4712 var T = ( .5 + t ) / (a+n)**x; 4713 4714 return S + I + T; 4715 4716 } 4717 4718} 4719 4720 4721function polylog( n, x, tolerance=1e-10 ) { 4722 4723 if ( isEqualTo(x,1) ) return zeta(n); 4724 4725 if ( isEqualTo(x,-1) ) return neg( dirichletEta(n) ); 4726 4727 if ( isEqualTo(n,1) ) return neg( log( sub(1,x) ) ); 4728 4729 if ( isEqualTo(n,0) ) return div( x, sub(1,x) ); 4730 4731 if ( isEqualTo(n,-1) ) return div( x, mul( sub(1,x), sub(1,x) ) ); 4732 4733 if ( abs(x) > 1 ) { 4734 4735 var twoPiI = complex(0,2*pi); 4736 4737 if ( isPositiveInteger(n) ) { 4738 4739 // Crandall, Note on Fast Polylogarithm Computation 4740 4741 var t1 = mul( (-1)**n, polylog( n, inv(x) ) ); 4742 4743 var t2 = mul( div( pow(twoPiI,n), factorial(n) ), bernoulli( n, div(log(x),twoPiI) ) ); 4744 4745 var y = isComplex(x) ? x : complex(x); // just for test 4746 var t3 = y.im < 0 || ( y.im === 0 && y.re >= 1 ) ? 4747 mul( twoPiI, div( pow(log(x),n-1), factorial(n-1) ) ) : 0; 4748 4749 var result = neg( add( t1, t2, t3 ) ); 4750 4751 // real on negative real axis 4752 if ( !isComplex(x) && x < 0 ) return result.re; 4753 4754 return result; 4755 4756 } 4757 4758 var v = sub(1,n); 4759 var I = complex(0,1); 4760 var L = div( log(neg(x)), twoPiI ); 4761 4762 var z1 = mul( pow(I,v), hurwitzZeta( v, add(.5,L) ) ); 4763 var z2 = mul( pow(I,neg(v)), hurwitzZeta( v, sub(.5,L) ) ); 4764 4765 return mul( gamma(v), pow(2*pi,neg(v)), add(z1,z2) ) 4766 4767 } 4768 4769 if ( isComplex(n) || isComplex(x) ) { 4770 4771 var s = x; 4772 var p = complex(1); 4773 var i = 2; 4774 4775 while ( Math.abs(p.re) > tolerance || Math.abs(p.im) > tolerance ) { 4776 p = div( pow(x,i), pow(i,n) ); 4777 s = add( s, p ); 4778 i++; 4779 } 4780 4781 return s; 4782 4783 } else { 4784 4785 var s = x; 4786 var p = 1; 4787 var i = 2; 4788 4789 while ( Math.abs(p) > tolerance ) { 4790 p = x**i / i**n; 4791 s += p; 4792 i++; 4793 } 4794 4795 return s; 4796 4797 } 4798 4799} 4800 4801 4802function ode( f, y, [x0,x1], step=.001, method='runge-kutta' ) { 4803 4804 if ( x1 < x0 ) { 4805 var compare = x => x >= x1; 4806 step *= -1; 4807 } else 4808 var compare = x => x <= x1; 4809 4810 // vectorizing first-order real equation works because +[1] = 1 4811 // for complex case +[C(1)] = NaN, so explicit array references 4812 // are necessary in the input function 4813 4814 if ( !Array.isArray(y) ) { 4815 var g = f; 4816 f = (x,y) => [ g(x,y) ]; 4817 y = [ y ]; 4818 } 4819 4820 // preparation for complex system 4821 if ( isComplex(x0) || isComplex(x1) || y.some( e => isComplex(e) ) 4822 || f(x0,y).some( e => isComplex(e) ) ) { 4823 4824 if ( !isComplex(x0) ) x0 = complex(x0); 4825 4826 y.forEach( (e,i,a) => { if ( !isComplex(e) ) a[i] = complex(e); } ); 4827 4828 if ( f(x0,y).every( e => !isComplex(e) ) ) 4829 throw Error( 'All functions must handle complex math' ); 4830 4831 var d = sub(x1,x0), absD = abs(d); 4832 step = mul( step, div( d, absD ) ); 4833 var steps = Math.trunc( absD / abs(step) ), currentStep = 0; 4834 4835 } 4836 4837 var points = [ [x0].concat(y) ]; 4838 var size = y.length; 4839 4840 switch( method ) { 4841 4842 case 'euler': 4843 4844 if ( isComplex(x0) ) { 4845 4846 for ( var x = add(x0,step) ; currentStep < steps ; x = add(x,step) ) { 4847 4848 var k = f(x,y); 4849 4850 for ( var i = 0 ; i < size ; i++ ) y[i] = add( y[i], mul( k[i], step ) ); 4851 4852 points.push( [x].concat(y) ); 4853 4854 currentStep++; 4855 4856 } 4857 4858 return points; 4859 4860 } else { 4861 4862 for ( var x = x0+step ; compare(x) ; x += step ) { 4863 4864 var k = f(x,y); 4865 4866 for ( var i = 0 ; i < size ; i++ ) y[i] += k[i] * step; 4867 4868 points.push( [x].concat(y) ); 4869 4870 } 4871 4872 return points; 4873 4874 } 4875 4876 case 'runge-kutta': 4877 4878 if ( isComplex(x0) ) { 4879 4880 var halfStep = div( step, 2 ); 4881 4882 for ( var x = add(x0,step) ; currentStep < steps ; x = add(x,step) ) { 4883 4884 var y1 = [], y2 = [], y3 = []; 4885 4886 var k1 = f(x,y); 4887 for ( var i = 0 ; i < size ; i++ ) y1.push( add( y[i], mul( k1[i], halfStep ) ) ); 4888 var k2 = f( add( x, halfStep ), y1 ); 4889 for ( var i = 0 ; i < size ; i++ ) y2.push( add( y[i], mul( k2[i], halfStep ) ) ); 4890 var k3 = f( add( x, halfStep ), y2 ); 4891 for ( var i = 0 ; i < size ; i++ ) y3.push( add( y[i], mul( k3[i], step ) ) ); 4892 var k4 = f( add( x, step ), y3 ); 4893 4894 for ( var i = 0 ; i < size ; i++ ) 4895 y[i] = add( y[i], mul( add( k1[i], mul(2,k2[i]), mul(2,k3[i]), k4[i] ), step, 1/6 ) ); 4896 4897 points.push( [x].concat(y) ); 4898 4899 currentStep++; 4900 4901 } 4902 4903 return points; 4904 4905 } else { 4906 4907 for ( var x = x0+step ; compare(x) ; x += step ) { 4908 4909 var y1 = [], y2 = [], y3 = []; 4910 4911 var k1 = f(x,y); 4912 for ( var i = 0 ; i < size ; i++ ) y1.push( y[i] + k1[i]*step/2 ); 4913 var k2 = f( x+step/2, y1 ); 4914 for ( var i = 0 ; i < size ; i++ ) y2.push( y[i] + k2[i]*step/2 ); 4915 var k3 = f( x+step/2, y2 ); 4916 for ( var i = 0 ; i < size ; i++ ) y3.push( y[i] + k3[i]*step ); 4917 var k4 = f( x+step, y3 ); 4918 4919 for ( var i = 0 ; i < size ; i++ ) 4920 y[i] += ( k1[i] + 2*k2[i] + 2*k3[i] + k4[i] ) * step / 6; 4921 4922 points.push( [x].concat(y) ); 4923 4924 } 4925 4926 return points; 4927 4928 } 4929 4930 default: 4931 4932 throw Error( 'Unsupported differential equation solver method' ); 4933 4934 } 4935 4936} 4937 4938 4939function diff( f, x, n=1, options={} ) { 4940 4941 if ( !Number.isInteger(n) || n < 0 ) 4942 throw Error( 'Only positive integer orders for differentiation' ); 4943 4944 if ( isComplex(x) || isComplex(f(x)) ) { 4945 4946 if ( !isComplex(f(x)) ) throw Error( 'Function must handle complex math' ); 4947 4948 var absX = abs(x); 4949 var normed = absX === 0 ? complex(1) : div( x, absX ); 4950 4951 var real = diff( t => f( mul(normed,t) ).re, absX, n, options ); 4952 var imag = diff( t => f( mul(normed,t) ).im, absX, n, options ); 4953 4954 return div( complex( real, imag ), pow(normed,n) ); 4955 4956 } 4957 4958 var method = 'method' in options ? options.method : 'ridders'; 4959 var epsilon = 'epsilon' in options ? options.epsilon : 1e-5; 4960
4961 // central differences have h**2 error but division 4962 // by h**n increases roundoff error 4963 // step sizes chosen as epsilon**(1/(n+2)) to minimize error 4964 4965 function difference() { 4966 4967 var s = 0; 4968 for ( var i = 0 ; i <= n ; i++ ) 4969 s += (-1)**i * binomial(n,i) * f( x + (n-2*i)*h ); 4970 4971 return s / (2*h)**n 4972 4973 } 4974 4975 switch( method ) { 4976 4977 case 'naive': 4978 4979 // only accurate for first couple derivatives 4980 epsilon /= 1000; 4981 var h = epsilon**(1/(n+2)); 4982 return difference(); 4983 4984 case 'ridders': 4985 4986 var h = epsilon**(1/(n+2)); 4987 var error = Number.MAX_VALUE; 4988 var maxIter = 10; 4989 var result; 4990 4991 var d = []; 4992 for ( var i = 0 ; i < maxIter ; i++ ) d.push( [] ); 4993 4994 // Richardson extrapolation as per C. Ridders 4995 d[0][0] = difference(); 4996 4997 for ( var i = 1 ; i < maxIter ; i++ ) { 4998 4999 h /= 2; 5000 d[0][i] = difference(); 5001 5002 for ( var j = 1 ; j <= i ; j++ ) { 5003 5004 d[j][i] = ( 4**j * d[j-1][i] - d[j-1][i-1] ) / ( 4**j - 1 ); 5005 5006 var delta = Math.max( Math.abs( d[j][i] - d[j-1][i] ), 5007 Math.abs( d[j][i] - d[j-1][i-1] ) ); 5008 if ( delta <= error ) { 5009 error = delta; 5010 result = d[j][i]; 5011 } 5012 5013 } 5014 5015 if ( Math.abs( d[i][i] - d[i-1][i-1] ) > error ) break; 5016 5017 } 5018 5019 return result; 5020 5021 default: 5022 5023 throw Error( 'Unsupported differentiation method' ); 5024 5025 } 5026 5027} 5028 5029var D = diff; 5030 5031 5032function taylorSeries( f, x0, terms=5 ) { 5033 5034 var c = [ f(x0) ]; 5035 for ( var i = 1 ; i < terms ; i++ ) c.push( diff( f, x0, i ) ); 5036 5037 return function( x ) { 5038 5039 var s = 0; 5040 for ( var i = 0 ; i < c.length ; i++ ) 5041 s = add( s, mul( c[i], pow( sub(x,x0), i ), 1/factorial(i) ) ); 5042 return s; 5043 5044 } 5045 5046} 5047 5048 5049function gradient( f, point ) { 5050 5051 if ( f.length !== point.length ) throw Error( 'Gradient point length differs from function' ); 5052 5053 var result = []; 5054 5055 for ( var i = 0 ; i < point.length ; i++ ) { 5056 5057 var a = point.slice(); 5058 5059 result.push( diff( x => { a[i] = x; return f.apply( null, a ); }, a[i] ) ); 5060 5061 } 5062 5063 return result; 5064 5065} 5066 5067function findExtremum( f, point, options={} ) { 5068 5069 if ( !Array.isArray(point) ) point = [ point ]; 5070 5071 var sign = options.findMaximum ? 1 : -1; 5072 var tolerance = 'tolerance' in options ? options.tolerance : 1e-10; 5073 5074 var maxIter = 1e4; 5075 var gamma = .01 * sign; 5076 var grad, step, test; 5077 5078 for ( var i = 0 ; i < maxIter ; i++ ) { 5079 5080 grad = gradient( f, point ); 5081 test = true; 5082 5083 for ( var j = 0 ; j < point.length ; j++ ) { 5084 step = gamma * grad[j]; 5085 point[j] += step; 5086 test = test && step < tolerance; 5087 } 5088 5089 if ( test ) 5090 if ( point.length === 1 ) return point[0]; 5091 else return point; 5092 5093 } 5094 5095 throw Error( 'No extremum found for tolerance ' + tolerance ); 5096 5097} 5098 5099 5100function integrate( f, [a,b], options={} ) { 5101 5102 if ( isComplex(a) || isComplex(b) || isComplex(f(a)) ) { 5103 5104 if ( !isComplex(a) ) a = complex(a); 5105 if ( !isComplex(b) ) b = complex(b); 5106 5107 if ( a.re === b.re && a.im === b.im ) return complex(0); 5108 5109 if ( !isComplex(f(a)) ) throw Error( 'Function must handle complex math' ); 5110 5111 function lerp( t ) { return add( mul( sub(b,a), t ), a ); } 5112 5113 var real = integrate( t => f( lerp(t) ).re, [0,1], options ); 5114 var imag = integrate( t => f( lerp(t) ).im, [0,1], options ); 5115 5116 return mul( sub(b,a), complex( real, imag ) ); 5117 5118 } 5119 5120 var method = 'method' in options ? options.method : 'adaptive-simpson'; 5121 var tolerance = 'tolerance' in options ? options.tolerance : 1e-10; 5122 5123 if ( a === b ) return 0; 5124 5125 if ( options.avoidEndpoints ) 5126 if ( a < b ) { a += tolerance; b -= tolerance; } 5127 else { a -= tolerance; b += tolerance; } 5128 5129 function nextEulerIteration() { 5130 5131 h /= 2; 5132 var x = a + h; 5133 while ( x < b ) { 5134 // only add new function evaluations 5135 s += f(x); 5136 x += 2*h; 5137 } 5138 5139 } 5140 5141 switch( method ) { 5142 5143 case 'euler-maclaurin': 5144 5145 // Euler-Maclaurin summation formula 5146 5147 var maxIter = 50; 5148 5149 var h = ( b - a ) / 2; 5150 var s = ( f(a) + f(b) ) / 2 + f( (a+b)/2 ); 5151 var result = h * s; 5152 var previous = result; 5153 5154 for ( var i = 0 ; i < maxIter ; i++ ) { 5155 5156 nextEulerIteration(); 5157 result = h * s; 5158 if ( Math.abs( result - previous ) < tolerance * Math.abs(previous) ) return result; 5159 previous = result; 5160 5161 } 5162 5163 throw Error( 'Maximum interations reached' ); 5164 5165 case 'romberg': 5166 5167 var error = Number.MAX_VALUE; 5168 var maxIter = 30; 5169 5170 var h = ( b - a ) / 2; 5171 var s = ( f(a) + f(b) ) / 2 + f( (a+b)/2 ); 5172 var result = h * s; 5173 5174 var d = []; 5175 for ( var i = 0 ; i < maxIter ; i++ ) d.push( [] ); 5176 5177 // Richardson extrapolation of Euler-Maclaurin trapezoids 5178 d[0][0] = result; 5179 5180 for ( var i = 1 ; i < maxIter ; i++ ) { 5181 5182 nextEulerIteration(); 5183 d[0][i] = h * s; 5184 5185 for ( var j = 1 ; j <= i ; j++ ) { 5186 5187 d[j][i] = ( 4**j * d[j-1][i] - d[j-1][i-1] ) / ( 4**j - 1 ); 5188 5189 var delta = Math.max( Math.abs( d[j][i] - d[j-1][i] ), 5190 Math.abs( d[j][i] - d[j-1][i-1] ) ); 5191 if ( delta <= error ) { 5192 error = delta; 5193 result = d[j][i]; 5194 } 5195 5196 } 5197 5198 if ( Math.abs( d[i][i] - d[i-1][i-1] ) > error ) break; 5199 5200 } 5201 5202 return result; 5203 5204 case 'adaptive-simpson': 5205 5206 // algorithm by Charles Collins 5207 5208 var maxIter = 50; 5209 5210 function adaptiveSimpson( a, b, fa, fm, fb, s, tolerance, depth ) { 5211 5212 var h = b - a; 5213 var f1 = f( a + h/4 ); 5214 var f2 = f( b - h/4 ) 5215 5216 if ( isNaN(f1) || isNaN(f2) ) throw Error( 'NaN encountered in integration' ); 5217 5218 var s1 = ( fa + 4*f1 + fm ) * h / 12; 5219 var s2 = ( fm + 4*f2 + fb ) * h / 12; 5220 var ss = s1 + s2; 5221 var error = ( ss - s ) / 15; 5222 5223 if ( Math.abs(error) < tolerance || depth > maxIter ) return ss + error; 5224 else { 5225 var m = a + h/2; 5226 return adaptiveSimpson( a, m, fa, f1, fm, s1, tolerance/2, depth+1 ) 5227 + adaptiveSimpson( m, b, fm, f2, fb, s2, tolerance/2, depth+1 ); 5228 } 5229 5230 } 5231 5232 var fa = f(a); 5233 var fm = f( (a+b)/2 ); 5234 var fb = f(a); 5235 var s = ( fa + 4*fm + fb ) * (b-a) / 6; 5236 var depth = 0; 5237 5238 return adaptiveSimpson( a, b, fa, fm, fb, s, tolerance, depth ); 5239 5240 case 'tanh-sinh': 5241 5242 // based on Borwein & Bailey, Experimentation in Mathematics 5243 5244 var m = 10; 5245 var h = 1 / 2**m; 5246 var x = [], w = []; 5247 5248 for ( var k = 0 ; k <= 20 * 2**m ; k++ ) { 5249 var t = k * h; 5250 x[k] = Math.tanh( Math.PI/2 * Math.sinh(t) ); 5251 w[k] = Math.PI/2 * Math.cosh(t) / Math.cosh( Math.PI/2 * Math.sinh(t) )**2; 5252 if ( Math.abs(1-x[k]) < tolerance ) break; 5253 } 5254 5255 var nt = k;
5256 var sum = 0; 5257 5258 // rescale [a,b] to [-1,1] 5259 var len = ( b - a ) / 2; 5260 var mid = ( b + a ) / 2; 5261 5262 for ( var k = 1 ; k <= m ; k++ ) { 5263 for ( var i = 0 ; i < nt ; i += 2**(m-k) ) { 5264 if ( i % 2**(m-k+1) !== 0 || k === 1 ) { 5265 if ( i === 0 ) sum += w[0] * f( mid ); 5266 else sum += w[i] * ( f( mid - len*x[i] ) + f( mid + len*x[i] ) ); 5267 } 5268 } 5269 } 5270 5271 return len * h * sum; 5272 5273 case 'gaussian': 5274 5275 // based on Borwein & Bailey, Experimentation in Mathematics 5276 5277 var m = 10; 5278 var x = [], w = []; 5279 5280 var n = 3 * 2**m; 5281 5282 for ( var j = 1 ; j <= n/2 ; j++ ) { 5283 5284 var r = Math.cos( Math.PI * (j-.25) / (n+.5) ); 5285 5286 while ( true ) { 5287 5288 var t1 = 1, t2 = 0; 5289 5290 for ( var j1 = 1 ; j1 <= n ; j1++ ) { 5291 t3 = t2; 5292 t2 = t1; 5293 t1 = ( (2*j1-1) * r * t2 - (j1-1) * t3 ) / j1; 5294 } 5295 5296 var t4 = n * ( r*t1 - t2 ) / ( r**2 - 1 ); 5297 var delta = t1 / t4; 5298 r -= delta; 5299 5300 if ( Math.abs( delta ) < tolerance ) break; 5301 5302 } 5303 5304 x[j] = r; 5305 w[j] = 2 / ( 1 - r**2 ) / t4**2 5306 5307 } 5308 5309 // rescale [a,b] to [-1,1] 5310 var len = ( b - a ) / 2; 5311 var mid = ( b + a ) / 2; 5312 5313 var sum = 0; 5314 5315 for ( var j = 1 ; j <= n/2 ; j++ ) 5316 sum += w[j] * ( f( mid - len*x[j] ) + f( mid + len*x[j] ) ); 5317 5318 return len * sum; 5319 5320 default: 5321 5322 throw Error( 'Unsupported integration method' ); 5323 5324 } 5325 5326} 5327 5328 5329function discreteIntegral( values, step ) { 5330 5331 // composite trapezoidal rule 5332 5333 var s = ( values[0] + values[ values.length - 1 ] ) / 2; 5334 5335 for ( var i = 1 ; i < values.length - 1 ; i++ ) s += values[i]; 5336 5337 return s * step; 5338 5339} 5340 5341 5342function summation( f, [a,b,step=1] ) { 5343 5344 if ( isComplex( f(a) ) ) { 5345 5346 var s = complex(0); 5347 5348 for ( var i = a ; i <= b ; i += step ) s = add( s, f(i) ); 5349 5350 return s; 5351 5352 } else { 5353 5354 var s = 0; 5355 5356 for ( var i = a ; i <= b ; i += step ) s += f(i); 5357 5358 return s; 5359 5360 } 5361 5362} 5363 5364 5365function polynomial( x, coefficients, options={} ) { 5366 5367 var derivative = 'derivative' in options ? options.derivative : false; 5368 var reverse = 'reverse' in options ? options.reverse : false; 5369 5370 if ( reverse ) coefficients = coefficients.slice().reverse(); 5371 5372 // Horner's method with highest power coefficient first 5373 5374 var p = coefficients[0]; 5375 var q = 0; 5376 5377 for ( var i = 1 ; i < coefficients.length ; i++ ) { 5378 if ( derivative ) q = add( p, mul( q, x ) ); 5379 p = add( coefficients[i], mul( p, x ) ); 5380 } 5381 5382 if ( derivative ) return { polynomial: p, derivative: q }; 5383 5384 return p; 5385 5386} 5387 5388 5389function partialBell( n, k, argumentArray ) { 5390 5391 if ( n === 0 && k === 0 ) return 1; 5392 5393 if ( n === 0 || k === 0 ) return 0; 5394 5395 // evaluate recursively 5396 var s = 0; 5397 var p = 1; 5398 5399 for ( var i = 1 ; i <= n - k + 1 ; i++ ) { 5400 5401 s += p * argumentArray[i-1] * partialBell( n-i, k-1, argumentArray ); 5402 p *= ( n - i ) / i; 5403 5404 } 5405 5406 return s; 5407 5408} 5409 5410 5411function findRoot( f, start, options={} ) { 5412 5413 var tolerance = 'tolerance' in options ? options.tolerance : 1e-10; 5414 var maxIter = 100; 5415 5416 if ( Array.isArray(f) ) { 5417 5418 if ( f.length !== start.length ) 5419 throw Error( 'Mismatch between equations and starting point for root' ); 5420 5421 var root = start.slice(); 5422 5423 for ( var i = 0; i < maxIter ; i++ ) { 5424 5425 var J = [], F = []; 5426 5427 for ( var j = 0 ; j < root.length ; j++ ) { 5428 J.push( gradient( f[j], root ) ); 5429 F.push( f[j].apply( null, root ) ); 5430 } 5431 5432 var delta = luSolve( J, F ); 5433 5434 for ( var j = 0 ; j < root.length ; j++ ) root[j] -= delta[j]; 5435 5436 if ( delta.every( d => Math.abs(d) < tolerance ) ) return root; 5437 5438 } 5439 5440 throw Error( 'No root found for tolerance ' + tolerance ); 5441 5442 } 5443 5444 if ( !Array.isArray(start) && !options.method ) options.method = 'newton'; 5445 5446 var method = 'method' in options ? options.method : 'bisect'; 5447 5448 switch( method ) { 5449 5450 case 'bisect': 5451 5452 var a = start[0]; 5453 var b = start[1]; 5454 5455 var fa = f(a); 5456 var fb = f(b); 5457 5458 if ( fa * f(b) >= 0 ) throw Error( 'Change of sign necessary for bisection' ); 5459 5460 var root, h; 5461 if ( fa < 0 ) { 5462 root = a; 5463 h = b - a; 5464 } else { 5465 root = b; 5466 h = a - b; 5467 } 5468 5469 for ( var i = 0; i < maxIter ; i++ ) { 5470 h /= 2; 5471 var mid = root + h; 5472 fmid = f(mid); 5473 if ( fmid <= 0 ) root = mid; 5474 if ( fmid === 0 || Math.abs(h) < tolerance ) return root; 5475 } 5476 5477 throw Error( 'No root found for tolerance ' + tolerance ); 5478 5479 case 'newton': 5480 5481 var root = start; 5482 5483 if ( isComplex( f(root) ) ) { 5484 5485 for ( var i = 0; i < maxIter ; i++ ) { 5486 var delta = div( f(root), diff( f, root ) ); 5487 root = sub( root, delta ); 5488 if ( abs(delta) < tolerance ) return root; 5489 } 5490 5491 } else { 5492 5493 for ( var i = 0; i < maxIter ; i++ ) { 5494 var delta = f(root) / diff( f, root ); 5495 root -= delta; 5496 if ( Math.abs(delta) < tolerance ) return root; 5497 } 5498 5499 } 5500 5501 throw Error( 'No root found for tolerance ' + tolerance ); 5502 5503 default: 5504 5505 throw Error( 'Unsupported root finding method' ); 5506 5507 } 5508 5509} 5510 5511 5512function findRoots( f, point, tolerance=1e-10 ) { 5513 5514 console.log( 'Renamed to findRoot' ); 5515 5516} 5517 5518 5519function spline( points, value='function', tolerance=1e-10 ) { 5520 5521 // adapted from gsl / cspline.c and reference therein 5522 5523 var a = [], b = [], c = [], d = []; 5524 5525 for ( var i = 0 ; i < points.length ; i++ ) a[i] = points[i][1]; 5526 5527 c[0] = 0; 5528 c[ points.length - 1 ] = 0; 5529 5530 var A = matrix( points.length - 2 ); 5531 var y = vector( points.length - 2 ); 5532 5533 function h( i ) { return points[i+1][0] - points[i][0]; } 5534 5535 for ( var i = 0 ; i < A.length ; i++ ) { 5536 A[i][i] = 2 * ( h(i) + h(i+1) ); 5537 y[i] = 3 * ( a[i+2] - a[i+1] ) / h(i+1) - 3 * ( a[i+1] - a[i] ) / h(i);
5538 } 5539 for ( var i = 1 ; i < A.length ; i++ ) { 5540 A[i][i-1] = h(i); 5541 A[i-1][i] = h(i); 5542 } 5543 5544 var x = luSolve( A, y ); 5545 5546 for ( var i = 0 ; i < x.length ; i++ ) c[i+1] = x[i]; 5547 5548 for ( var i = 0 ; i < c.length - 1 ; i++ ) { 5549 b[i] = ( a[i+1] - a[i] ) / h(i) - ( c[i+1] + 2*c[i] ) * h(i) / 3; 5550 d[i] = ( c[i+1] - c[i] ) / 3 / h(i); 5551 } 5552 5553 switch( value ) { 5554 5555 case 'function': 5556 5557 return function( x ) { 5558 5559 if ( x < points[0][0] || x > points[points.length-1][0] ) 5560 throw Error( 'Argument outside spline input domain' ); 5561 5562 for ( var i = 0 ; i < points.length ; i++ ) 5563 if ( x === points[i][0] ) return a[i]; 5564 5565 for ( var i = 0 ; i < points.length - 1 ; i++ ) 5566 if ( x > points[i][0] && x < points[i+1][0] ) { 5567 var xi = points[i][0]; 5568 return a[i] + b[i] * ( x - xi ) 5569 + c[i] * ( x - xi )**2 + d[i] * ( x - xi )**3; 5570 } 5571 5572 } 5573 5574 case 'derivative': 5575 5576 return function( x ) { 5577 5578 if ( x < points[0][0] || x > points[points.length-1][0] ) 5579 throw Error( 'Argument outside spline input domain' ); 5580 5581 // method does not define b[points.length-1] so fudge endpoint 5582 if ( x === points[points.length-1][0] ) x -= tolerance; 5583 5584 for ( var i = 0 ; i < points.length ; i++ ) 5585 if ( x === points[i][0] ) return b[i]; 5586 5587 for ( var i = 0 ; i < points.length - 1 ; i++ ) 5588 if ( x > points[i][0] && x < points[i+1][0] ) { 5589 var xi = points[i][0]; 5590 return b[i] + 2 * c[i] * ( x - xi ) + 3 * d[i] * ( x - xi )**2; 5591 } 5592 5593 } 5594 5595 case 'integral': 5596 5597 return function( x ) { 5598 5599 if ( x < points[0][0] || x > points[points.length-1][0] ) 5600 throw Error( 'Argument outside spline input domain' ); 5601 5602 var sum = 0; 5603 5604 function F( x, i ) { 5605 var xi = points[i][0]; 5606 return a[i] * ( x - xi ) + b[i] * ( x - xi )**2 / 2 5607 + c[i] * ( x - xi )**3 / 3 + d[i] * ( x - xi )**4 / 4; 5608 } 5609 5610 for ( var i = 0 ; i < points.length - 1 ; i++ ) 5611 if ( x < points[i+1][0] ) { 5612 sum += F( x, i ) - F( points[i][0], i ); 5613 break; 5614 } else sum += F( points[i+1][0], i ) - F( points[i][0], i ); 5615 5616 return sum; 5617 5618 } 5619 5620 default: 5621 5622 throw Error( 'Unsupported spline value' ); 5623 5624 } 5625 5626} 5627 5628function padeApproximant( f, n, d, center=0 ) { 5629 5630 if ( !isPositiveInteger(n) || !isPositiveInteger(d) ) 5631 throw Error( 'Pade indices must be positive integers' ); 5632 5633 var c = []; 5634 5635 if ( typeof f === 'function' ) { 5636 c.push( f(center) ); 5637 for ( var i = 1 ; i <= n+d ; i++ ) c.push( diff( f, center, i ) / factorial(i) ); 5638 } else { 5639 if ( f.length < n+d+1 ) throw Error( 'Need n+d+1 Pade coefficients' ); 5640 c = f; 5641 } 5642 5643 var M = matrix( d ), v = vector( d ); 5644 5645 for ( var i = 0 ; i < d ; i++ ) { 5646 v[i] = -c[ n + 1 + i ]; 5647 // avoid negative index leading to undefined entry 5648 for ( var j = 0 ; j < d && j <= n+i ; j++ ) M[i][j] = c[ n + i - j ]; 5649 } 5650 5651 // need to understand why Mathematica returns results for singular matrices 5652 try { var b = luSolve( M, v ); } 5653 catch { throw Error( 'Singular Pade matrix encountered' ); }; 5654 b.unshift( 1 ); 5655 5656 var a = [ c[0] ]; 5657 5658 for ( var i = 1 ; i <= n ; i++ ) { 5659 var s = c[i]; 5660 // cannot exceed i when d > n 5661 for( var j = 1 ; j <= d && j <= i ; j++ ) s += b[j] * c[i-j]; 5662 a.push( s ); 5663 } 5664 5665 return { N: chop(a), D: chop(b) }; 5666 5667} 5668 5669 5670function fourierSinCoefficient( f, n, period ) { 5671 5672 if ( !Number.isInteger(n) ) throw Error( 'Nonintegral Fourier index' ); 5673 5674 if ( n === 0 ) return 0; 5675 5676 if ( typeof f === 'function' ) { 5677 5678 var T = period || 2*pi; 5679 5680 return 2/T * integrate( t => f(t) * sin( 2*n*pi/T * t ), [0,T], { method: 'tanh-sinh' } ); 5681 5682 } 5683 5684 if ( Array.isArray(f) ) { 5685 5686 var s = 0, N = f.length; 5687 5688 for ( var i = 0 ; i < N ; i++ ) s += f[i][1] * sin( 2*n*pi*i/N ); 5689 5690 return 2 * s / N; 5691 5692 } 5693 5694 throw Error( 'Unsupported Fourier input' ); 5695 5696} 5697 5698function fourierCosCoefficient( f, n, period ) { 5699 5700 if ( !Number.isInteger(n) ) throw Error( 'Nonintegral Fourier index' ); 5701 5702 if ( typeof f === 'function' ) { 5703 5704 var T = period || 2*pi; 5705 5706 if ( n === 0 ) return 1/T * integrate( t => f(t), [0,T], { method: 'tanh-sinh' } ); 5707 5708 return 2/T * integrate( t => f(t) * cos( 2*n*pi/T * t ), [0,T], { method: 'tanh-sinh' } ); 5709 5710 } 5711 5712 if ( Array.isArray(f) ) { 5713 5714 var s = 0, N = f.length; 5715 5716 if ( n === 0 ) { 5717 5718 for ( var i = 0 ; i < N ; i++ ) s += f[i][1]; 5719 5720 return s / N; 5721 5722 } 5723 5724 for ( var i = 0 ; i < N ; i++ ) s += f[i][1] * cos( 2*n*pi*i/N ); 5725 5726 return 2 * s / N; 5727 5728 } 5729 5730 throw Error( 'Unsupported Fourier input' ); 5731 5732} 5733 5734 5735function eigensystem( A, symmetric=true ) { 5736 5737 if ( symmetric ) return tridiagonalQL( tridiagonalForm(A) ); 5738 else throw Error( 'Unsupported eigensystem' ); 5739 5740} 5741 5742// sourced from http://math.nist.gov/javanumerics/jama/ 5743// no need to reinvent this wheel... 5744 5745function tridiagonalForm( A ) { 5746 5747 var n = A.length; 5748 var V = []; 5749 for ( var i = 0 ; i < n ; i++ ) V[i] = A[i].slice(); // deeper copy 5750 5751 var d = vector( n ); 5752 var e = vector( n ); 5753 5754 for ( var j = 0 ; j < n ; j++ ) d[j] = V[n-1][j]; 5755 5756 // Householder reduction to tridiagonal form 5757 5758 for ( var i = n - 1 ; i > 0 ; i-- ) { 5759 5760 // scale to avoid under/overflow 5761 5762 var scale = 0; 5763 var h = 0; 5764 for ( var k = 0 ; k < i ; k++ ) scale += Math.abs(d[k]); 5765 5766 if ( scale === 0 ) { 5767 e[i] = d[i-1]; 5768 for ( var j = 0 ; j < i ; j++ ) { 5769 d[j] = V[i-1][j]; 5770 V[i][j] = 0; 5771 V[j][i] = 0; 5772 } 5773 } else { 5774 5775 // generate Householder vector 5776 5777 for ( var k = 0 ; k < i ; k++ ) { 5778 d[k] /= scale; 5779 h += d[k] * d[k]; 5780 } 5781 var f = d[i-1]; 5782 var g = Math.sqrt(h); 5783 if ( f > 0 ) g = -g; 5784 e[i] = scale * g; 5785 h = h - f * g; 5786 d[i-1] = f - g; 5787 for ( var j = 0; j < i; j++ ) e[j] = 0; 5788 5789 // apply similarity transformation to remaining columns 5790 5791 for ( var j = 0 ; j < i ; j++ ) { 5792 f = d[j]; 5793 V[j][i] = f; 5794 g = e[j] + V[j][j] * f; 5795 for ( var k = j + 1 ; k <= i - 1 ; k++ ) { 5796 g += V[k][j] * d[k]; 5797 e[k] += V[k][j] * f; 5798 } 5799 e[j] = g; 5800 } 5801 f = 0; 5802 for ( var j = 0 ; j < i ; j++ ) { 5803 e[j] /= h; 5804 f += e[j] * d[j]; 5805 } 5806 var hh = f / ( h + h ); 5807 for ( var j = 0 ; j < i ; j++ ) { 5808 e[j] -= hh * d[j]; 5809 } 5810 for ( var j = 0 ; j < i ; j++ ) { 5811 f = d[j]; 5812 g = e[j]; 5813 for ( var k = j ; k <= i - 1 ; k++ ) { 5814 V[k][j] -= f * e[k] + g * d[k]; 5815 } 5816 d[j] = V[i-1][j]; 5817 V[i][j] = 0; 5818 } 5819 5820 } 5821 5822 d[i] = h; 5823 5824 } 5825 5826 // accumulate transformations 5827 5828 for ( var i = 0 ; i < n - 1 ; i++ ) { 5829 V[n-1][i] = V[i][i]; 5830 V[i][i] = 1; 5831 var h = d[i+1]; 5832 if ( h !== 0 ) { 5833 for ( var k = 0 ; k <= i ; k++ ) d[k] = V[k][i+1] / h; 5834 for ( var j = 0 ; j <= i ; j++ ) { 5835 var g = 0; 5836 for ( var k = 0 ; k <= i ; k++ ) g += V[k][i+1] * V[k][j]; 5837 for ( var k = 0 ; k <= i ; k++ ) V[k][j] -= g * d[k]; 5838 } 5839 } 5840 for ( var k = 0; k <= i; k++) V[k][i+1] = 0; 5841 } 5842 for ( var j = 0 ; j < n ; j++ ) { 5843 d[j] = V[n-1][j]; 5844 V[n-1][j] = 0; 5845 } 5846 V[n-1][n-1] = 1; 5847 e[0] = 0; 5848 5849 return { diagonal: d, offDiagonal: e, eigenvectors: V }; 5850 5851} 5852 5853 5854function tridiagonalQL( tridiagonalForm ) { 5855 5856 var d = tridiagonalForm.diagonal; 5857 var n = d.length; 5858 var e = tridiagonalForm.offDiagonal; 5859 var V = tridiagonalForm.eigenvectors; 5860 5861 function hypot( a, b) { 5862 var r; 5863 if ( Math.abs(a) > Math.abs(b) ) { 5864 r = b/a; 5865 r = Math.abs(a) * Math.sqrt( 1 + r*r ); 5866 } else if (b != 0) { 5867 r = a/b; 5868 r = Math.abs(b) * Math.sqrt( 1 + r*r ); 5869 } else r = 0; 5870 5871 return r; 5872 5873 } 5874 5875 for ( var i = 1 ; i < n ; i++ ) e[i-1] = e[i]; 5876 e[n-1] = 0; 5877 5878 var f = 0; 5879 var tst1 = 0; 5880 var eps = Math.pow( 2, -52 ); 5881 5882 for ( var l = 0 ; l < n ; l++ ) { 5883 5884 // find small subdiagonal element 5885 5886 tst1 = Math.max( tst1, Math.abs(d[l]) + Math.abs(e[l]) ); 5887 var m = l; 5888 while ( m < n ) { 5889 if ( Math.abs(e[m]) <= eps*tst1 ) break; 5890 m++; 5891 } 5892 5893 // if m === l, d[l] is an eigenvalue, otherwise iterate 5894 5895 if ( m > l ) { 5896 5897 var iter = 0; 5898 do { 5899 5900 iter = iter + 1; 5901 if ( iter > 1000 ) throw Error( 'Eigenvalues not converging...' ); 5902 5903 // compute implicit shift 5904 5905 var g = d[l]; 5906 var p = ( d[l+1] - g ) / ( 2 * e[l] );
5907 var r = hypot(p,1); 5908 if ( p < 0 ) r = -r; 5909 d[l] = e[l] / ( p + r ); 5910 d[l+1] = e[l] * ( p + r ); 5911 var dl1 = d[l+1]; 5912 var h = g - d[l]; 5913 for ( var i = l + 2 ; i < n ; i++ ) d[i] -= h; 5914 f = f + h; 5915 5916 // implicit QL transformation 5917 5918 p = d[m]; 5919 var c = 1; 5920 var c2 = c; 5921 var c3 = c; 5922 var el1 = e[l+1]; 5923 var s = 0; 5924 var s2 = 0; 5925 for ( var i = m - 1 ; i >= l ; i-- ) { 5926 c3 = c2; 5927 c2 = c; 5928 s2 = s; 5929 g = c * e[i]; 5930 h = c * p; 5931 r = hypot(p,e[i]); 5932 e[i+1] = s * r; 5933 s = e[i] / r; 5934 c = p / r; 5935 p = c * d[i] - s * g; 5936 d[i+1] = h + s * ( c * g + s * d[i] ); 5937 5938 // accumulate transformation 5939 5940 for ( var k = 0 ; k < n ; k++ ) { 5941 h = V[k][i+1]; 5942 V[k][i+1] = s * V[k][i] + c * h; 5943 V[k][i] = c * V[k][i] - s * h; 5944 } 5945 } 5946 p = -s * s2 * c3 * el1 * e[l] / dl1; 5947 e[l] = s * p; 5948 d[l] = c * p; 5949
5950 // check for convergence 5951 5952 } while ( Math.abs(e[l]) > eps*tst1 ); 5953 } 5954 d[l] = d[l] + f; 5955 e[l] = 0; 5956 } 5957 5958 // sort eigenvalues and corresponding vectors 5959 5960 for ( var i = 0 ; i < n - 1 ; i++ ) { 5961 var k = i; 5962 var p = d[i]; 5963 for ( var j = i + 1 ; j < n ; j++ ) { 5964 if ( d[j] < p ) { 5965 k = j; 5966 p = d[j]; 5967 } 5968 } 5969 if ( k != i ) { 5970 d[k] = d[i]; 5971 d[i] = p; 5972 for ( var j = 0 ; j < n ; j++ ) { 5973 p = V[j][i]; 5974 V[j][i] = V[j][k]; 5975 V[j][k] = p; 5976 } 5977 } 5978 } 5979 5980 return { eigenvalues: d, eigenvectors: V }; 5981 5982} 5983 5984 5985function hessenbergForm( A ) { 5986 5987} 5988 5989 5990function luDecomposition( A, tolerance=1e-10 ) { 5991 5992 var size = A.length; 5993 var LU = []; 5994 for ( var i = 0 ; i < size ; i++ ) LU[i] = A[i].slice(); // deeper copy 5995 5996 var P = identity( size ); 5997 pivots = 0; 5998 5999 for ( var i = 0 ; i < size ; i++ ) { 6000 6001 var maxValue = 0; 6002 var maxIndex = i; 6003 6004 for ( var j = i ; j < size ; j++ ) { 6005 var element = Math.abs( LU[j][i] ); 6006 if ( element > maxValue ) { 6007 maxValue = element; 6008 maxIndex = j; 6009 } 6010 } 6011 6012 if ( maxValue < tolerance ) throw Error( 'Matrix is degenerate' ); 6013 6014 if ( maxIndex !== i ) { 6015 6016 // pivot matrix rows 6017 var t = LU[i]; 6018 LU[i] = LU[maxIndex]; 6019 LU[maxIndex] = t; 6020 6021 // pivot permutation rows 6022 var t = P[i]; 6023 P[i] = P[maxIndex]; 6024 P[maxIndex] = t; 6025 6026 pivots++; 6027 6028 } 6029 6030 for ( var j = i + 1 ; j < size ; j++ ) { 6031 LU[j][i] /= LU[i][i]; 6032 for ( var k = i + 1; k < size ; k++ ) 6033 LU[j][k] -= LU[j][i] * LU[i][k]; 6034 } 6035 } 6036 6037 var L = identity( size ); 6038 for ( var i = 1 ; i < size ; i++ ) 6039 for ( var j = 0 ; j < i ; j++ ) L[i][j] = LU[i][j]; 6040 6041 var U = matrix( size ); 6042 for ( var i = 0 ; i < size ; i++ ) 6043 for ( var j = i ; j < size ; j++ ) U[i][j] = LU[i][j]; 6044 6045 return { L: L, U: U, P: P, pivots: pivots }; 6046 6047} 6048 6049function luSolve( A, b ) { 6050 6051 var size = A.length; 6052 var lu = luDecomposition(A); 6053 6054 var x = vector( size ); 6055 var y = vector( size ); 6056 var pb = vector( size ); 6057 6058 for ( var i = 0 ; i < size ; i++ ) 6059 for ( var j = 0 ; j < size ; j++ ) 6060 pb[i] += lu.P[i][j] * b[j]; 6061 6062 // forward solve 6063 for ( var i = 0 ; i < size ; i++ ) { 6064 y[i] = pb[i]; 6065 for ( var j = 0 ; j < i ; j++ ) y[i] -= lu.L[i][j] * y[j]; 6066 y[i] /= lu.L[i][i]; 6067 } 6068 6069 // backward solve 6070 for ( var i = size - 1 ; i >= 0 ; i-- ) { 6071 x[i] = y[i]; 6072 for ( var j = i + 1 ; j < size ; j++ ) x[i] -= lu.U[i][j] * x[j]; 6073 x[i] /= lu.U[i][i]; 6074 } 6075 6076 return x; 6077 6078} 6079 6080function determinant( A ) { 6081 6082 var lu = luDecomposition(A); 6083 6084 var product = 1; 6085 for ( var i = 0 ; i < A.length; i++ ) product *= lu.U[i][i]; 6086 6087 return (-1)**lu.pivots * product; 6088 6089} 6090 6091function inverse( A ) { 6092 6093 // calling luSolve for each column is not efficient 6094 // but avoids code duplication 6095 6096 var I = matrix( A.length ); 6097 6098 for ( var i = 0 ; i < A.length ; i++ ) { 6099 6100 var b = vector( A.length ); 6101 b[i] = 1; 6102 6103 var x = luSolve( A, b ); 6104 for ( var j = 0 ; j < A.length ; j++ ) I[j][i] = x[j]; 6105 6106 } 6107 6108 return I; 6109 6110} 6111 6112 6113 6114function vector( size, value=0 ) { 6115 6116 var v = []; 6117 for ( var i = 0 ; i < size ; i++ ) 6118 if ( Array.isArray(value) ) v.push( random.apply( null, value ) ); 6119 else v.push( value ); 6120 6121 return v; 6122 6123} 6124 6125function matrix( rows, columns, value=0 ) { 6126 6127 var columns = columns || rows; 6128 6129 var m = []; 6130 for ( var i = 0 ; i < rows ; i++ ) { 6131 m.push( [] ); 6132 for ( var j = 0 ; j < columns ; j++ ) 6133 if ( Array.isArray(value) ) m[i].push( random.apply( null, value ) ); 6134 else m[i].push( value ); 6135 } 6136 6137 return m; 6138 6139} 6140 6141function identity( rows, value=1 ) { 6142 6143 var m = matrix( rows ); 6144 for ( var i = 0 ; i < rows ; i++ ) m[i][i] = value; 6145 6146 return m; 6147 6148} 6149 6150function transpose( A ) { 6151 6152 var T = matrix( A[0].length, A.length ); 6153 6154 for ( var i = 0 ; i < A.length ; i++ ) 6155 for ( var j = 0 ; j < A[0].length ; j++ ) 6156 T[j][i] = A[i][j]; 6157 6158 return T; 6159 6160} 6161 6162function matrixAdd( A, B ) { 6163 6164 if ( !Array.isArray(A) && !Array.isArray(B) ) throw Error( 'No matrices to add' ); 6165 if ( !Array.isArray(A) ) A = matrix( B.length, B[0].length, A ); 6166 if ( !Array.isArray(B) ) B = matrix( A.length, A[0].length, B ); 6167 6168 var C = matrix( A.length, A[0].length, 0 ); 6169 6170 for ( var i = 0 ; i < A.length ; i++ ) 6171 for ( var j = 0 ; j < A[0].length ; j++ ) 6172 C[i][j] = add( A[i][j], B[i][j] ); 6173 6174 return C;
6175 6176} 6177 6178function matrixSub( A, B ) { 6179 6180 if ( !Array.isArray(A) && !Array.isArray(B) ) throw Error( 'No matrices to subtract' ); 6181 if ( !Array.isArray(A) ) A = matrix( B.length, B[0].length, A ); 6182 if ( !Array.isArray(B) ) B = matrix( A.length, A[0].length, B ); 6183 6184 var C = matrix( A.length, A[0].length, 0 ); 6185 6186 for ( var i = 0 ; i < A.length ; i++ ) 6187 for ( var j = 0 ; j < A[0].length ; j++ ) 6188 C[i][j] = sub( A[i][j], B[i][j] ); 6189 6190 return C; 6191 6192} 6193 6194function matrixMul( A, B ) { 6195 6196 if ( !Array.isArray(A) && !Array.isArray(B) ) throw Error( 'No matrices to multiply' ); 6197 if ( !Array.isArray(A) ) A = identity( B.length, A ); 6198 if ( !Array.isArray(B) ) B = identity( A[0].length, B ); 6199 if ( A[0].length !== B.length ) throw Error( 'Incompatible matrices to multiply' ); 6200 6201 var C = matrix( A.length, B[0].length, 0 ); 6202 6203 for ( var i = 0 ; i < A.length ; i++ ) 6204 for ( var j = 0 ; j < B[0].length ; j++ ) 6205 for ( var k = 0 ; k < A[0].length ; k++ ) 6206 C[i][j] = add( C[i][j], mul( A[i][k], B[k][j] ) ); 6207 6208 return C; 6209 6210} 6211
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.