@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.
Files changed (168) hide show
  1. package/CHANGELOG.md +101 -0
  2. package/README.ja.md +36 -20
  3. package/README.md +34 -18
  4. package/THIRD_PARTY_NOTICES.md +25 -5
  5. package/dist/chunks/chunk-2JCIB5H2.js +2 -0
  6. package/dist/chunks/chunk-7JVSDGQQ.js +2 -0
  7. package/dist/chunks/chunk-7NK4SA6B.js +2 -0
  8. package/dist/chunks/chunk-AJYQNFXK.js +2 -0
  9. package/dist/chunks/chunk-DRNMIEYH.js +2 -0
  10. package/dist/chunks/chunk-NAYDS7CL.js +2 -0
  11. package/dist/chunks/chunk-NOCEAM44.js +2 -0
  12. package/dist/chunks/chunk-QNAIK4PY.js +2 -0
  13. package/dist/chunks/chunk-TPC6MTYC.js +2 -0
  14. package/dist/chunks/chunk-UGUQYWRQ.js +2 -0
  15. package/dist/chunks/chunk-XM2TVD63.js +2 -0
  16. package/dist/chunks/chunk-Y4IZV7XK.js +2 -0
  17. package/dist/chunks/chunk-ZWJ42QN6.js +2 -0
  18. package/dist/descriptive/basic.d.ts +73 -0
  19. package/dist/descriptive/basic.js +256 -0
  20. package/dist/descriptive/cov.d.ts +39 -0
  21. package/dist/descriptive/cov.js +105 -0
  22. package/dist/descriptive/hist.d.ts +64 -0
  23. package/dist/descriptive/hist.js +304 -0
  24. package/dist/descriptive/moments.d.ts +63 -0
  25. package/dist/descriptive/moments.js +158 -0
  26. package/dist/descriptive/quantile.d.ts +50 -0
  27. package/dist/descriptive/quantile.js +131 -0
  28. package/dist/descriptive/rank.d.ts +23 -0
  29. package/dist/descriptive/rank.js +72 -0
  30. package/dist/descriptive/table.d.ts +54 -0
  31. package/dist/descriptive/table.js +160 -0
  32. package/dist/descriptive.js +2 -0
  33. package/dist/distributions/arcsine.d.ts +24 -0
  34. package/dist/distributions/arcsine.js +73 -0
  35. package/dist/distributions/invgamma.d.ts +29 -0
  36. package/dist/distributions/invgamma.js +85 -0
  37. package/dist/distributions/kumaraswamy.d.ts +17 -0
  38. package/dist/distributions/kumaraswamy.js +96 -0
  39. package/dist/distributions/laplace.d.ts +23 -0
  40. package/dist/distributions/laplace.js +69 -0
  41. package/dist/distributions/multinomial.d.ts +24 -0
  42. package/dist/distributions/multinomial.js +70 -0
  43. package/dist/distributions/nbeta.js +28 -10
  44. package/dist/distributions/nchisq.js +20 -3
  45. package/dist/distributions/pareto.d.ts +23 -0
  46. package/dist/distributions/pareto.js +76 -0
  47. package/dist/distributions/summary.d.ts +25 -0
  48. package/dist/distributions/summary.js +289 -0
  49. package/dist/distributions/triangular.d.ts +25 -0
  50. package/dist/distributions/triangular.js +87 -0
  51. package/dist/distributions.js +2 -0
  52. package/dist/entries/descriptive.d.ts +7 -0
  53. package/dist/entries/descriptive.js +8 -0
  54. package/dist/entries/distributions.d.ts +41 -0
  55. package/dist/entries/distributions.js +41 -0
  56. package/dist/entries/linalg.d.ts +11 -0
  57. package/dist/entries/linalg.js +11 -0
  58. package/dist/entries/numerics.d.ts +8 -0
  59. package/dist/entries/numerics.js +6 -0
  60. package/dist/entries/special.d.ts +9 -0
  61. package/dist/entries/special.js +9 -0
  62. package/dist/entries/tests.d.ts +21 -0
  63. package/dist/entries/tests.js +20 -0
  64. package/dist/htest/ansariTest.d.ts +13 -0
  65. package/dist/htest/ansariTest.js +80 -0
  66. package/dist/htest/aov.d.ts +85 -0
  67. package/dist/htest/aov.js +178 -0
  68. package/dist/htest/binomTest.d.ts +36 -0
  69. package/dist/htest/binomTest.js +121 -0
  70. package/dist/htest/cor.d.ts +14 -0
  71. package/dist/htest/cor.js +4 -3
  72. package/dist/htest/core/ansari.d.ts +18 -0
  73. package/dist/htest/core/ansari.js +121 -0
  74. package/dist/htest/core/kolmogorov.d.ts +26 -0
  75. package/dist/htest/core/kolmogorov.js +221 -0
  76. package/dist/htest/core/mantelhaen.d.ts +26 -0
  77. package/dist/htest/core/mantelhaen.js +145 -0
  78. package/dist/htest/core/shapiro.d.ts +24 -0
  79. package/dist/htest/core/shapiro.js +142 -0
  80. package/dist/htest/core/smirnov.d.ts +20 -0
  81. package/dist/htest/core/smirnov.js +75 -0
  82. package/dist/htest/fisherTest.js +134 -120
  83. package/dist/htest/groups.d.ts +20 -0
  84. package/dist/htest/groups.js +62 -0
  85. package/dist/htest/ksTest.d.ts +31 -0
  86. package/dist/htest/ksTest.js +106 -0
  87. package/dist/htest/mantelhaenTest.d.ts +26 -0
  88. package/dist/htest/mantelhaenTest.js +196 -0
  89. package/dist/htest/mcnemarTest.d.ts +26 -0
  90. package/dist/htest/mcnemarTest.js +99 -0
  91. package/dist/htest/onewayTest.d.ts +26 -0
  92. package/dist/htest/onewayTest.js +97 -0
  93. package/dist/htest/pairwise.d.ts +64 -0
  94. package/dist/htest/pairwise.js +152 -0
  95. package/dist/htest/poissonTest.d.ts +35 -0
  96. package/dist/htest/poissonTest.js +170 -0
  97. package/dist/htest/propTest.d.ts +37 -0
  98. package/dist/htest/propTest.js +164 -0
  99. package/dist/htest/rankTests.d.ts +52 -0
  100. package/dist/htest/rankTests.js +234 -0
  101. package/dist/htest/types.d.ts +2 -0
  102. package/dist/htest/types.js +6 -0
  103. package/dist/htest/varTest.d.ts +48 -0
  104. package/dist/htest/varTest.js +118 -0
  105. package/dist/htest/wilcoxTest.js +136 -174
  106. package/dist/index.d.ts +6 -50
  107. package/dist/index.js +8 -50
  108. package/dist/integrate/quadpack.d.ts +29 -0
  109. package/dist/integrate/quadpack.js +525 -0
  110. package/dist/internal/seq.d.ts +5 -0
  111. package/dist/internal/seq.js +14 -0
  112. package/dist/internal/uniroot.js +10 -71
  113. package/dist/linalg/lu.d.ts +45 -0
  114. package/dist/linalg/lu.js +88 -0
  115. package/dist/linalg/ops.d.ts +90 -0
  116. package/dist/linalg/ops.js +320 -0
  117. package/dist/linalg.js +2 -0
  118. package/dist/models/lm.d.ts +98 -0
  119. package/dist/models/lm.js +187 -0
  120. package/dist/numerics/deriv.d.ts +37 -0
  121. package/dist/numerics/deriv.js +150 -0
  122. package/dist/numerics/integrate.d.ts +91 -0
  123. package/dist/numerics/integrate.js +264 -0
  124. package/dist/numerics/interp.d.ts +103 -0
  125. package/dist/numerics/interp.js +581 -0
  126. package/dist/numerics/ode.d.ts +23 -0
  127. package/dist/numerics/ode.js +59 -0
  128. package/dist/numerics.js +2 -0
  129. package/dist/optim/localmin.d.ts +16 -0
  130. package/dist/optim/localmin.js +97 -0
  131. package/dist/random/beta.js +1 -1
  132. package/dist/random/binomial.js +1 -1
  133. package/dist/random/continuous.js +1 -1
  134. package/dist/random/extra.d.ts +33 -0
  135. package/dist/random/extra.js +62 -0
  136. package/dist/random/gamma.js +1 -1
  137. package/dist/random/hyper.js +1 -1
  138. package/dist/random/mt.js +0 -1
  139. package/dist/random/poisson.js +0 -1
  140. package/dist/random/rank.js +1 -1
  141. package/dist/random/rng.js +1 -1
  142. package/dist/random/sample.js +1 -1
  143. package/dist/rstats.js +2 -2
  144. package/dist/special/besselIK.d.ts +38 -0
  145. package/dist/special/besselIK.js +315 -0
  146. package/dist/special/besselJY.d.ts +32 -0
  147. package/dist/special/besselJY.js +508 -0
  148. package/dist/special/besselSeries.d.ts +8 -0
  149. package/dist/special/besselSeries.js +24 -0
  150. package/dist/special/beta.js +22 -5
  151. package/dist/special/choose.d.ts +75 -0
  152. package/dist/special/choose.js +233 -0
  153. package/dist/special/erf.d.ts +28 -2
  154. package/dist/special/erf.js +37 -10
  155. package/dist/special/extra.d.ts +47 -0
  156. package/dist/special/extra.js +220 -0
  157. package/dist/special/igam.d.ts +4 -0
  158. package/dist/special/igam.js +57 -2
  159. package/dist/special/incbet.js +77 -22
  160. package/dist/special/lgammaDiff.d.ts +7 -0
  161. package/dist/special/lgammaDiff.js +16 -3
  162. package/dist/special/psigamma.d.ts +48 -0
  163. package/dist/special/psigamma.js +318 -0
  164. package/dist/special/rCompatible.d.ts +8 -0
  165. package/dist/special/rCompatible.js +1 -0
  166. package/dist/special.js +2 -0
  167. package/dist/tests.js +2 -0
  168. 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
+ }
@@ -1,6 +1,32 @@
1
- /** Error function erf(x). */
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
- /** Complementary error function erfc(x) = 1 - erf(x). */
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).
@@ -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
- /** Error function erf(x). */
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
- /** Complementary error function erfc(x) = 1 - erf(x). */
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
- y = 2 - y;
64
- if (y === 0)
65
- return a < 0 ? 2 : 0;
66
- return y;
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
+ }
@@ -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