ts-math
Version:
A collection of math functions and packages written in Typescript
1,310 lines • 135 kB
JavaScript
"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