UNPKG

ts-math

Version:

A collection of math functions and packages written in Typescript

1,310 lines 135 kB
"use strict"; // Fork from // https://github.com/paulmasson/math // I modified hypergeometric2F1 and added pfaffLimit, because som it speeded up callc where x were slightly above -1, due to too many iterations. var pi = Math.PI; var eulerGamma = .5772156649015329; var constants = { decimals: 50, precisionScale: BigInt(1e50), e: 271828182845904523536028747135266249775724709369995n, eulerGamma: 57721566490153286060651209008240243104215933593992n, pi: 314159265358979323846264338327950288419716939937510n }; function getConstant(name) { return constants[name] * precisionScale / constants.precisionScale; } // oeis.org/A000367 const bernoulli2nN = [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]; // oeis.org/A002445 const bernoulli2nD = [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]; var factorialCache = [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800]; function complex(x, y = 0) { if (y === 0 && isArbitrary(x)) y = 0n; return { re: x, im: y }; } var C = complex; function isComplex(x) { return typeof x === 'object' && 're' in x; } var decimals, precisionScale, arb1, arb2, onePi, twoPi, halfPi, ln10; function setPrecisionScale(n) { decimals = n; precisionScale = BigInt(Math.pow(10, decimals)); // set some commonly used constants arb1 = arbitrary(1); arb2 = arbitrary(2); onePi = getConstant('pi'); twoPi = mul(onePi, arb2); halfPi = div(onePi, arb2); ln10 = ln(arbitrary(10)); } setPrecisionScale(20); function arbitrary(x) { if (isComplex(x)) return { re: arbitrary(x.re), im: arbitrary(x.im) }; if (isArbitrary(x)) return Number(x) / Math.pow(10, decimals); // BigInt from exponential form includes wrong digits // manual construction from string more accurate var parts = x.toExponential().split('e'); var mantissa = parts[0].replace('.', ''); var digits = mantissa.length - (mantissa[0] === '-' ? 2 : 1); var padding = +parts[1] + decimals - digits; if (padding < 0) return BigInt(Math.round(x * Math.pow(10, decimals))); return BigInt(mantissa + '0'.repeat(padding)); } var A = arbitrary; function isArbitrary(x) { return typeof x === 'bigint' || typeof x.re === 'bigint'; } function isZero(x) { if (isComplex(x)) return x.re === 0 && x.im === 0; return x === 0; } function isInteger(x) { if (isComplex(x)) return Number.isInteger(x.re) && x.im === 0; return Number.isInteger(x); } function isPositiveInteger(x) { if (isComplex(x)) return Number.isInteger(x.re) && x.re > 0 && x.im === 0; return Number.isInteger(x) && x > 0; } function isPositiveIntegerOrZero(x) { if (isComplex(x)) return Number.isInteger(x.re) && x.re >= 0 && x.im === 0; return Number.isInteger(x) && x >= 0; } function isNegativeInteger(x) { if (isComplex(x)) return Number.isInteger(x.re) && x.re < 0 && x.im === 0; return Number.isInteger(x) && x < 0; } function isNegativeIntegerOrZero(x) { if (isComplex(x)) return Number.isInteger(x.re) && x.re <= 0 && x.im === 0; return Number.isInteger(x) && x <= 0; } function isEqualTo(x, y) { if (isComplex(x) || isComplex(y)) { if (!isComplex(x)) x = complex(x); if (!isComplex(y)) y = complex(y); return x.re === y.re && x.im === y.im; } return x === y; } function re(x) { if (isComplex(x)) return x.re; return x; } var real = re; function im(x) { if (isComplex(x)) return x.im; return 0; } var imag = im; function abs(x) { if (isComplex(x)) { if (x.re === 0 && x.im === 0) return 0; if (isArbitrary(x)) return sqrt(mul(x.re, x.re) + mul(x.im, x.im)); if (Math.abs(x.re) < Math.abs(x.im)) return Math.abs(x.im) * Math.sqrt(1 + Math.pow((x.re / x.im), 2)); else return Math.abs(x.re) * Math.sqrt(1 + Math.pow((x.im / x.re), 2)); } if (isArbitrary(x)) if (x < 0n) return -x; else return x; return Math.abs(x); } function arg(x) { if (isComplex(x)) return Math.atan2(x.im, x.re); return Math.atan2(0, x); } // JavaScript does not support operator overloading function add(x, y) { if (arguments.length > 2) { var z = add(x, y); for (var i = 2; i < arguments.length; i++) z = add(z, arguments[i]); return z; } if (isComplex(x) || isComplex(y)) { if (!isComplex(x)) x = complex(x); if (!isComplex(y)) y = complex(y); return { re: x.re + y.re, im: x.im + y.im }; } return x + y; } function sub(x, y) { if (isComplex(x) || isComplex(y)) { if (!isComplex(x)) x = complex(x); if (!isComplex(y)) y = complex(y); return { re: x.re - y.re, im: x.im - y.im }; } return x - y; } function mul(x, y) { if (arguments.length > 2) { var z = mul(x, y); for (var i = 2; i < arguments.length; i++) z = mul(z, arguments[i]); return z; } if (isComplex(x) || isComplex(y)) { if (!isComplex(x)) x = complex(x); if (!isComplex(y)) y = complex(y); if (isArbitrary(x)) return { re: (x.re * y.re - x.im * y.im) / precisionScale, im: (x.im * y.re + x.re * y.im) / precisionScale }; return { re: x.re * y.re - x.im * y.im, im: x.im * y.re + x.re * y.im }; } if (isArbitrary(x)) return x * y / precisionScale; return x * y; } function neg(x) { return mul(-1, x); } function div(x, y) { if (isComplex(x) || isComplex(y)) { if (!isComplex(x)) x = complex(x); if (!isComplex(y)) y = complex(y); if (y.re === 0 && y.im === 0 || y.re === 0n && y.im === 0n) // operator precedence throw Error('Division by zero'); if (isArbitrary(x)) { var N = { re: x.re * y.re + x.im * y.im, im: x.im * y.re - x.re * y.im }; var D = y.re * y.re + y.im * y.im; return { re: precisionScale * N.re / D, im: precisionScale * N.im / D }; } if (Math.abs(y.re) < Math.abs(y.im)) { var f = y.re / y.im; return { re: (x.re * f + x.im) / (y.re * f + y.im), im: (x.im * f - x.re) / (y.re * f + y.im) }; } else { var f = y.im / y.re; return { re: (x.re + x.im * f) / (y.re + y.im * f), im: (x.im - x.re * f) / (y.re + y.im * f) }; } } if (y === 0 || y === 0n) throw Error('Division by zero'); if (isArbitrary(x)) return precisionScale * x / y; return x / y; } function inv(x) { return div(1, x); } function pow(x, y) { if (isArbitrary(x) || isArbitrary(y)) { if (!isArbitrary(x)) x = arbitrary(x); if (!isArbitrary(y)) y = arbitrary(y); return exp(mul(y, ln(x))); } if (isComplex(x) || isComplex(y)) { if (!isComplex(x)) x = complex(x); if (!isComplex(y)) y = complex(y); if (x.re === 0 && x.im === 0 && y.re > 0) return complex(0); if (x.re === 0 && x.im === 0 && y.re === 0 && y.im === 0) return complex(1); if (x.re === 0 && x.im === 0 && y.re < 0) throw Error('Power singularity'); return exp(mul(y, log(x))); } if (x === 0 && y < 0) throw Error('Power singularity'); if (x < 0 && !Number.isInteger(y)) return pow(complex(x), y); return Math.pow(x, y); } function root(x, y) { return pow(x, div(1, y)); } function surd(x, n) { if (isComplex(x) || isComplex(n)) throw Error('Surd requires real inputs'); if (!isInteger(n)) throw Error('Second parameter of surd must be integer'); if (n & 1) { var sign = Math.sign(x); // zero at origin anyway return sign * root(sign * x, n); } if (x < 0) throw Error('First parameter of surd must be positive for even integers'); return root(x, n); } function sqrt(x) { if (isComplex(x)) { if (isArbitrary(x)) { if (x.im === 0n) if (x.re < 0n) return { re: 0n, im: sqrt(-x.re) }; else return { re: sqrt(x.re), im: 0n }; // need evaluation independent of natural logarithm var c = abs(x); var sign = x.im < 0n ? -1n : 1n; return { re: sqrt(div(c + x.re, arb2)), im: sign * sqrt(div(c - x.re, arb2)) }; } if (x.im === 0) if (x.re < 0) return { re: 0, im: Math.sqrt(-x.re) }; else return { re: Math.sqrt(x.re), im: 0 }; // expression above suffers from rounding errors when not arbitrary, // especially affecting elliptic integral of third kind return exp(mul(.5, log(x))); } if (isArbitrary(x)) { if (x === 0n) return 0n; if (x < 0n) throw Error('Cannot evaluate real square root of ' + x); // Brent, Modern Computer Arithmetic, SqrtInt algorithm var u = x, s, t; while (u !== s) { s = u; t = s + div(x, s); u = div(t, arb2); } return s; } if (x < 0) return { re: 0, im: Math.sqrt(-x) }; return Math.sqrt(x); } function complexAverage(f, x, offset = 1e-5) { return div(add(f(add(x, offset)), f(sub(x, offset))), 2); } function complexFromString(s, returnAsString = false) { var lead = '', real, imag; if (s[0] === '+' || s[0] === '-') { lead = s[0]; s = s.slice(1); } if (s.includes('+') || s.includes('-')) { if (s.includes('+')) { real = lead + s.slice(0, s.indexOf('+')); imag = s.slice(s.indexOf('+') + 1, s.length - 1); } else { real = lead + s.slice(0, s.indexOf('-')); imag = s.slice(s.indexOf('-'), s.length - 1); } } else { if (s.includes('i')) { real = '0'; imag = lead + s.slice(0, s.length - 1); } else { real = lead + s; imag = '0'; } } if (imag === '' || imag === '-') imag += '1'; if (returnAsString) return `{ re: ${real}, im: ${imag} }`; return { re: +real, im: +imag }; } function besselJ(n, x) { if (isComplex(n) || isComplex(x)) { if (isNegativeInteger(n)) return mul(pow(-1, n), besselJ(mul(-1, n), x)); var product = div(pow(div(x, 2), n), gamma(add(n, 1))); return mul(product, hypergeometric0F1(add(n, 1), mul(-.25, pow(x, 2)))); } if (isNegativeInteger(n)) return Math.pow((-1), n) * besselJ(-n, x); if (!Number.isInteger(n) && x < 0) return besselJ(n, complex(x)); return Math.pow((x / 2), n) * hypergeometric0F1(n + 1, -.25 * Math.pow(x, 2)) / gamma(n + 1); } function besselJZero(n, m, derivative = false) { if (n < 0) throw Error('Negative order for Bessel zero'); if (!Number.isInteger(m)) throw Error('Nonintegral index for Bessel zero'); // approximations from dlmf.nist.gov/10.21#vi var delta = pi / 4; if (derivative) { if (n === 0 && m === 1) return 0; var b = (m + n / 2 - 3 / 4) * pi; var e = b - (4 * Math.pow(n, 2) + 3) / (8 * b); // keep search evaluation real return findRoot(x => diff(x => besselJ(n, x), x), [e - delta < 0 ? 0 : e - delta, e + delta]); } else { var a = (m + n / 2 - 1 / 4) * pi; var e = a - (4 * Math.pow(n, 2) - 1) / (8 * a); return findRoot(x => besselJ(n, x), [e - delta, e + delta]); } } function besselY(n, x) { if (isComplex(n) || isComplex(x)) { // dlmf.nist.gov/10.2.3 if (isInteger(n)) return div(add(diff(n => besselJ(n, x), n), mul(pow(-1, n), diff(n => besselJ(n, x), neg(n)))), pi); var sum = sub(mul(besselJ(n, x), cos(mul(n, pi))), besselJ(mul(-1, n), x)); return div(sum, sin(mul(n, pi))); } if (x < 0) return besselY(n, complex(x)); // dlmf.nist.gov/10.2.3 if (Number.isInteger(n)) return (diff(n => besselJ(n, x), n) + Math.pow((-1), n) * diff(n => besselJ(n, x), -n)) / pi; return (besselJ(n, x) * cos(n * pi) - besselJ(-n, x)) / sin(n * pi); } function besselYZero(n, m, derivative = false) { if (n < 0) throw Error('Negative order for Bessel zero'); if (!Number.isInteger(m)) throw Error('Nonintegral index for Bessel zero'); // approximations from dlmf.nist.gov/10.21#vi var delta = pi / 4; if (derivative) { var b = (m + n / 2 - 1 / 4) * pi; var e = b - (4 * Math.pow(n, 2) + 3) / (8 * b); return findRoot(x => diff(x => besselY(n, x), x), [e - delta, e + delta]); } else { var a = (m + n / 2 - 3 / 4) * pi; var e = a - (4 * Math.pow(n, 2) - 1) / (8 * a); return findRoot(x => besselY(n, x), [e - delta, e + delta]); } } function besselI(n, x) { if (isComplex(n) || isComplex(x)) { if (isNegativeInteger(n)) return besselI(mul(-1, n), x); var product = div(pow(div(x, 2), n), gamma(add(n, 1))); return mul(product, hypergeometric0F1(add(n, 1), mul(.25, pow(x, 2)))); } if (isNegativeInteger(n)) return besselI(-n, x); if (!Number.isInteger(n) && x < 0) return besselI(n, complex(x)); return Math.pow((x / 2), n) * hypergeometric0F1(n + 1, .25 * Math.pow(x, 2)) / gamma(n + 1); } function besselK(n, x) { var useAsymptotic = 10; if (isComplex(n) || isComplex(x)) { // asymptotic form as per Johansson arxiv.org/abs/1606.06977 if (abs(x) > useAsymptotic) { var t1 = mul(sqrt(div(pi / 2, x)), exp(neg(x))); var t2 = hypergeometric2F0(add(n, .5), sub(.5, n), div(-.5, x)); return mul(t1, t2); } // dlmf.nist.gov/10.27.5 if (isInteger(n)) return mul(pow(-1, add(n, 1)), .5, add(diff(n => besselI(n, x), n), diff(n => besselI(n, x), neg(n)))); var product = div(pi / 2, sin(mul(n, pi))); return mul(product, sub(besselI(mul(-1, n), x), besselI(n, x))); } if (x > useAsymptotic) return sqrt(pi / 2 / x) * exp(-x) * hypergeometric2F0(n + .5, .5 - n, -.5 / x); if (x < 0) return besselK(n, complex(x)); // dlmf.nist.gov/10.27.5 if (Number.isInteger(n)) return Math.pow((-1), (n + 1)) / 2 * (diff(n => besselI(n, x), n) + diff(n => besselI(n, x), -n)); return pi / 2 * (besselI(-n, x) - besselI(n, x)) / sin(n * pi); } function hankel1(n, x) { return add(besselJ(n, x), mul(complex(0, 1), besselY(n, x))); } function hankel2(n, x) { return sub(besselJ(n, x), mul(complex(0, 1), besselY(n, x))); } // dlmf.nist.gov/9.2.ii and dlmf.nist.gov/9.6.i function airyAi(x) { if (isComplex(x)) { if (isZero(x)) return complex(1 / Math.pow(3, (2 / 3)) / gamma(2 / 3)); if (x.re < 0) { var z = mul(2 / 3, pow(neg(x), 3 / 2)); return mul(1 / 3, sqrt(neg(x)), add(besselJ(1 / 3, z), besselJ(-1 / 3, z))); } var z = mul(2 / 3, pow(x, 3 / 2)); return mul(1 / pi, sqrt(div(x, 3)), besselK(1 / 3, z)); } if (x === 0) return 1 / Math.pow(3, (2 / 3)) / gamma(2 / 3); if (x < 0) { var z = 2 / 3 * Math.pow((-x), (3 / 2)); return sqrt(-x) / 3 * (besselJ(1 / 3, z) + besselJ(-1 / 3, z)); } var z = 2 / 3 * Math.pow(x, (3 / 2)); return 1 / pi * sqrt(x / 3) * besselK(1 / 3, z); } function airyAiPrime(x) { if (isComplex(x)) { if (isZero(x)) return complex(-1 / Math.pow(3, (1 / 3)) / gamma(1 / 3)); if (x.re < 0) { var z = mul(2 / 3, pow(neg(x), 3 / 2)); return mul(1 / 3, x, sub(besselJ(-2 / 3, z), besselJ(2 / 3, z))); } var z = mul(2 / 3, pow(x, 3 / 2)); return mul(-1 / pi / sqrt(3), x, besselK(2 / 3, z)); } if (x === 0) return -1 / Math.pow(3, (1 / 3)) / gamma(1 / 3); if (x < 0) { var z = 2 / 3 * Math.pow((-x), (3 / 2)); return x / 3 * (besselJ(-2 / 3, z) - besselJ(2 / 3, z)); } var z = 2 / 3 * Math.pow(x, (3 / 2)); return -1 / pi / sqrt(3) * x * besselK(2 / 3, z); } function airyBi(x) { if (isComplex(x)) { if (isZero(x)) return complex(1 / Math.pow(3, (1 / 6)) / gamma(2 / 3)); if (x.re < 0) { var z = mul(2 / 3, pow(neg(x), 3 / 2)); return mul(sqrt(div(neg(x), 3)), sub(besselJ(-1 / 3, z), besselJ(1 / 3, z))); } var z = mul(2 / 3, pow(x, 3 / 2)); return mul(sqrt(div(x, 3)), add(besselI(1 / 3, z), besselI(-1 / 3, z))); } if (x === 0) return 1 / Math.pow(3, (1 / 6)) / gamma(2 / 3); if (x < 0) { var z = 2 / 3 * Math.pow((-x), (3 / 2)); return sqrt(-x / 3) * (besselJ(-1 / 3, z) - besselJ(1 / 3, z)); } var z = 2 / 3 * Math.pow(x, (3 / 2)); return sqrt(x / 3) * (besselI(1 / 3, z) + besselI(-1 / 3, z)); } function airyBiPrime(x) { if (isComplex(x)) { if (isZero(x)) return complex(Math.pow(3, (1 / 6)) / gamma(1 / 3)); if (x.re < 0) { var z = mul(2 / 3, pow(neg(x), 3 / 2)); return mul(1 / sqrt(3), neg(x), add(besselJ(2 / 3, z), besselJ(-2 / 3, z))); } var z = mul(2 / 3, pow(x, 3 / 2)); return mul(1 / sqrt(3), x, add(besselI(2 / 3, z), besselI(-2 / 3, z))); } if (x === 0) return Math.pow(3, (1 / 6)) / gamma(1 / 3); if (x < 0) { var z = 2 / 3 * Math.pow((-x), (3 / 2)); return -x / sqrt(3) * (besselJ(2 / 3, z) + besselJ(-2 / 3, z)); } var z = 2 / 3 * Math.pow(x, (3 / 2)); return x / sqrt(3) * (besselI(2 / 3, z) + besselI(-2 / 3, z)); } function sphericalBesselJ(n, x) { return mul(div(sqrt(pi / 2), sqrt(x)), besselJ(add(n, .5), x)); } function sphericalBesselY(n, x) { return mul(div(sqrt(pi / 2), sqrt(x)), besselY(add(n, .5), x)); } function sphericalHankel1(n, x) { return add(sphericalBesselJ(n, x), mul(complex(0, 1), sphericalBesselY(n, x))); } function sphericalHankel2(n, x) { return sub(sphericalBesselJ(n, x), mul(complex(0, 1), sphericalBesselY(n, x))); } function struveH(n, x) { return mul(pow(x, add(n, 1)), inv(mul(pow(2, n), sqrt(pi), gamma(add(n, 3 / 2)))), hypergeometric1F2(1, 3 / 2, add(n, 3 / 2), mul(-1 / 4, pow(x, 2)))); } function struveL(n, x) { // one sign different from struveH return mul(pow(x, add(n, 1)), inv(mul(pow(2, n), sqrt(pi), gamma(add(n, 3 / 2)))), hypergeometric1F2(1, 3 / 2, add(n, 3 / 2), mul(1 / 4, pow(x, 2)))); } function jacobiTheta(n, x, q, tolerance = 1e-10) { if (abs(q) >= 1) throw Error('Unsupported elliptic nome'); if (![1, 2, 3, 4].includes(n)) throw Error('Undefined Jacobi theta index'); if (isComplex(x) || isComplex(q)) { if (!isComplex(x)) x = complex(x); var piTau = div(log(q), complex(0, 1)); // dlmf.nist.gov/20.2 to reduce overflow if (Math.abs(x.im) > Math.abs(piTau.im) || Math.abs(x.re) > Math.PI) { // use floor for consistency with fundamentalParallelogram var pt = Math.floor(x.im / piTau.im); x = sub(x, mul(pt, piTau)); var p = Math.floor(x.re / Math.PI); x = sub(x, p * Math.PI); var qFactor = pow(q, -pt * pt); var eFactor = exp(mul(-2 * pt, x, complex(0, 1))); // factors can become huge, so chop spurious parts first switch (n) { case 1: return mul(Math.pow((-1), (p + pt)), qFactor, eFactor, chop(jacobiTheta(n, x, q), tolerance)); case 2: return mul(Math.pow((-1), p), qFactor, eFactor, chop(jacobiTheta(n, x, q), tolerance)); case 3: return mul(qFactor, eFactor, chop(jacobiTheta(n, x, q), tolerance)); case 4: return mul(Math.pow((-1), pt), qFactor, eFactor, chop(jacobiTheta(n, x, q), tolerance)); } } switch (n) { case 1: var s = complex(0); var p = complex(1); var i = 0; while (Math.abs(p.re) > tolerance || Math.abs(p.im) > tolerance) { p = mul(Math.pow((-1), i), pow(q, i * i + i), sin(mul(2 * i + 1, x))); s = add(s, p); i++; } return mul(2, pow(q, 1 / 4), s); case 2: var s = complex(0); var p = complex(1); var i = 0; while (Math.abs(p.re) > tolerance || Math.abs(p.im) > tolerance) { p = mul(pow(q, i * i + i), cos(mul(2 * i + 1, x))); s = add(s, p); i++; } return mul(2, pow(q, 1 / 4), s); case 3: var s = complex(0); var p = complex(1); var i = 1; while (Math.abs(p.re) > tolerance || Math.abs(p.im) > tolerance) { p = mul(pow(q, i * i), cos(mul(2 * i, x))); s = add(s, p); i++; } return add(1, mul(2, s)); case 4: var s = complex(0); var p = complex(1); var i = 1; while (Math.abs(p.re) > tolerance || Math.abs(p.im) > tolerance) { p = mul(pow(neg(q), i * i), cos(mul(2 * i, x))); s = add(s, p); i++; } return add(1, mul(2, s)); } } else { switch (n) { case 1: if (q < 0) return jacobiTheta(n, x, complex(q)); var s = 0; var p = 1; var i = 0; while (Math.abs(p) > tolerance) { p = Math.pow((-1), i) * Math.pow(q, (i * i + i)) * sin((2 * i + 1) * x); s += p; i++; } return 2 * Math.pow(q, (1 / 4)) * s; case 2: if (q < 0) return jacobiTheta(n, x, complex(q)); var s = 0; var p = 1; var i = 0; while (Math.abs(p) > tolerance) { p = Math.pow(q, (i * i + i)) * cos((2 * i + 1) * x); s += p; i++; } return 2 * Math.pow(q, (1 / 4)) * s; case 3: var s = 0; var p = 1; var i = 1; while (Math.abs(p) > tolerance) { p = Math.pow(q, (i * i)) * cos(2 * i * x); s += p; i++; } return 1 + 2 * s; case 4: var s = 0; var p = 1; var i = 1; while (Math.abs(p) > tolerance) { p = Math.pow((-q), (i * i)) * cos(2 * i * x); s += p; i++; } return 1 + 2 * s; } } } function ellipticNome(m) { if (isComplex(m)) return exp(div(mul(-pi, ellipticK(sub(1, m))), ellipticK(m))); if (m > 1) return ellipticNome(complex(m)); if (m < 0) return -exp(-pi * ellipticK(1 / (1 - m)) / ellipticK(m / (m - 1))); return exp(-pi * ellipticK(1 - m) / ellipticK(m)); } function fundamentalParallelogram(x, p1, p2) { // x = m p1 + n p2, solve for m, n var m = (x.re * p2.im - x.im * p2.re) / (p1.re * p2.im - p1.im * p2.re); var n = (x.im * p1.re - x.re * p1.im) / (p1.re * p2.im - p1.im * p2.re); return add(x, mul(-Math.floor(m), p1), mul(-Math.floor(n), p2)); } function sn(x, m) { if (m > 1 || isComplex(x) || isComplex(m)) { if (!isComplex(m)) m = complex(m); // ensure K complex // dlmf.nist.gov/22.17 if (abs(m) > 1) return mul(inv(sqrt(m)), sn(mul(sqrt(m), x), inv(m))); // periods 4K, 2iK' var p1 = mul(4, ellipticK(m)); var p2 = mul(complex(0, 2), ellipticK(sub(1, m))); x = fundamentalParallelogram(x, p1, p2); var q = ellipticNome(m); var t = div(x, pow(jacobiTheta(3, 0, q), 2)); return mul(div(jacobiTheta(3, 0, q), jacobiTheta(2, 0, q)), div(jacobiTheta(1, t, q), jacobiTheta(4, t, q))); } // dlmf.nist.gov/22.5.ii if (m === 0) return sin(x); if (m === 1) return tanh(x); var q = ellipticNome(m); var t = x / Math.pow(jacobiTheta(3, 0, q), 2); if (m < 0) return jacobiTheta(3, 0, q) / jacobiTheta(4, t, q) * div(jacobiTheta(1, t, q), jacobiTheta(2, 0, q)).re; return jacobiTheta(3, 0, q) / jacobiTheta(2, 0, q) * jacobiTheta(1, t, q) / jacobiTheta(4, t, q); } function cn(x, m) { if (m > 1 || isComplex(x) || isComplex(m)) { if (!isComplex(m)) m = complex(m); // ensure K complex // dlmf.nist.gov/22.17 if (abs(m) > 1) return dn(mul(sqrt(m), x), inv(m)); // periods 4K, 2K + 2iK' var p1 = mul(4, ellipticK(m)); var p2 = add(div(p1, 2), mul(complex(0, 2), ellipticK(sub(1, m)))); x = fundamentalParallelogram(x, p1, p2); var q = ellipticNome(m); var t = div(x, pow(jacobiTheta(3, 0, q), 2)); return mul(div(jacobiTheta(4, 0, q), jacobiTheta(2, 0, q)), div(jacobiTheta(2, t, q), jacobiTheta(4, t, q))); } // dlmf.nist.gov/22.5.ii if (m === 0) return cos(x); if (m === 1) return sech(x); var q = ellipticNome(m); var t = x / Math.pow(jacobiTheta(3, 0, q), 2); if (m < 0) return jacobiTheta(4, 0, q) / jacobiTheta(4, t, q) * div(jacobiTheta(2, t, q), jacobiTheta(2, 0, q)).re; return jacobiTheta(4, 0, q) / jacobiTheta(2, 0, q) * jacobiTheta(2, t, q) / jacobiTheta(4, t, q); } function dn(x, m) { if (m > 1 || isComplex(x) || isComplex(m)) { if (!isComplex(m)) m = complex(m); // ensure K complex // dlmf.nist.gov/22.17 if (abs(m) > 1) return cn(mul(sqrt(m), x), inv(m)); // periods 2K, 4iK' var p1 = mul(2, ellipticK(m)); var p2 = mul(complex(0, 4), ellipticK(sub(1, m))); x = fundamentalParallelogram(x, p1, p2); var q = ellipticNome(m); var t = div(x, pow(jacobiTheta(3, 0, q), 2)); return mul(div(jacobiTheta(4, 0, q), jacobiTheta(3, 0, q)), div(jacobiTheta(3, t, q), jacobiTheta(4, t, q))); } // dlmf.nist.gov/22.5.ii if (m === 0) return 1; if (m === 1) return sech(x); var q = ellipticNome(m); var t = x / Math.pow(jacobiTheta(3, 0, q), 2); return jacobiTheta(4, 0, q) / jacobiTheta(3, 0, q) * jacobiTheta(3, t, q) / jacobiTheta(4, t, q); } function am(x, m) { if (m > 1 || isComplex(x) || isComplex(m)) { if (!isComplex(x)) x = complex(x); if (!isComplex(m)) m = complex(m); if (m.im === 0 && m.re <= 1) { var K = ellipticK(m.re); var n = Math.round(x.re / 2 / K); x = sub(x, 2 * n * K); if (m.re < 0) { var Kp = ellipticK(1 - m.re); var p = Math.round(x.im / 2 / Kp.re); // bitwise test for odd integer if (p & 1) return sub(n * pi, arcsin(sn(x, m))); } return add(arcsin(sn(x, m)), n * pi); } return arcsin(sn(x, m)); } else { var K = ellipticK(m); var n = Math.round(x / 2 / K); x = x - 2 * n * K; return Math.asin(sn(x, m)) + n * pi; } } function weierstrassRoots(g2, g3) { function cubicTrigSolution(p, q, n) { // p, q both negative in defining cubic return mul(2 / sqrt(3), sqrt(p), cos(sub(div(arccos(mul(3 * sqrt(3) / 2, q, pow(p, -3 / 2))), 3), 2 * pi * n / 3))); } g2 = div(g2, 4); g3 = div(g3, 4); var e1 = cubicTrigSolution(g2, g3, 0); var e2 = cubicTrigSolution(g2, g3, 1); var e3 = cubicTrigSolution(g2, g3, 2); return [e1, e2, e3]; } function weierstrassHalfPeriods(g2, g3) { // Davis, Intro to Nonlinear Diff. & Integral Eqs., pp.157-8 // consistent with periods of Jacobi sine in weierstrassP // not consistent with Mathematica var [e1, e2, e3] = weierstrassRoots(g2, g3); var lambda = sqrt(sub(e1, e3)); var m = div(sub(e2, e3), sub(e1, e3)); var w1 = div(ellipticK(m), lambda); var w3 = div(mul(complex(0, 1), ellipticK(sub(1, m))), lambda); return [w1, w3]; } function weierstrassInvariants(w1, w3) { if (!isComplex(w1)) w1 = complex(w1); if (!isComplex(w3)) w3 = complex(w3); // order half periods by complex slope if (w3.im / w3.re < w1.im / w1.re) [w1, w3] = [w3, w1]; var ratio = div(w3, w1), conjugate; if (ratio.im < 0) { ratio.im = -ratio.im; conjugate = true; } var q = exp(mul(complex(0, 1), pi, ratio)); // en.wikipedia.org/wiki/Weierstrass's_elliptic_functions // modified for input of half periods var a = jacobiTheta(2, 0, q); var b = jacobiTheta(3, 0, q); var g2 = mul(4 / 3 * Math.pow(pi, 4), pow(mul(2, w1), -4), add(pow(a, 8), mul(-1, pow(a, 4), pow(b, 4)), pow(b, 8))); var g3 = mul(8 / 27 * Math.pow(pi, 6), pow(mul(2, w1), -6), add(pow(a, 12), mul(-3 / 2, pow(a, 8), pow(b, 4)), mul(-3 / 2, pow(a, 4), pow(b, 8)), pow(b, 12))); if (conjugate) { g2.im = -g2.im; g3.im = -g3.im; } return [g2, g3]; } function weierstrassP(x, g2, g3) { if (!isComplex(x)) x = complex(x); var [e1, e2, e3] = weierstrassRoots(g2, g3); // Whittaker & Watson, Section 22.351 var m = div(sub(e2, e3), sub(e1, e3)); return add(e3, mul(sub(e1, e3), pow(sn(mul(x, sqrt(sub(e1, e3))), m), -2))); } function weierstrassPPrime(x, g2, g3) { if (!isComplex(x)) x = complex(x); var [e1, e2, e3] = weierstrassRoots(g2, g3); // Whittaker & Watson, Section 22.351 var m = div(sub(e2, e3), sub(e1, e3)); var argument = mul(x, sqrt(sub(e1, e3))); return mul(-2, pow(sub(e1, e3), 3 / 2), cn(argument, m), dn(argument, m), pow(sn(argument, m), -3)); } function inverseWeierstrassP(x, g2, g3) { if (!isComplex(x)) x = complex(x); var [e1, e2, e3] = weierstrassRoots(g2, g3); // Johansson arxiv.org/pdf/1806.06725.pdf p.17 // sign of imaginary part on real axis differs from Mathematica return carlsonRF(sub(x, e1), sub(x, e2), sub(x, e3)); } function kleinJ(x) { // from mpmath / elliptic.py var q = exp(mul(complex(0, pi), x)); var t2 = chop(jacobiTheta(2, 0, q)); var t3 = chop(jacobiTheta(3, 0, q)); var t4 = chop(jacobiTheta(4, 0, q)); var P = pow(add(pow(t2, 8), pow(t3, 8), pow(t4, 8)), 3); var Q = mul(54, pow(mul(t2, t3, t4), 8)); return div(P, Q); } // Carlson symmetric integrals function carlsonRC(x, y) { if (x < 0 || y < 0 || isComplex(x) || isComplex(y)) { if (!isComplex(x)) x = complex(x); if (!isComplex(y)) y = complex(y); if (x.re === y.re && x.im === y.im) return inv(sqrt(x)); // return value by continuity return div(arccos(div(sqrt(x), sqrt(y))), mul(sqrt(y), sqrt(sub(1, div(x, y))))); } if (x === y) return 1 / Math.sqrt(x); if (x < y) return Math.acos(Math.sqrt(x / y)) / Math.sqrt(y - x); return Math.acosh(Math.sqrt(x / y)) / Math.sqrt(x - y); } function carlsonRD(x, y, z) { return carlsonRJ(x, y, z, z); } function carlsonRF(x, y, z, tolerance = 1e-10) { if (isComplex(x) || isComplex(y) || isComplex(z)) { var xm = x; var ym = y; var zm = z; var Am = A0 = div(add(x, y, z), 3); var Q = Math.pow(3 * tolerance, -1 / 6) * Math.max(abs(sub(A0, x)), abs(sub(A0, y)), abs(sub(A0, z))); var g = .25; var pow4 = 1; while (true) { var xs = sqrt(xm); var ys = sqrt(ym); var zs = sqrt(zm); var lm = add(mul(xs, ys), mul(xs, zs), mul(ys, zs)); var Am1 = mul(add(Am, lm), g); xm = mul(add(xm, lm), g); ym = mul(add(ym, lm), g); zm = mul(add(zm, lm), g); if (pow4 * Q < abs(Am)) break; Am = Am1; pow4 *= g; } var t = div(pow4, Am); var X = mul(sub(A0, x), t); var Y = mul(sub(A0, y), t); var Z = neg(add(X, Y)); var E2 = sub(mul(X, Y), mul(Z, Z)); var E3 = mul(X, Y, Z); return mul(pow(Am, -.5), add(9240, mul(-924, E2), mul(385, E2, E2), mul(660, E3), mul(-630, E2, E3)), 1 / 9240); } else { if (y === z) return carlsonRC(x, y); if (x === z) return carlsonRC(y, x); if (x === y) return carlsonRC(z, x); if (x < 0 || y < 0 || z < 0) return carlsonRF(complex(x), y, z); // adapted from mpmath / elliptic.py var xm = x; var ym = y; var zm = z; var Am = A0 = (x + y + z) / 3; var Q = Math.pow(3 * tolerance, -1 / 6) * Math.max(Math.abs(A0 - x), Math.abs(A0 - y), Math.abs(A0 - z)); var g = .25; var pow4 = 1; while (true) { var xs = Math.sqrt(xm); var ys = Math.sqrt(ym); var zs = Math.sqrt(zm); var lm = xs * ys + xs * zs + ys * zs; var Am1 = (Am + lm) * g; xm = (xm + lm) * g; ym = (ym + lm) * g; zm = (zm + lm) * g; if (pow4 * Q < Math.abs(Am)) break; Am = Am1; pow4 *= g; } var t = pow4 / Am; var X = (A0 - x) * t; var Y = (A0 - y) * t; var Z = -X - Y; var E2 = X * Y - Math.pow(Z, 2); var E3 = X * Y * Z; return Math.pow(Am, -.5) * (9240 - 924 * E2 + 385 * Math.pow(E2, 2) + 660 * E3 - 630 * E2 * E3) / 9240; } } function carlsonRG(x, y, z) { var t1 = mul(z, carlsonRF(x, y, z)); var t2 = mul(-1 / 3, sub(x, z), sub(y, z), carlsonRD(x, y, z)); var t3 = sqrt(mul(x, y, inv(z))); return mul(.5, add(t1, t2, t3)); } function carlsonRJ(x, y, z, p, tolerance = 1e-10) { if (isComplex(x) || isComplex(y) || isComplex(z) || isComplex(p)) { var xm = x; var ym = y; var zm = z; var pm = p; var A0 = Am = div(add(x, y, z, mul(2, p)), 5); var delta = mul(sub(p, x), sub(p, y), sub(p, z)); var Q = Math.pow(.25 * tolerance, -1 / 6) * Math.max(abs(sub(A0, x)), abs(sub(A0, y)), abs(sub(A0, z)), abs(sub(A0, p))); var g = .25; var pow4 = 1; var S = complex(0); while (true) { var sx = sqrt(xm); var sy = sqrt(ym); var sz = sqrt(zm); var sp = sqrt(pm); var lm = add(mul(sx, sy), mul(sx, sz), mul(sy, sz)); var Am1 = mul(add(Am, lm), g); xm = mul(add(xm, lm), g); ym = mul(add(ym, lm), g); zm = mul(add(zm, lm), g); pm = mul(add(pm, lm), g); var dm = mul(add(sp, sx), add(sp, sy), add(sp, sz)); var em = mul(delta, Math.pow(pow4, 3), inv(dm), inv(dm)); if (pow4 * Q < abs(Am)) break; var T = mul(carlsonRC(1, add(1, em)), pow4, inv(dm)); S = add(S, T); pow4 *= g; Am = Am1; } var t = div(pow4, Am); var X = mul(sub(A0, x), t); var Y = mul(sub(A0, y), t); var Z = mul(sub(A0, z), t); var P = div(add(X, Y, Z), -2); var E2 = add(mul(X, Y), mul(X, Z), mul(Y, Z), mul(-3, P, P)); var E3 = add(mul(X, Y, Z), mul(2, E2, P), mul(4, P, P, P)); var E4 = mul(add(mul(2, X, Y, Z), mul(E2, P), mul(3, P, P, P)), P); var E5 = mul(X, Y, Z, P, P); P = add(24024, mul(-5148, E2), mul(2457, E2, E2), mul(4004, E3), mul(-4158, E2, E3), mul(-3276, E4), mul(2772, E5)); var v1 = mul(pow4, pow(Am, -1.5), P, 1 / 24024); var v2 = mul(6, S); return add(v1, v2); } else { if (x < 0 || y < 0 || z < 0 || p < 0) return carlsonRJ(complex(x), y, z, p); // adapted from mpmath / elliptic.py var xm = x; var ym = y; var zm = z; var pm = p; var A0 = Am = (x + y + z + 2 * p) / 5; var delta = (p - x) * (p - y) * (p - z); var Q = Math.pow(.25 * tolerance, -1 / 6) * Math.max(Math.abs(A0 - x), Math.abs(A0 - y), Math.abs(A0 - z), Math.abs(A0 - p)); var g = .25; var pow4 = 1; var S = 0; while (true) { var sx = Math.sqrt(xm); var sy = Math.sqrt(ym); var sz = Math.sqrt(zm); var sp = Math.sqrt(pm); var lm = sx * sy + sx * sz + sy * sz; var Am1 = (Am + lm) * g; xm = (xm + lm) * g; ym = (ym + lm) * g; zm = (zm + lm) * g; pm = (pm + lm) * g; var dm = (sp + sx) * (sp + sy) * (sp + sz); var em = delta * Math.pow(pow4, 3) / Math.pow(dm, 2); if (pow4 * Q < Math.abs(Am)) break; var T = carlsonRC(1, 1 + em) * pow4 / dm; S += T; pow4 *= g; Am = Am1; } var t = pow4 / Am; var X = (A0 - x) * t; var Y = (A0 - y) * t; var Z = (A0 - z) * t; var P = (-X - Y - Z) / 2; var E2 = X * Y + X * Z + Y * Z - 3 * Math.pow(P, 2); var E3 = X * Y * Z + 2 * E2 * P + 4 * Math.pow(P, 3); var E4 = (2 * X * Y * Z + E2 * P + 3 * Math.pow(P, 3)) * P; var E5 = X * Y * Z * Math.pow(P, 2); P = 24024 - 5148 * E2 + 2457 * Math.pow(E2, 2) + 4004 * E3 - 4158 * E2 * E3 - 3276 * E4 + 2772 * E5; var v1 = pow4 * Math.pow(Am, -1.5) * P / 24024; var v2 = 6 * S; return v1 + v2; } } // elliptic integrals function ellipticF(x, m) { if (arguments.length === 1) { m = x; x = pi / 2; } if (isComplex(x) || isComplex(m)) { if (!isComplex(x)) x = complex(x); var period = complex(0); if (Math.abs(x.re) > pi / 2) { var p = Math.round(x.re / pi); x.re = x.re - p * pi; period = mul(2 * p, ellipticK(m)); } return add(mul(sin(x), carlsonRF(mul(cos(x), cos(x)), sub(1, mul(m, sin(x), sin(x))), 1)), period); } else { if (m > 1 && Math.abs(x) > Math.asin(1 / Math.sqrt(m))) return ellipticF(complex(x), m); var period = 0; if (Math.abs(x) > pi / 2) { var p = Math.round(x / pi); x = x - p * pi; period = 2 * p * ellipticK(m); } return sin(x) * carlsonRF(Math.pow(cos(x), 2), 1 - m * Math.pow(sin(x), 2), 1) + period; } } function ellipticK(m) { return ellipticF(m); } function ellipticE(x, m) { if (arguments.length === 1) { m = x; x = pi / 2; } if (isComplex(x) || isComplex(m)) { if (!isComplex(x)) x = complex(x); var period = complex(0); if (Math.abs(x.re) > pi / 2) { var p = Math.round(x.re / pi); x.re = x.re - p * pi; period = mul(2 * p, ellipticE(m)); } return add(mul(sin(x), carlsonRF(mul(cos(x), cos(x)), sub(1, mul(m, sin(x), sin(x))), 1)), mul(-1 / 3, m, pow(sin(x), 3), carlsonRD(mul(cos(x), cos(x)), sub(1, mul(m, sin(x), sin(x))), 1)), period); } else { if (m > 1 && Math.abs(x) > Math.asin(1 / Math.sqrt(m))) return ellipticE(complex(x), m); var period = 0; if (Math.abs(x) > pi / 2) { var p = Math.round(x / pi); x = x - p * pi; period = 2 * p * ellipticE(m); } return sin(x) * carlsonRF(Math.pow(cos(x), 2), 1 - m * Math.pow(sin(x), 2), 1) - m / 3 * Math.pow(sin(x), 3) * carlsonRD(Math.pow(cos(x), 2), 1 - m * Math.pow(sin(x), 2), 1) + period; } } function ellipticPi(n, x, m) { if (arguments.length === 2) { m = x; x = pi / 2; } // x outside period and abs(n)>1 agrees with mpmath, differs from Mathematica if (isComplex(n) || isComplex(x) || isComplex(m)) { if (!isComplex(x)) x = complex(x); var period = complex(0); if (Math.abs(x.re) > pi / 2) { var p = Math.round(x.re / pi); x.re = x.re - p * pi; period = mul(2 * p, ellipticPi(n, m)); } return add(mul(sin(x), carlsonRF(mul(cos(x), cos(x)), sub(1, mul(m, sin(x), sin(x))), 1)), mul(1 / 3, n, pow(sin(x), 3), carlsonRJ(mul(cos(x), cos(x)), sub(1, mul(m, sin(x), sin(x))), 1, sub(1, mul(n, sin(x), sin(x))))), period); } else { if (n > 1 && Math.abs(x) > Math.asin(1 / Math.sqrt(n))) return ellipticPi(n, complex(x), m); if (m > 1 && Math.abs(x) > Math.asin(1 / Math.sqrt(m))) return ellipticPi(n, complex(x), m); var period = 0; if (Math.abs(x) > pi / 2) { var p = Math.round(x / pi); x = x - p * pi; period = 2 * p * ellipticPi(n, m); } return sin(x) * carlsonRF(Math.pow(cos(x), 2), 1 - m * Math.pow(sin(x), 2), 1) + n / 3 * Math.pow(sin(x), 3) * carlsonRJ(Math.pow(cos(x), 2), 1 - m * Math.pow(sin(x), 2), 1, 1 - n * Math.pow(sin(x), 2)) + period; } } function jacobiZeta(x, m) { // using definition matching elliptic integrals // alternate definition replaces x with am(x,m) return sub(ellipticE(x, m), mul(ellipticF(x, m), ellipticE(m), inv(ellipticK(m)))); } function factorial(n) { if (isComplex(n)) { if (n.im === 0 && isPositiveIntegerOrZero(n.re)) return complex(factorial(n.re)); return gamma(add(n, 1)); } if (isPositiveIntegerOrZero(n)) { if (factorialCache[n]) return factorialCache[n]; var last = factorialCache.length - 1; var result = factorialCache[last]; for (var i = last + 1; i <= n; i++) { result *= i; factorialCache[i] = result; } return result; } return gamma(n + 1); } function factorial2(n) { if (isZero(n)) return 1; if (isPositiveInteger(n)) { // bitwise test for odd integer, upward recursion for possible caching var result = n & 1 ? 1 : 2; for (var i = result + 2; i <= n; i += 2) result *= i; return result; } var f1 = pow(2, div(n, 2)); var f2 = pow(pi / 2, div(sub(cos(mul(pi, n)), 1), 4)); var f3 = gamma(add(div(n, 2), 1)); return mul(f1, f2, f3); } function binomial(n, m) { if (Number.isInteger(m) && m < 0 && n >= 0) return 0; if (Number.isInteger(n) && Number.isInteger(m) && n >= 0 && m > n) return 0; if (isComplex(n) || isComplex(m)) return div(factorial(n), mul(factorial(sub(n, m)), factorial(m))); return factorial(n) / factorial(n - m) / factorial(m); } // log of gamma less likely to overflow than gamma // Lanczos approximation as evaluated by Paul Godfrey function logGamma(x) { var c = [57.1562356658629235, -59.5979603554754912, 14.1360979747417471, -.491913816097620199, .339946499848118887e-4, .465236289270485756e-4, -.983744753048795646e-4, .158088703224912494e-3, -.210264441724104883e-3, .217439618115212643e-3, -.164318106536763890e-3, .844182239838527433e-4, -.261908384015814087e-4, .368991826595316234e-5]; if (isComplex(x)) { if (isNegativeIntegerOrZero(x)) throw Error('Gamma function pole'); // reflection formula with modified Hare correction to imaginary part if (x.re < 0) { var t = sub(log(div(pi, sin(mul(pi, x)))), logGamma(sub(1, x))); var s = x.im < 0 ? -1 : 1; var d = x.im === 0 ? 1 / 4 : 0; var k = Math.ceil(x.re / 2 - 3 / 4 + d); return add(t, complex(0, 2 * s * k * pi)); } var t = add(x, 5.24218750000000000); t = sub(mul(add(x, .5), log(t)), t); var s = .999999999999997092; for (var j = 0; j < 14; j++) s = add(s, div(c[j], add(x, j + 1))); var u = add(t, log(mul(2.5066282746310005, div(s, x)))); // adjustment to keep imaginary part on same sheet if (s.re < 0) { if (x.im < 0 && div(s, x).im < 0) u = add(u, complex(0, 2 * pi)); if (x.im > 0 && div(s, x).im > 0) u = add(u, complex(0, -2 * pi)); } return u; } else { if (isNegativeIntegerOrZero(x)) throw Error('Gamma function pole'); var t = x + 5.24218750000000000; t = (x + .5) * log(t) - t; var s = .999999999999997092; for (var j = 0; j < 14; j++) s += c[j] / (x + j + 1); return t + log(2.5066282746310005 * s / x); } } function gamma(x, y = null, z = null) { if (y !== null && z === null) { if (isZero(x)) { if (isZero(y)) throw Error('Gamma function pole'); // combination of logarithms adds/subtracts complex(0,pi) var sign = y.im > 0 ? -1 : y.im < 0 ? 1 : 0; var result = add(neg(expIntegralEi(neg(y))), complex(0, sign * pi)); if (!isComplex(y) && y > 0) return result.re; return result; } // dlmf.nist.gov/8.4.15 if (isNegativeInteger(x)) { var n = isComplex(x) ? -x.re : -x; var t = mul(exp(neg(y)), summation(k => div(Math.pow((-1), k) * factorial(k), pow(y, k + 1)), [0, n - 1])); // dlmf.nist.gov/8.4.4 var result = mul(Math.pow((-1), n) / factorial(n), sub(gamma(0, y), t)); if (isComplex(x) && !isComplex(result)) return complex(result); // complex in, complex out return result; } return sub(gamma(x), gamma(x, 0, y)); } if (y !== null && z !== null) { if (!isZero(y)) return sub(gamma(x, 0, z), gamma(x, 0, y)); return mul(pow(z, x), inv(x), hypergeometric1F1(x, add(x, 1), neg(z))); } if (isPositiveInteger(x)) return factorial(sub(x, 1)); // logGamma complex on negative axis if (!isComplex(x) && x < 0) return exp(logGamma(complex(x))).re; return exp(logGamma(x)); } function gammaRegularized(x, y, z) { if (arguments.length === 3) return div(gamma(x, y, z), gamma(x)); return div(gamma(x, y), gamma(x)); } function beta(x, y, z, w) { if (arguments.length === 4) return sub(beta(y, z, w), beta(x, z, w)); if (arguments.length === 3) return mul(pow(x, y), inv(y), hypergeometric2F1(y, sub(1, z), add(y, 1), x)); return div(mul(gamma(x), gamma(y)), gamma(add(x, y))); } function betaRegularized(x, y, z, w) { if (arguments.length === 4) return div(beta(x, y, z, w), beta(z, w)); return div(beta(x, y, z), beta