@numero-uno-jpn/rstats 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +101 -0
- package/README.ja.md +36 -20
- package/README.md +34 -18
- package/THIRD_PARTY_NOTICES.md +25 -5
- package/dist/chunks/chunk-2JCIB5H2.js +2 -0
- package/dist/chunks/chunk-7JVSDGQQ.js +2 -0
- package/dist/chunks/chunk-7NK4SA6B.js +2 -0
- package/dist/chunks/chunk-AJYQNFXK.js +2 -0
- package/dist/chunks/chunk-DRNMIEYH.js +2 -0
- package/dist/chunks/chunk-NAYDS7CL.js +2 -0
- package/dist/chunks/chunk-NOCEAM44.js +2 -0
- package/dist/chunks/chunk-QNAIK4PY.js +2 -0
- package/dist/chunks/chunk-TPC6MTYC.js +2 -0
- package/dist/chunks/chunk-UGUQYWRQ.js +2 -0
- package/dist/chunks/chunk-XM2TVD63.js +2 -0
- package/dist/chunks/chunk-Y4IZV7XK.js +2 -0
- package/dist/chunks/chunk-ZWJ42QN6.js +2 -0
- package/dist/descriptive/basic.d.ts +73 -0
- package/dist/descriptive/basic.js +256 -0
- package/dist/descriptive/cov.d.ts +39 -0
- package/dist/descriptive/cov.js +105 -0
- package/dist/descriptive/hist.d.ts +64 -0
- package/dist/descriptive/hist.js +304 -0
- package/dist/descriptive/moments.d.ts +63 -0
- package/dist/descriptive/moments.js +158 -0
- package/dist/descriptive/quantile.d.ts +50 -0
- package/dist/descriptive/quantile.js +131 -0
- package/dist/descriptive/rank.d.ts +23 -0
- package/dist/descriptive/rank.js +72 -0
- package/dist/descriptive/table.d.ts +54 -0
- package/dist/descriptive/table.js +160 -0
- package/dist/descriptive.js +2 -0
- package/dist/distributions/arcsine.d.ts +24 -0
- package/dist/distributions/arcsine.js +73 -0
- package/dist/distributions/invgamma.d.ts +29 -0
- package/dist/distributions/invgamma.js +85 -0
- package/dist/distributions/kumaraswamy.d.ts +17 -0
- package/dist/distributions/kumaraswamy.js +96 -0
- package/dist/distributions/laplace.d.ts +23 -0
- package/dist/distributions/laplace.js +69 -0
- package/dist/distributions/multinomial.d.ts +24 -0
- package/dist/distributions/multinomial.js +70 -0
- package/dist/distributions/nbeta.js +28 -10
- package/dist/distributions/nchisq.js +20 -3
- package/dist/distributions/pareto.d.ts +23 -0
- package/dist/distributions/pareto.js +76 -0
- package/dist/distributions/summary.d.ts +25 -0
- package/dist/distributions/summary.js +289 -0
- package/dist/distributions/triangular.d.ts +25 -0
- package/dist/distributions/triangular.js +87 -0
- package/dist/distributions.js +2 -0
- package/dist/entries/descriptive.d.ts +7 -0
- package/dist/entries/descriptive.js +8 -0
- package/dist/entries/distributions.d.ts +41 -0
- package/dist/entries/distributions.js +41 -0
- package/dist/entries/linalg.d.ts +11 -0
- package/dist/entries/linalg.js +11 -0
- package/dist/entries/numerics.d.ts +8 -0
- package/dist/entries/numerics.js +6 -0
- package/dist/entries/special.d.ts +9 -0
- package/dist/entries/special.js +9 -0
- package/dist/entries/tests.d.ts +21 -0
- package/dist/entries/tests.js +20 -0
- package/dist/htest/ansariTest.d.ts +13 -0
- package/dist/htest/ansariTest.js +80 -0
- package/dist/htest/aov.d.ts +85 -0
- package/dist/htest/aov.js +178 -0
- package/dist/htest/binomTest.d.ts +36 -0
- package/dist/htest/binomTest.js +121 -0
- package/dist/htest/cor.d.ts +14 -0
- package/dist/htest/cor.js +4 -3
- package/dist/htest/core/ansari.d.ts +18 -0
- package/dist/htest/core/ansari.js +121 -0
- package/dist/htest/core/kolmogorov.d.ts +26 -0
- package/dist/htest/core/kolmogorov.js +221 -0
- package/dist/htest/core/mantelhaen.d.ts +26 -0
- package/dist/htest/core/mantelhaen.js +145 -0
- package/dist/htest/core/shapiro.d.ts +24 -0
- package/dist/htest/core/shapiro.js +142 -0
- package/dist/htest/core/smirnov.d.ts +20 -0
- package/dist/htest/core/smirnov.js +75 -0
- package/dist/htest/fisherTest.js +134 -120
- package/dist/htest/groups.d.ts +20 -0
- package/dist/htest/groups.js +62 -0
- package/dist/htest/ksTest.d.ts +31 -0
- package/dist/htest/ksTest.js +106 -0
- package/dist/htest/mantelhaenTest.d.ts +26 -0
- package/dist/htest/mantelhaenTest.js +196 -0
- package/dist/htest/mcnemarTest.d.ts +26 -0
- package/dist/htest/mcnemarTest.js +99 -0
- package/dist/htest/onewayTest.d.ts +26 -0
- package/dist/htest/onewayTest.js +97 -0
- package/dist/htest/pairwise.d.ts +64 -0
- package/dist/htest/pairwise.js +152 -0
- package/dist/htest/poissonTest.d.ts +35 -0
- package/dist/htest/poissonTest.js +170 -0
- package/dist/htest/propTest.d.ts +37 -0
- package/dist/htest/propTest.js +164 -0
- package/dist/htest/rankTests.d.ts +52 -0
- package/dist/htest/rankTests.js +234 -0
- package/dist/htest/types.d.ts +2 -0
- package/dist/htest/types.js +6 -0
- package/dist/htest/varTest.d.ts +48 -0
- package/dist/htest/varTest.js +118 -0
- package/dist/htest/wilcoxTest.js +136 -174
- package/dist/index.d.ts +6 -50
- package/dist/index.js +8 -50
- package/dist/integrate/quadpack.d.ts +29 -0
- package/dist/integrate/quadpack.js +525 -0
- package/dist/internal/seq.d.ts +5 -0
- package/dist/internal/seq.js +14 -0
- package/dist/internal/uniroot.js +10 -71
- package/dist/linalg/lu.d.ts +45 -0
- package/dist/linalg/lu.js +88 -0
- package/dist/linalg/ops.d.ts +90 -0
- package/dist/linalg/ops.js +320 -0
- package/dist/linalg.js +2 -0
- package/dist/models/lm.d.ts +98 -0
- package/dist/models/lm.js +187 -0
- package/dist/numerics/deriv.d.ts +37 -0
- package/dist/numerics/deriv.js +150 -0
- package/dist/numerics/integrate.d.ts +91 -0
- package/dist/numerics/integrate.js +264 -0
- package/dist/numerics/interp.d.ts +103 -0
- package/dist/numerics/interp.js +581 -0
- package/dist/numerics/ode.d.ts +23 -0
- package/dist/numerics/ode.js +59 -0
- package/dist/numerics.js +2 -0
- package/dist/optim/localmin.d.ts +16 -0
- package/dist/optim/localmin.js +97 -0
- package/dist/random/beta.js +1 -1
- package/dist/random/binomial.js +1 -1
- package/dist/random/continuous.js +1 -1
- package/dist/random/extra.d.ts +33 -0
- package/dist/random/extra.js +62 -0
- package/dist/random/gamma.js +1 -1
- package/dist/random/hyper.js +1 -1
- package/dist/random/mt.js +0 -1
- package/dist/random/poisson.js +0 -1
- package/dist/random/rank.js +1 -1
- package/dist/random/rng.js +1 -1
- package/dist/random/sample.js +1 -1
- package/dist/rstats.js +2 -2
- package/dist/special/besselIK.d.ts +38 -0
- package/dist/special/besselIK.js +315 -0
- package/dist/special/besselJY.d.ts +32 -0
- package/dist/special/besselJY.js +508 -0
- package/dist/special/besselSeries.d.ts +8 -0
- package/dist/special/besselSeries.js +24 -0
- package/dist/special/beta.js +22 -5
- package/dist/special/choose.d.ts +75 -0
- package/dist/special/choose.js +233 -0
- package/dist/special/erf.d.ts +28 -2
- package/dist/special/erf.js +37 -10
- package/dist/special/extra.d.ts +47 -0
- package/dist/special/extra.js +220 -0
- package/dist/special/igam.d.ts +4 -0
- package/dist/special/igam.js +57 -2
- package/dist/special/incbet.js +77 -22
- package/dist/special/lgammaDiff.d.ts +7 -0
- package/dist/special/lgammaDiff.js +16 -3
- package/dist/special/psigamma.d.ts +48 -0
- package/dist/special/psigamma.js +318 -0
- package/dist/special/rCompatible.d.ts +8 -0
- package/dist/special/rCompatible.js +1 -0
- package/dist/special.js +2 -0
- package/dist/tests.js +2 -0
- package/package.json +26 -1
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
// Binomial coefficients and factorials: choose, lchoose, factorial, lfactorial.
|
|
2
|
+
//
|
|
3
|
+
// Written for rStats.js from the definitions (no ported code):
|
|
4
|
+
// choose(n, k) = n (n - 1) ... (n - k + 1) / k! for integer k >= 1, 1 for k = 0, 0 for k < 0,
|
|
5
|
+
// with n any real number. For small k the product is formed in double-double. For larger k it is
|
|
6
|
+
// choose(n, k) = 1 / ((n + 1) B(n - k + 1, k + 1)) (n >= k - 1)
|
|
7
|
+
// choose(n, k) = (-1)^(k+1) sin(pi n) / pi * B(n + 1, k - n) (0 <= n < k - 1, n not an integer)
|
|
8
|
+
// choose(n, k) = (-1)^k / ((k - n) B(-n, k + 1)) (n < 0)
|
|
9
|
+
// where the second line follows from the reflection formula Gamma(z) Gamma(1 - z) = pi / sin(pi z).
|
|
10
|
+
// log B is taken from lbetaDD (double-double), since its value can be in the hundreds and exp would turn the
|
|
11
|
+
// rounding of a double log into a relative error of 1e-13.
|
|
12
|
+
import { nonInt, roundHalfEven } from "../internal/integer.js";
|
|
13
|
+
import { lbeta, lbetaDD } from "./beta.js";
|
|
14
|
+
import { ddAdd, ddDiv, ddLog, ddMul, twoSum } from "./dd.js";
|
|
15
|
+
import { gamma, lgamma, lgamma1p, sinpi } from "./gamma.js";
|
|
16
|
+
import { digamma } from "./psigamma.js";
|
|
17
|
+
/** Below this k, choose is the product n (n-1) ... (n-k+1) / k! */
|
|
18
|
+
const K_SMALL = 30;
|
|
19
|
+
function isInt(x) {
|
|
20
|
+
return x === Math.floor(x);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The n that R uses in choose (k >= K_SMALL) and lchoose (k >= 2): R reflects n < 0 to k - 1 - n before
|
|
24
|
+
* testing for an integer, so there the tolerance is relative to k - 1 - n. Returns n unchanged when R
|
|
25
|
+
* keeps it; a snapped value of -0 stands for the reflected n rounding to k - 1 (choose is then ±0).
|
|
26
|
+
*/
|
|
27
|
+
function rSnap(n, k) {
|
|
28
|
+
if (isInt(n))
|
|
29
|
+
return n;
|
|
30
|
+
if (n >= 0)
|
|
31
|
+
return nonInt(n) ? n : roundHalfEven(n);
|
|
32
|
+
const m = k - 1 - n;
|
|
33
|
+
return nonInt(m) ? n : k - 1 - roundHalfEven(m) || -0;
|
|
34
|
+
}
|
|
35
|
+
/** n (n-1) ... (n-k+1) / k! in double-double (1 <= k < K_SMALL) */
|
|
36
|
+
function fallingRatio(n, k) {
|
|
37
|
+
let r = [1, 0];
|
|
38
|
+
for (let j = 0; j < k; j++)
|
|
39
|
+
r = ddDiv(ddMul(r, twoSum(n, -j)), [j + 1, 0]);
|
|
40
|
+
return r;
|
|
41
|
+
}
|
|
42
|
+
/** log B(a, b) (a, b > 0): double-double where lbetaDD is accurate, else lbeta (whose value is then large) */
|
|
43
|
+
function logBeta(a, b) {
|
|
44
|
+
return Math.min(a, b) >= 1 && Math.max(a, b) < 2 ** 52 ? lbetaDD(a, b) : [lbeta(a, b), 0];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* log|choose(n, k)| in double-double and the sign, for k >= K_SMALL and choose(n, k) != 0.
|
|
48
|
+
* Returns undefined when choose(n, k) is 0 (integer n with 0 <= n < k)
|
|
49
|
+
*/
|
|
50
|
+
function logChooseLarge(n, k) {
|
|
51
|
+
if (n < 0) {
|
|
52
|
+
// choose(n, k) = (-1)^k choose(k - n - 1, k) = (-1)^k / ((k - n) B(-n, k + 1)). Forming k - n - 1 would round
|
|
53
|
+
// away the digits of a small n (4e-4 relative at n = -1e-10, k = 1000)
|
|
54
|
+
const l = ddAdd(ddLog(twoSum(k, -n)), logBeta(-n, k + 1));
|
|
55
|
+
return { log: [-l[0], -l[1]], sign: k % 2 === 0 ? 1 : -1 };
|
|
56
|
+
}
|
|
57
|
+
if (isInt(n) && n < k)
|
|
58
|
+
return undefined;
|
|
59
|
+
if (n >= k - 1) {
|
|
60
|
+
// -log(n + 1) - log B(n - k + 1, k + 1); n - k + 1 is exact for n in [k - 1, 2 (k - 1)]
|
|
61
|
+
const l = ddAdd(ddLog(twoSum(n, 1)), logBeta(n - k + 1, k + 1));
|
|
62
|
+
return { log: [-l[0], -l[1]], sign: 1 };
|
|
63
|
+
}
|
|
64
|
+
// 0 <= n < k - 1, n not an integer: (-1)^(k+1) sin(pi n) / pi * B(n + 1, k - n). n + 1 and k - n round when they
|
|
65
|
+
// lie in a higher binade than n (1.4e-14 at n = 191.38, k = 503; 3.9e-14 at n = 255.9, k = 1001): put the roundings
|
|
66
|
+
// back to first order, d/da log B(a, b) = psi(a) - psi(a + b)
|
|
67
|
+
const s = sinpi(n);
|
|
68
|
+
const [ah, al] = twoSum(n, 1);
|
|
69
|
+
const [bh, bl] = twoSum(k, -n);
|
|
70
|
+
const dab = al === 0 && bl === 0 ? 0 : digamma(ah + bh);
|
|
71
|
+
const corr = (al === 0 ? 0 : al * (digamma(ah) - dab)) + (bl === 0 ? 0 : bl * (digamma(bh) - dab));
|
|
72
|
+
const l = ddAdd(logBeta(ah, bh), [Math.log(Math.abs(s) / Math.PI) + corr, 0]);
|
|
73
|
+
return { log: l, sign: (k % 2 === 0 ? -1 : 1) * Math.sign(s) };
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Binomial coefficient. Equivalent to R's `choose(n, k)` for single numbers.
|
|
77
|
+
*
|
|
78
|
+
* `n` may be any real number. `k` is rounded to the nearest integer (as R does, which also warns
|
|
79
|
+
* when `k` is not close to an integer); `choose(n, k)` is 0 for `k < 0` and 1 for `k = 0`.
|
|
80
|
+
* For integer `n` the result is an integer (exact up to 2^53).
|
|
81
|
+
*
|
|
82
|
+
* Edge cases where R differs from the mathematical value, followed by default (`rCompatible: false`
|
|
83
|
+
* returns the mathematical value instead):
|
|
84
|
+
* - `n` within 1e-9 max(1, |n|) of an integer is treated as that integer, so `choose(1e-10, 2)` is 0
|
|
85
|
+
* (mathematically -5e-11); for `n < 0` and `k >= 30` the tolerance is relative to `k - 1 - n`;
|
|
86
|
+
* - `choose(n, Infinity)` is 0 for `n >= 0` (otherwise `NaN`);
|
|
87
|
+
* - `choose(±Infinity, k)` is `NaN` for `k >= 30` (`±Infinity` below).
|
|
88
|
+
*
|
|
89
|
+
* @param n - Number of items (any real number).
|
|
90
|
+
* @param k - Number chosen (rounded to an integer).
|
|
91
|
+
* @param options - `rCompatible` (default `true`).
|
|
92
|
+
* @returns The binomial coefficient.
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```ts
|
|
96
|
+
* choose(5, 2); // 10
|
|
97
|
+
* choose(0.5, 3); // 0.0625
|
|
98
|
+
* choose(-2, 3); // -4
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
export function choose(n, k, options) {
|
|
102
|
+
if (Number.isNaN(n) || Number.isNaN(k))
|
|
103
|
+
return NaN;
|
|
104
|
+
const rc = options?.rCompatible ?? true;
|
|
105
|
+
k = roundHalfEven(k);
|
|
106
|
+
if (k < 0)
|
|
107
|
+
return 0;
|
|
108
|
+
if (k === 0)
|
|
109
|
+
return 1;
|
|
110
|
+
if (!Number.isFinite(k))
|
|
111
|
+
return rc && n >= 0 && n < Infinity ? 0 : NaN;
|
|
112
|
+
if (!Number.isFinite(n))
|
|
113
|
+
return rc && k >= K_SMALL ? NaN : n > 0 || k % 2 === 0 ? Infinity : -Infinity;
|
|
114
|
+
if (k < K_SMALL) {
|
|
115
|
+
// R treats n within 1e-9 max(1, |n|) of an integer as an integer (src/internal/integer.ts)
|
|
116
|
+
const snap = rc && !isInt(n) && !nonInt(n);
|
|
117
|
+
// R keeps n in the product and rounds the result (choose(1e15 + 0.125, 2) is not choose(1e15, 2))
|
|
118
|
+
if (snap && n >= 0 && roundHalfEven(n) < k)
|
|
119
|
+
return 0;
|
|
120
|
+
const r = fallingRatio(n, k);
|
|
121
|
+
const v = r[0] + r[1];
|
|
122
|
+
if (Number.isFinite(v))
|
|
123
|
+
return isInt(n) || snap ? Math.round(v) : v;
|
|
124
|
+
// Overflow (the double-double steps then give NaN): the plain product overflows too
|
|
125
|
+
let p = 1;
|
|
126
|
+
for (let j = 0; j < k; j++)
|
|
127
|
+
p = (p * (n - j)) / (j + 1);
|
|
128
|
+
return p;
|
|
129
|
+
}
|
|
130
|
+
if (rc) {
|
|
131
|
+
n = rSnap(n, k);
|
|
132
|
+
// R reflects n < 0 that rounds to -0 into choose(k - 1, k) = 0, with the sign (-1)^k
|
|
133
|
+
if (Object.is(n, -0))
|
|
134
|
+
return k % 2 === 0 ? 0 : -0;
|
|
135
|
+
}
|
|
136
|
+
const r = logChooseLarge(n, k);
|
|
137
|
+
if (r === undefined)
|
|
138
|
+
return 0;
|
|
139
|
+
const v = r.sign * Math.exp(r.log[0]) * (1 + r.log[1]);
|
|
140
|
+
return isInt(n) && Number.isFinite(v) ? Math.round(v) : v;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Logarithm of the absolute value of the binomial coefficient. Equivalent to R's `lchoose(n, k)`
|
|
144
|
+
* for single numbers: `log(abs(choose(n, k)))`, computed without overflow.
|
|
145
|
+
*
|
|
146
|
+
* Edge cases where R differs from the mathematical value, followed by default (`rCompatible: false`
|
|
147
|
+
* returns the mathematical value instead): `n` within 1e-9 max(1, |n|) of an integer is treated as that
|
|
148
|
+
* integer (for `k >= 2`; for `n < 0` the tolerance is relative to `k - 1 - n`); `lchoose(n, Infinity)` is `-Infinity` for `n >= 0` (otherwise `NaN`); `lchoose(±Infinity, k)`
|
|
149
|
+
* is `NaN` for `k >= 2` (`Infinity` for `k = 1`).
|
|
150
|
+
*
|
|
151
|
+
* @param n - Number of items (any real number).
|
|
152
|
+
* @param k - Number chosen (rounded to an integer).
|
|
153
|
+
* @param options - `rCompatible` (default `true`).
|
|
154
|
+
* @returns log|choose(n, k)| (`-Infinity` where `choose(n, k)` is 0).
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* ```ts
|
|
158
|
+
* lchoose(1000, 500); // 689.4672615678512
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
export function lchoose(n, k, options) {
|
|
162
|
+
if (Number.isNaN(n) || Number.isNaN(k))
|
|
163
|
+
return NaN;
|
|
164
|
+
const rc = options?.rCompatible ?? true;
|
|
165
|
+
k = roundHalfEven(k);
|
|
166
|
+
if (k < 0)
|
|
167
|
+
return -Infinity;
|
|
168
|
+
if (k === 0)
|
|
169
|
+
return 0;
|
|
170
|
+
if (!Number.isFinite(k))
|
|
171
|
+
return rc && n >= 0 && n < Infinity ? -Infinity : NaN;
|
|
172
|
+
if (!Number.isFinite(n))
|
|
173
|
+
return rc && k >= 2 ? NaN : Infinity;
|
|
174
|
+
// R's lchoose(n, 1) is log|n| without rounding n; for k >= 2 it rounds n itself (unlike choose for k < 30)
|
|
175
|
+
if (rc && k >= 2)
|
|
176
|
+
n = rSnap(n, k);
|
|
177
|
+
if (k < K_SMALL) {
|
|
178
|
+
const r = fallingRatio(n, k);
|
|
179
|
+
const v = Math.abs(r[0] + r[1]);
|
|
180
|
+
if (v > 1e-300 && v < 1e300) {
|
|
181
|
+
// log of a double-double value
|
|
182
|
+
const l = ddLog(r[0] < 0 ? [-r[0], -r[1]] : r);
|
|
183
|
+
return l[0] + l[1];
|
|
184
|
+
}
|
|
185
|
+
if (v === 0)
|
|
186
|
+
return -Infinity;
|
|
187
|
+
// Out of range: sum of logs (the result is large in magnitude, so its rounding is relatively small)
|
|
188
|
+
let s = -lgamma(k + 1);
|
|
189
|
+
for (let j = 0; j < k; j++)
|
|
190
|
+
s += Math.log(Math.abs(n - j));
|
|
191
|
+
return s;
|
|
192
|
+
}
|
|
193
|
+
const r = logChooseLarge(n, k);
|
|
194
|
+
if (r === undefined)
|
|
195
|
+
return -Infinity;
|
|
196
|
+
// For integer n the coefficient is an integer; below 2^53 take the log of the exact value (so that lchoose(n, n) is 0)
|
|
197
|
+
if (isInt(n) && r.log[0] < 36)
|
|
198
|
+
return Math.log(Math.round(Math.exp(r.log[0]) * (1 + r.log[1])));
|
|
199
|
+
return r.log[0] + r.log[1];
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Factorial. Equivalent to R's `factorial(x)` for a single number: `gamma(x + 1)`, defined for
|
|
203
|
+
* any real `x` that is not a negative integer.
|
|
204
|
+
*
|
|
205
|
+
* @param x - Argument.
|
|
206
|
+
* @returns x! = Gamma(x + 1) (`NaN` at negative integers, `Infinity` on overflow).
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* ```ts
|
|
210
|
+
* factorial(5); // 120
|
|
211
|
+
* factorial(0.5); // 0.8862269254527579
|
|
212
|
+
* ```
|
|
213
|
+
*/
|
|
214
|
+
export function factorial(x) {
|
|
215
|
+
return gamma(x + 1);
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Logarithm of the factorial. Equivalent to R's `lfactorial(x)` for a single number:
|
|
219
|
+
* `lgamma(x + 1)`, computed without forming `x + 1` near 0 so that small `x` keep their accuracy.
|
|
220
|
+
*
|
|
221
|
+
* @param x - Argument.
|
|
222
|
+
* @returns log|x!| = log|Gamma(x + 1)| (`Infinity` at negative integers).
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* ```ts
|
|
226
|
+
* lfactorial(100); // 363.73937555556347
|
|
227
|
+
* ```
|
|
228
|
+
*/
|
|
229
|
+
export function lfactorial(x) {
|
|
230
|
+
if (Math.abs(x) <= 0.2)
|
|
231
|
+
return lgamma1p(x);
|
|
232
|
+
return lgamma(x + 1);
|
|
233
|
+
}
|
package/dist/special/erf.d.ts
CHANGED
|
@@ -1,6 +1,32 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Error function, erf(x) = 2 / sqrt(pi) * integral from 0 to x of exp(-t^2) dt. Not part of R (where it is written
|
|
3
|
+
* `2 * pnorm(x * sqrt(2)) - 1`, which loses the relative accuracy of small x).
|
|
4
|
+
*
|
|
5
|
+
* @param x - Argument.
|
|
6
|
+
* @returns erf(x).
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* erf(0.5); // 0.5204998778130465
|
|
11
|
+
* erf(1e-10); // 1.1283791670955126e-10
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
2
14
|
export declare function erf(x: number): number;
|
|
3
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* Complementary error function, erfc(x) = 1 - erf(x), accurate in the upper tail (down to the subnormal range near
|
|
17
|
+
* x = 27). Not part of R (where it is written `2 * pnorm(x * sqrt(2), lower.tail = FALSE)`).
|
|
18
|
+
*
|
|
19
|
+
* @param a - Argument.
|
|
20
|
+
* @returns erfc(a).
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* erfc(5); // 1.537459794428035e-12
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* Changes from xsf: exp(-x^2) is formed from x^2 = hi + lo exact (the rounding of x^2 is 33 ε relative at x = 5.7),
|
|
28
|
+
* and below the normal range exp(-700) is applied last so that subnormal results (x up to 27.2) are not flushed to 0.
|
|
29
|
+
*/
|
|
4
30
|
export declare function erfc(a: number): number;
|
|
5
31
|
/**
|
|
6
32
|
* Scaled complementary error function erfcx(x) = exp(x^2) erfc(x) (x >= 0).
|
package/dist/special/erf.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
// approximations directly. This gives the tail value before multiplying by exp(-x^2), usable for computations
|
|
8
8
|
// that take the log (logP in pnorm) or that recompute exp(-x^2) accurately.
|
|
9
9
|
import { p1evl, polevl } from "./polevl.js";
|
|
10
|
+
import { twoProd } from "./dd.js";
|
|
10
11
|
const P = [
|
|
11
12
|
2.46196981473530512524e-10, 5.64189564831068821977e-1, 7.46321056442269912687e0,
|
|
12
13
|
4.86371970985681366614e1, 1.96520832956077098242e2, 5.26445194995477358631e2,
|
|
@@ -37,7 +38,19 @@ const U = [
|
|
|
37
38
|
const MAXLOG = 7.097827128933839730962063185871e2;
|
|
38
39
|
/** 1/sqrt(pi) */
|
|
39
40
|
const INV_SQRT_PI = 0.5641895835477562869;
|
|
40
|
-
/**
|
|
41
|
+
/**
|
|
42
|
+
* Error function, erf(x) = 2 / sqrt(pi) * integral from 0 to x of exp(-t^2) dt. Not part of R (where it is written
|
|
43
|
+
* `2 * pnorm(x * sqrt(2)) - 1`, which loses the relative accuracy of small x).
|
|
44
|
+
*
|
|
45
|
+
* @param x - Argument.
|
|
46
|
+
* @returns erf(x).
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```ts
|
|
50
|
+
* erf(0.5); // 0.5204998778130465
|
|
51
|
+
* erf(1e-10); // 1.1283791670955126e-10
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
41
54
|
export function erf(x) {
|
|
42
55
|
if (Number.isNaN(x))
|
|
43
56
|
return NaN;
|
|
@@ -48,22 +61,36 @@ export function erf(x) {
|
|
|
48
61
|
const z = x * x;
|
|
49
62
|
return (x * polevl(z, T)) / p1evl(z, U);
|
|
50
63
|
}
|
|
51
|
-
/**
|
|
64
|
+
/**
|
|
65
|
+
* Complementary error function, erfc(x) = 1 - erf(x), accurate in the upper tail (down to the subnormal range near
|
|
66
|
+
* x = 27). Not part of R (where it is written `2 * pnorm(x * sqrt(2), lower.tail = FALSE)`).
|
|
67
|
+
*
|
|
68
|
+
* @param a - Argument.
|
|
69
|
+
* @returns erfc(a).
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```ts
|
|
73
|
+
* erfc(5); // 1.537459794428035e-12
|
|
74
|
+
* ```
|
|
75
|
+
*
|
|
76
|
+
* Changes from xsf: exp(-x^2) is formed from x^2 = hi + lo exact (the rounding of x^2 is 33 ε relative at x = 5.7),
|
|
77
|
+
* and below the normal range exp(-700) is applied last so that subnormal results (x up to 27.2) are not flushed to 0.
|
|
78
|
+
*/
|
|
52
79
|
export function erfc(a) {
|
|
53
80
|
if (Number.isNaN(a))
|
|
54
81
|
return NaN;
|
|
55
82
|
const x = Math.abs(a);
|
|
56
83
|
if (x < 1)
|
|
57
84
|
return 1 - erf(a);
|
|
58
|
-
const z = -a * a;
|
|
59
|
-
if (z < -MAXLOG)
|
|
60
|
-
return a < 0 ? 2 : 0;
|
|
61
|
-
let y = Math.exp(z) * erfcxRational(x);
|
|
62
85
|
if (a < 0)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
86
|
+
return 2 - erfc(x);
|
|
87
|
+
const [hi, lo] = twoProd(x, x);
|
|
88
|
+
if (hi > MAXLOG + 40)
|
|
89
|
+
return 0;
|
|
90
|
+
const r = erfcxRational(x) * (1 - lo);
|
|
91
|
+
if (hi < 700)
|
|
92
|
+
return Math.exp(-hi) * r;
|
|
93
|
+
return Math.exp(700 - hi) * r * Math.exp(-700);
|
|
67
94
|
}
|
|
68
95
|
/**
|
|
69
96
|
* Scaled complementary error function erfcx(x) = exp(x^2) erfc(x) (x >= 0).
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/** Result of {@link gammainc}. */
|
|
2
|
+
export interface GammaincResult {
|
|
3
|
+
/** Lower incomplete gamma function gamma(a, x) = integral from 0 to x of t^(a-1) e^-t dt. */
|
|
4
|
+
lower: number;
|
|
5
|
+
/** Upper incomplete gamma function Gamma(a, x) = integral from x to Inf of t^(a-1) e^-t dt. */
|
|
6
|
+
upper: number;
|
|
7
|
+
/** log of `lower` (finite where `lower` overflows). */
|
|
8
|
+
logLower: number;
|
|
9
|
+
/** log of `upper`. */
|
|
10
|
+
logUpper: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The non-regularized incomplete gamma functions gamma(a, x) and Gamma(a, x) and their logs. As
|
|
14
|
+
* `pracma::gammainc(x, a)` (`lowinc`, `uppinc`), or R's `pgamma(x, a) * gamma(a)` and
|
|
15
|
+
* `pgamma(x, a, lower.tail = FALSE) * gamma(a)`.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* gammainc(2, 3).lower; // 0.6466471676338731 = 2 - 10 e^-2
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare function gammainc(x: number, a: number): GammaincResult;
|
|
23
|
+
/**
|
|
24
|
+
* Inverse of the complementary error function: the x with erfc(x) = z, for 0 <= z <= 2 (NaN outside). As
|
|
25
|
+
* `pracma::erfcinv`.
|
|
26
|
+
*/
|
|
27
|
+
export declare function erfcinv(z: number): number;
|
|
28
|
+
/**
|
|
29
|
+
* Inverse of the error function: the x with erf(x) = y, for -1 <= y <= 1 (NaN outside). As `pracma::erfinv`, but
|
|
30
|
+
* accurate for small |y| (pracma's `qnorm((1 + y) / 2)` loses it: `erfinv(1e-300)` is -0 there).
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* erfinv(0.5); // 0.4769362762044699
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export declare function erfinv(y: number): number;
|
|
38
|
+
/**
|
|
39
|
+
* Number of ordered selections of k items from n: n! / (n - k)!, defined for real n >= 0 as
|
|
40
|
+
* Gamma(n + 1) / Gamma(n - k + 1) (0 for an integer n < k). jStat's `permutation`. Negative arguments give NaN.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* perm(5, 2); // 20
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export declare function perm(n: number, k: number): number;
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
// The non-regularized incomplete gamma functions, the inverse error functions and the number of permutations:
|
|
2
|
+
// gammainc, erfinv, erfcinv, perm.
|
|
3
|
+
//
|
|
4
|
+
// Written for rStats.js from the definitions (no ported code).
|
|
5
|
+
// - gammainc: gamma(a) times the regularized P(a, x) and Q(a, x) of src/special/igam.ts. Where the product over- or
|
|
6
|
+
// underflows but the value does not (a above 171.6), the smaller tail is x^a e^-x times the series (x < a) or the
|
|
7
|
+
// continued fraction (x > a) of igam.ts, with the exponent a log x - x in double-double (exp(lgamma(a) + log P)
|
|
8
|
+
// turned the rounding of its terms, up to 1e5 in size, into a relative error of 1e-12).
|
|
9
|
+
// - erfcinv(z) = -qnorm(z / 2) / sqrt(2) (exact halving; qnorm is accurate in both tails); erfinv(y) = erfcinv(1 - y)
|
|
10
|
+
// for |y| >= 1/2 (1 - |y| is exact there), and for smaller |y| that value refined by Newton steps on erf (the
|
|
11
|
+
// form through qnorm((1 + y) / 2) loses the relative accuracy of a small y).
|
|
12
|
+
// - perm(n, k) = n (n - 1) ... (n - k + 1) = Gamma(n + 1) / Gamma(n - k + 1): the product formed in double-double
|
|
13
|
+
// for integer k up to 1000 (+-Infinity once it overflows), otherwise the ratio of gamma functions with its log in
|
|
14
|
+
// double-double: for n - k >= 15 from Stirling's formula through Loader's stirlerr and bd0 (src/special/loader.ts),
|
|
15
|
+
// which does not cancel as lgamma(n + 1) - lgamma(n - k + 1) does, and for n - k + 1 <= 0 by the reflection formula.
|
|
16
|
+
import { qnorm } from "../distributions/normal.js";
|
|
17
|
+
import { ddAdd, ddLog, ddMul, ddMulNum, ddSub, twoSum } from "./dd.js";
|
|
18
|
+
import { erf } from "./erf.js";
|
|
19
|
+
import { gamma, lgamma, lgammaSign, sinpi } from "./gamma.js";
|
|
20
|
+
import { gammaInc, igamcContinuedFractionValue, igamSeriesSum } from "./igam.js";
|
|
21
|
+
import { bd0dd, stirlerr } from "./loader.js";
|
|
22
|
+
import { digamma } from "./psigamma.js";
|
|
23
|
+
/**
|
|
24
|
+
* The non-regularized incomplete gamma functions gamma(a, x) and Gamma(a, x) and their logs. As
|
|
25
|
+
* `pracma::gammainc(x, a)` (`lowinc`, `uppinc`), or R's `pgamma(x, a) * gamma(a)` and
|
|
26
|
+
* `pgamma(x, a, lower.tail = FALSE) * gamma(a)`.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* gammainc(2, 3).lower; // 0.6466471676338731 = 2 - 10 e^-2
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export function gammainc(x, a) {
|
|
34
|
+
if (Number.isNaN(x) || Number.isNaN(a) || x < 0 || !(a > 0))
|
|
35
|
+
return { lower: NaN, upper: NaN, logLower: NaN, logUpper: NaN };
|
|
36
|
+
const lg = lgamma(a);
|
|
37
|
+
const g = gamma(a);
|
|
38
|
+
const tail = (lower) => {
|
|
39
|
+
const p = gammaInc(a, x, lower, false);
|
|
40
|
+
const v = g * p;
|
|
41
|
+
// the product where it is a normal number (then also its log: lgamma(a) + log P can cancel)
|
|
42
|
+
if (Number.isFinite(g) && p > DBL_MIN && Number.isFinite(v) && v > DBL_MIN)
|
|
43
|
+
return [v, Math.log(v)];
|
|
44
|
+
const log = lg + gammaInc(a, x, lower, true);
|
|
45
|
+
// the smaller tail, unless the value over- or underflows anyway (its log is then large, and lg + log P is accurate
|
|
46
|
+
// relative to it)
|
|
47
|
+
if (log < 710 && log > -746 && x > 0 && (lower ? x < a : x > a))
|
|
48
|
+
return smallTail(x, a, lower);
|
|
49
|
+
return [Math.exp(log), log];
|
|
50
|
+
};
|
|
51
|
+
const [lower, logLower] = tail(true);
|
|
52
|
+
const [upper, logUpper] = tail(false);
|
|
53
|
+
return { lower, upper, logLower, logUpper };
|
|
54
|
+
}
|
|
55
|
+
const DBL_MIN = 2.2250738585072014e-308;
|
|
56
|
+
/**
|
|
57
|
+
* [value, log] of gamma(a, x) for x < a (lower) or Gamma(a, x) for x > a: x^a e^-x / a times the series
|
|
58
|
+
* sum_n x^n / ((a + 1) ... (a + n)), or x^a e^-x times the continued fraction. The exponent is formed in
|
|
59
|
+
* double-double together with the logs of the other factors, and exp is taken once
|
|
60
|
+
*/
|
|
61
|
+
function smallTail(x, a, lower) {
|
|
62
|
+
let e = ddAdd(ddMulNum(ddLog([x, 0]), a), [-x, 0]);
|
|
63
|
+
if (lower)
|
|
64
|
+
e = ddAdd(e, ddLog([igamSeriesSum(a, x) / a, 0]));
|
|
65
|
+
else
|
|
66
|
+
e = ddAdd(e, ddLog([igamcContinuedFractionValue(a, x), 0]));
|
|
67
|
+
const log = e[0] + e[1];
|
|
68
|
+
// exp(e0 + e1) = exp(e0) (1 + e1); in the subnormal range the last digits are lost anyway
|
|
69
|
+
const v = e[0] > -708 ? Math.exp(e[0]) * (1 + e[1]) : Math.exp(log);
|
|
70
|
+
return [v, log];
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Inverse of the complementary error function: the x with erfc(x) = z, for 0 <= z <= 2 (NaN outside). As
|
|
74
|
+
* `pracma::erfcinv`.
|
|
75
|
+
*/
|
|
76
|
+
export function erfcinv(z) {
|
|
77
|
+
if (Number.isNaN(z) || z < 0 || z > 2)
|
|
78
|
+
return NaN;
|
|
79
|
+
return -qnorm(z / 2) * Math.SQRT1_2;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Inverse of the error function: the x with erf(x) = y, for -1 <= y <= 1 (NaN outside). As `pracma::erfinv`, but
|
|
83
|
+
* accurate for small |y| (pracma's `qnorm((1 + y) / 2)` loses it: `erfinv(1e-300)` is -0 there).
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```ts
|
|
87
|
+
* erfinv(0.5); // 0.4769362762044699
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
export function erfinv(y) {
|
|
91
|
+
if (Number.isNaN(y) || y < -1 || y > 1)
|
|
92
|
+
return NaN;
|
|
93
|
+
const s = y < 0 ? -1 : 1;
|
|
94
|
+
const a = Math.abs(y);
|
|
95
|
+
if (a >= 0.5)
|
|
96
|
+
return s * erfcinv(1 - a);
|
|
97
|
+
if (a < 1e-8)
|
|
98
|
+
return y * 0.886226925452758 * (1 + (Math.PI / 12) * y * y); // sqrt(pi)/2 (y + pi y^3 / 12)
|
|
99
|
+
let x = erfcinv(1 - a);
|
|
100
|
+
for (let i = 0; i < 3; i++) {
|
|
101
|
+
const d = (erf(x) - a) / (1.1283791670955126 * Math.exp(-x * x)); // erf' = 2/sqrt(pi) e^(-x^2)
|
|
102
|
+
x -= d;
|
|
103
|
+
if (Math.abs(d) <= 1e-17 * x)
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
return s * x;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Number of ordered selections of k items from n: n! / (n - k)!, defined for real n >= 0 as
|
|
110
|
+
* Gamma(n + 1) / Gamma(n - k + 1) (0 for an integer n < k). jStat's `permutation`. Negative arguments give NaN.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```ts
|
|
114
|
+
* perm(5, 2); // 20
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
export function perm(n, k) {
|
|
118
|
+
if (Number.isNaN(n) || Number.isNaN(k) || n < 0 || k < 0)
|
|
119
|
+
return NaN;
|
|
120
|
+
const intK = k === Math.floor(k);
|
|
121
|
+
if (n === Math.floor(n) && intK && k > n)
|
|
122
|
+
return 0;
|
|
123
|
+
if (!Number.isFinite(n) || !Number.isFinite(k))
|
|
124
|
+
return permLgamma(n, k);
|
|
125
|
+
if (intK && k <= 1000)
|
|
126
|
+
return fallingFactorial(n, k);
|
|
127
|
+
return gammaRatio(n, k);
|
|
128
|
+
}
|
|
129
|
+
/** n (n - 1) ... (n - k + 1) in double-double (integer k; no factor is 0), +-Infinity once it overflows */
|
|
130
|
+
function fallingFactorial(n, k) {
|
|
131
|
+
let p = [1, 0];
|
|
132
|
+
for (let i = 0; i < k; i++) {
|
|
133
|
+
const f = twoSum(n, -i); // n - i exactly
|
|
134
|
+
const hi = p[0] * f[0];
|
|
135
|
+
if (!Number.isFinite(hi)) {
|
|
136
|
+
// the sign of the rest of the product decides the sign of the infinity
|
|
137
|
+
let sign = Math.sign(hi);
|
|
138
|
+
for (let j = i + 1; j < k; j++)
|
|
139
|
+
if (n - j < 0)
|
|
140
|
+
sign = -sign;
|
|
141
|
+
return sign * Infinity;
|
|
142
|
+
}
|
|
143
|
+
// Veltkamp splitting overflows above about 1e300: the last steps before overflow in double precision
|
|
144
|
+
p = Math.abs(p[0]) > 1e290 || Math.abs(f[0]) > 1e290 ? [(p[0] + p[1]) * (f[0] + f[1]), 0] : ddMul(p, f);
|
|
145
|
+
}
|
|
146
|
+
return p[0] + p[1];
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Gamma(n + 1) / Gamma(m + 1), m = n - k (as mh + ml exactly). For n > 170 Stirling's formula
|
|
150
|
+
* Gamma(z + 1) = sqrt(2 pi z) (z / e)^z exp(stirlerr(z)) gives, with n = mh + kk,
|
|
151
|
+
* log ratio = kk log n - bd0(mh, n) + stirlerr(n) - stirlerr(mh), bd0(mh, n) = mh log(mh / n) + n - mh,
|
|
152
|
+
* formed in double-double without the cancellation of lgamma(n + 1) - lgamma(m + 1) (1e-9 relative at
|
|
153
|
+
* perm(1e6, 2.5)). The rounding ml of n - k is put back to first order, Gamma(m + 1) = Gamma(mh + 1) e^(ml psi(mh + 1));
|
|
154
|
+
* for m + 1 < 0 the reflection formula takes over (m + 1 can lie next to a pole of Gamma)
|
|
155
|
+
*/
|
|
156
|
+
function gammaRatio(n, k) {
|
|
157
|
+
const [mh, ml] = twoSum(n, -k);
|
|
158
|
+
if (mh + 1 <= 0)
|
|
159
|
+
return gammaRatioReflected(n, mh, ml);
|
|
160
|
+
// n^k (1 + O(k^2 / n)); a k large enough for the correction to matter overflows n^k
|
|
161
|
+
if (n > 1e280)
|
|
162
|
+
return k * k < 1e-17 * n ? Math.pow(n, k) : Infinity;
|
|
163
|
+
let r;
|
|
164
|
+
if (mh >= 15) {
|
|
165
|
+
// the ratio is at least (m + 1)^k: overflow before the terms get large
|
|
166
|
+
if (k * Math.log(mh + 1) > 720)
|
|
167
|
+
return Infinity;
|
|
168
|
+
const kk = twoSum(n, -mh);
|
|
169
|
+
let e = ddSub(ddMul(kk, ddLog([n, 0])), bd0dd(mh, [n, 0]));
|
|
170
|
+
e = ddAdd(e, [stirlerr(n) - stirlerr(mh), 0]);
|
|
171
|
+
r = Math.sqrt(n / mh) * Math.exp(e[0]) * (1 + e[1]);
|
|
172
|
+
}
|
|
173
|
+
else if (n < 30) {
|
|
174
|
+
r = gamma(n + 1) / gamma(mh + 1);
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
if (lgamma(n + 1) - lgamma(mh + 1) > 720)
|
|
178
|
+
return Infinity;
|
|
179
|
+
const e = ddSub(lgammaDD(n + 1), ddLog([gamma(mh + 1), 0]));
|
|
180
|
+
r = Math.exp(e[0]) * (1 + e[1]);
|
|
181
|
+
}
|
|
182
|
+
if (ml !== 0 && Number.isFinite(r) && r !== 0)
|
|
183
|
+
r *= Math.exp(-ml * digamma(mh + 1));
|
|
184
|
+
return r;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Gamma(n + 1) / Gamma(m + 1) for m + 1 = z <= 0: 1 / Gamma(z) = Gamma(1 - z) sin(pi z) / pi with 1 - z = -m.
|
|
188
|
+
* sin(pi z) takes the low part of z to first order (exact 0 at a pole, (-1)^j pi zl next to it)
|
|
189
|
+
*/
|
|
190
|
+
function gammaRatioReflected(n, mh, ml) {
|
|
191
|
+
const z = ddAdd(twoSum(mh, 1), [ml, 0]);
|
|
192
|
+
const s = sinpi(z[0]) + sinpi(z[0] + 0.5) * Math.PI * z[1];
|
|
193
|
+
if (s === 0)
|
|
194
|
+
return 0;
|
|
195
|
+
const sign = Math.sign(s);
|
|
196
|
+
// log(|s| / pi) in double-double too: next to a pole it is about -33, whose rounding would be 3e-15 relative
|
|
197
|
+
const ls = ddAdd(ddSub(ddLog([Math.abs(s), 0]), LOGPI_DD), [-ml * digamma(-mh), 0]);
|
|
198
|
+
if (lgamma(n + 1) + lgamma(-mh) + ls[0] > 720)
|
|
199
|
+
return sign * Infinity;
|
|
200
|
+
const e = ddAdd(ddAdd(lgammaDD(n + 1), lgammaDD(-mh)), ls);
|
|
201
|
+
return sign * Math.exp(e[0]) * (1 + e[1]);
|
|
202
|
+
}
|
|
203
|
+
const LS2PI_DD = [0.91893853320467278, -3.8782941580672414e-17];
|
|
204
|
+
const LOGPI_DD = [1.1447298858494002, 1.0265951162707826e-17];
|
|
205
|
+
/**
|
|
206
|
+
* lgamma(z) in double-double for 0 < z < 1e280: the log of gamma(z) below 15, above it
|
|
207
|
+
* (z - 1/2) log z - z + log sqrt(2 pi) + stirlerr(z) (stirlerr(z) = lgamma(z + 1) - (z + 1/2) log z + z - log sqrt(2 pi))
|
|
208
|
+
*/
|
|
209
|
+
function lgammaDD(z) {
|
|
210
|
+
if (z < 15)
|
|
211
|
+
return ddLog([gamma(z), 0]);
|
|
212
|
+
const e = ddSub(ddMul(twoSum(z, -0.5), ddLog([z, 0])), [z, 0]);
|
|
213
|
+
return ddAdd(ddAdd(e, LS2PI_DD), [stirlerr(z), 0]);
|
|
214
|
+
}
|
|
215
|
+
/** Gamma(n + 1) / Gamma(n - k + 1) from lgamma (infinite n or k) */
|
|
216
|
+
function permLgamma(n, k) {
|
|
217
|
+
const num = lgammaSign(n + 1);
|
|
218
|
+
const den = lgammaSign(n - k + 1);
|
|
219
|
+
return num.sign * den.sign * Math.exp(num.value - den.value);
|
|
220
|
+
}
|
package/dist/special/igam.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/** sum_n x^n / ((a + 1) ... (a + n)): P(a, x) = x^a e^-x / Gamma(a + 1) times this */
|
|
2
|
+
export declare function igamSeriesSum(a: number, x: number): number;
|
|
3
|
+
/** The continued fraction of DLMF 8.9.2: Q(a, x) = x^a e^-x / Gamma(a) times this */
|
|
4
|
+
export declare function igamcContinuedFractionValue(a: number, x: number): number;
|
|
1
5
|
/**
|
|
2
6
|
* Return [log, linear] of gammaInc from a single series/continued-fraction computation.
|
|
3
7
|
* The values are bit-identical to gammaInc(.., true) and gammaInc(.., false) respectively
|