@numero-uno-jpn/rstats 0.0.0-stage → 0.5.0

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 (159) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/LICENSE +21 -0
  3. package/README.ja.md +181 -0
  4. package/README.md +172 -2
  5. package/THIRD_PARTY_NOTICES.md +297 -0
  6. package/dist/distributions/beta.d.ts +62 -0
  7. package/dist/distributions/beta.js +246 -0
  8. package/dist/distributions/binomial.d.ts +41 -0
  9. package/dist/distributions/binomial.js +136 -0
  10. package/dist/distributions/cauchy.d.ts +34 -0
  11. package/dist/distributions/cauchy.js +139 -0
  12. package/dist/distributions/chisq.d.ts +42 -0
  13. package/dist/distributions/chisq.js +60 -0
  14. package/dist/distributions/exponential.d.ts +34 -0
  15. package/dist/distributions/exponential.js +93 -0
  16. package/dist/distributions/f.d.ts +47 -0
  17. package/dist/distributions/f.js +214 -0
  18. package/dist/distributions/gamma.d.ts +61 -0
  19. package/dist/distributions/gamma.js +188 -0
  20. package/dist/distributions/geometric.d.ts +38 -0
  21. package/dist/distributions/geometric.js +116 -0
  22. package/dist/distributions/hypergeometric.d.ts +38 -0
  23. package/dist/distributions/hypergeometric.js +234 -0
  24. package/dist/distributions/logistic.d.ts +36 -0
  25. package/dist/distributions/logistic.js +94 -0
  26. package/dist/distributions/lognormal.d.ts +39 -0
  27. package/dist/distributions/lognormal.js +158 -0
  28. package/dist/distributions/nbeta.d.ts +7 -0
  29. package/dist/distributions/nbeta.js +312 -0
  30. package/dist/distributions/nchisq.d.ts +3 -0
  31. package/dist/distributions/nchisq.js +217 -0
  32. package/dist/distributions/negbinomial.d.ts +54 -0
  33. package/dist/distributions/negbinomial.js +325 -0
  34. package/dist/distributions/normal.d.ts +66 -0
  35. package/dist/distributions/normal.js +184 -0
  36. package/dist/distributions/nt.d.ts +3 -0
  37. package/dist/distributions/nt.js +491 -0
  38. package/dist/distributions/poisson.d.ts +41 -0
  39. package/dist/distributions/poisson.js +104 -0
  40. package/dist/distributions/signrank.d.ts +38 -0
  41. package/dist/distributions/signrank.js +216 -0
  42. package/dist/distributions/t.d.ts +49 -0
  43. package/dist/distributions/t.js +235 -0
  44. package/dist/distributions/tukey.d.ts +43 -0
  45. package/dist/distributions/tukey.js +985 -0
  46. package/dist/distributions/uniform.d.ts +36 -0
  47. package/dist/distributions/uniform.js +90 -0
  48. package/dist/distributions/weibull.d.ts +37 -0
  49. package/dist/distributions/weibull.js +125 -0
  50. package/dist/distributions/wilcox.d.ts +47 -0
  51. package/dist/distributions/wilcox.js +270 -0
  52. package/dist/htest/chisqTest.d.ts +55 -0
  53. package/dist/htest/chisqTest.js +160 -0
  54. package/dist/htest/cor.d.ts +81 -0
  55. package/dist/htest/cor.js +436 -0
  56. package/dist/htest/fisherTest.d.ts +48 -0
  57. package/dist/htest/fisherTest.js +468 -0
  58. package/dist/htest/pAdjust.d.ts +34 -0
  59. package/dist/htest/pAdjust.js +147 -0
  60. package/dist/htest/tTest.d.ts +57 -0
  61. package/dist/htest/tTest.js +153 -0
  62. package/dist/htest/types.d.ts +49 -0
  63. package/dist/htest/types.js +20 -0
  64. package/dist/htest/wilcoxTest.d.ts +61 -0
  65. package/dist/htest/wilcoxTest.js +471 -0
  66. package/dist/index.d.ts +50 -0
  67. package/dist/index.js +50 -0
  68. package/dist/internal/ddexp.d.ts +19 -0
  69. package/dist/internal/ddexp.js +94 -0
  70. package/dist/internal/discrete.d.ts +14 -0
  71. package/dist/internal/discrete.js +85 -0
  72. package/dist/internal/dpq.d.ts +43 -0
  73. package/dist/internal/dpq.js +54 -0
  74. package/dist/internal/integer.d.ts +4 -0
  75. package/dist/internal/integer.js +17 -0
  76. package/dist/internal/invert.d.ts +36 -0
  77. package/dist/internal/invert.js +124 -0
  78. package/dist/internal/noncentral.d.ts +12 -0
  79. package/dist/internal/noncentral.js +2 -0
  80. package/dist/internal/poissonMixture.d.ts +31 -0
  81. package/dist/internal/poissonMixture.js +401 -0
  82. package/dist/internal/summary.d.ts +21 -0
  83. package/dist/internal/summary.js +125 -0
  84. package/dist/internal/uniroot.d.ts +11 -0
  85. package/dist/internal/uniroot.js +75 -0
  86. package/dist/internal/vectorize.d.ts +12 -0
  87. package/dist/internal/vectorize.js +7 -0
  88. package/dist/linalg/basic.d.ts +95 -0
  89. package/dist/linalg/basic.js +152 -0
  90. package/dist/linalg/chol.d.ts +83 -0
  91. package/dist/linalg/chol.js +200 -0
  92. package/dist/linalg/eigen.d.ts +52 -0
  93. package/dist/linalg/eigen.js +753 -0
  94. package/dist/linalg/kappa.d.ts +51 -0
  95. package/dist/linalg/kappa.js +199 -0
  96. package/dist/linalg/matrix.d.ts +29 -0
  97. package/dist/linalg/matrix.js +65 -0
  98. package/dist/linalg/norm.d.ts +32 -0
  99. package/dist/linalg/norm.js +92 -0
  100. package/dist/linalg/qr.d.ts +264 -0
  101. package/dist/linalg/qr.js +494 -0
  102. package/dist/linalg/solve.d.ts +123 -0
  103. package/dist/linalg/solve.js +380 -0
  104. package/dist/linalg/svd.d.ts +38 -0
  105. package/dist/linalg/svd.js +275 -0
  106. package/dist/random/as241.d.ts +2 -0
  107. package/dist/random/as241.js +58 -0
  108. package/dist/random/beta.d.ts +14 -0
  109. package/dist/random/beta.js +117 -0
  110. package/dist/random/binomial.d.ts +25 -0
  111. package/dist/random/binomial.js +224 -0
  112. package/dist/random/common.d.ts +4 -0
  113. package/dist/random/common.js +35 -0
  114. package/dist/random/continuous.d.ts +91 -0
  115. package/dist/random/continuous.js +138 -0
  116. package/dist/random/gamma.d.ts +52 -0
  117. package/dist/random/gamma.js +217 -0
  118. package/dist/random/hyper.d.ts +12 -0
  119. package/dist/random/hyper.js +240 -0
  120. package/dist/random/mt.d.ts +16 -0
  121. package/dist/random/mt.js +97 -0
  122. package/dist/random/poisson.d.ts +33 -0
  123. package/dist/random/poisson.js +204 -0
  124. package/dist/random/rank.d.ts +19 -0
  125. package/dist/random/rank.js +62 -0
  126. package/dist/random/rng.d.ts +46 -0
  127. package/dist/random/rng.js +148 -0
  128. package/dist/random/sample.d.ts +45 -0
  129. package/dist/random/sample.js +255 -0
  130. package/dist/rstats.js +2 -0
  131. package/dist/special/beta.d.ts +30 -0
  132. package/dist/special/beta.js +152 -0
  133. package/dist/special/constants.d.ts +16 -0
  134. package/dist/special/constants.js +19 -0
  135. package/dist/special/dd.d.ts +29 -0
  136. package/dist/special/dd.js +693 -0
  137. package/dist/special/erf.d.ts +9 -0
  138. package/dist/special/erf.js +85 -0
  139. package/dist/special/gamma.d.ts +32 -0
  140. package/dist/special/gamma.js +270 -0
  141. package/dist/special/igam.d.ts +11 -0
  142. package/dist/special/igam.js +291 -0
  143. package/dist/special/igamAsympCoeff.d.ts +4 -0
  144. package/dist/special/igamAsympCoeff.js +233 -0
  145. package/dist/special/igami.d.ts +2 -0
  146. package/dist/special/igami.js +135 -0
  147. package/dist/special/incbet.d.ts +25 -0
  148. package/dist/special/incbet.js +326 -0
  149. package/dist/special/lgammaDiff.d.ts +2 -0
  150. package/dist/special/lgammaDiff.js +34 -0
  151. package/dist/special/loader.d.ts +43 -0
  152. package/dist/special/loader.js +456 -0
  153. package/dist/special/log1pmx.d.ts +2 -0
  154. package/dist/special/log1pmx.js +20 -0
  155. package/dist/special/ndtri.d.ts +4 -0
  156. package/dist/special/ndtri.js +82 -0
  157. package/dist/special/polevl.d.ts +4 -0
  158. package/dist/special/polevl.js +17 -0
  159. package/package.json +68 -4
@@ -0,0 +1,325 @@
1
+ // Negative binomial distribution dnbinom / pnbinom / qnbinom (size and prob, or size and mu).
2
+ //
3
+ // Probabilities use the relation to the binomial probability (Loader 2000)
4
+ // P[X = x] = Γ(x + n) / (Γ(n) x!) p^n q^x = n / (n + x) · b(n; n + x, p)
5
+ // where b is dbinomRawDD from src/special/loader.ts (n need not be an integer).
6
+ // The distribution function is P[X <= x] = I_p(n, x + 1) (regularized incomplete beta function).
7
+ // When mu is given, p = n / (n + mu) and q = mu / (n + mu) are formed in double-double
8
+ // (for n ≫ mu, rounding 1 - p gives q a large relative error). size = Inf is the Poisson distribution.
9
+ // Quantiles use an integer search from a Cornish-Fisher starting value.
10
+ import { vectorize } from "../internal/vectorize.js";
11
+ import { d0, dt0, dt1, quantileBoundaries } from "../internal/dpq.js";
12
+ import { discreteQuantile } from "../internal/discrete.js";
13
+ import { nonInt } from "../internal/integer.js";
14
+ import { betaIncDD } from "../special/incbet.js";
15
+ import { dbinomRawDD, dpoisRaw } from "../special/loader.js";
16
+ import { ddAdd, ddDiv, ddMul, twoSum } from "../special/dd.js";
17
+ import { qnorm } from "./normal.js";
18
+ import { qgamma1 } from "./gamma.js";
19
+ import { ppois1, qpois1 } from "./poisson.js";
20
+ import { probPair } from "./geometric.js";
21
+ function fromMu(size, mu) {
22
+ const s = twoSum(size, mu);
23
+ return { size, pd: ddDiv([size, 0], s), qd: ddDiv([mu, 0], s) };
24
+ }
25
+ function fromProb(size, prob) {
26
+ return { size, ...probPair(prob) };
27
+ }
28
+ /** P[X = x] (x a nonnegative integer, 0 < size < ∞, 0 < p < 1) */
29
+ function dnbinomRaw(x, par, log) {
30
+ const { size, pd, qd } = par;
31
+ const b = dbinomRawDD(size, x, pd, qd, log);
32
+ if (x === 0)
33
+ return b;
34
+ const r = size / (size + x);
35
+ return log ? b + Math.log(r) : b * r;
36
+ }
37
+ /** P[X <= x] (lower) / P[X > x] (!lower) (x a nonnegative integer) */
38
+ function pnbinomRaw(x, par, lower, log) {
39
+ const up = upperSeries(x, par);
40
+ if (up !== undefined) {
41
+ // The upper tail (the smaller side) was computed directly by the series
42
+ if (!lower)
43
+ return log ? Math.log(up) : up;
44
+ return log ? Math.log1p(-up) : 1 - up;
45
+ }
46
+ return betaIncDD(par.size, x + 1, par.pd, par.qd, lower, log);
47
+ }
48
+ /**
49
+ * For size <= 1 with q close to 1, compute the upper probability P[X > x] = I_q(x + 1, size) directly by the
50
+ * positive-term series
51
+ * I_z(A, B) = z^A (1 - z)^B / (A B(A, B)) [1 + Σ_{k>=1} Π_{j<k} (A + B + j) / (A + 1 + j) z^k]
52
+ * (z = q, A = x + 1, B = size). In this region the incomplete beta continued fraction is off by about 3e-14
53
+ * relative near the mean. All terms are positive, so there is no cancellation. The number of terms is about
54
+ * -log(1e-17) / -log z. Returns undefined when not usable (too many terms, or the value exceeds 1/2)
55
+ */
56
+ function upperSeries(x, par) {
57
+ const { size, pd, qd } = par;
58
+ const z = qd[0] + qd[1];
59
+ const p = pd[0] + pd[1];
60
+ if (!(size <= 1) || !(z >= 0.95) || p < 1e-4)
61
+ return undefined;
62
+ const A = x + 1;
63
+ const B = size;
64
+ // Prefactor z^A (1 - z)^B / (A B(A, B)) = b(A; A + B, z) B / (A + B)
65
+ const pre = (dbinomRawDD(A, B, qd, pd, false) * B) / (A + B);
66
+ if (!(pre > 1e-290))
67
+ return undefined;
68
+ // Rounding of the term ratios accumulates with the number of terms (1e-12 over tens of thousands), so keep terms and sum in double-double
69
+ const ab = twoSum(A, B);
70
+ let term = [1, 0];
71
+ let sum = [1, 0];
72
+ for (let k = 0; k < 1e6; k++) {
73
+ const ratio = ddDiv(ddAdd(ab, [k, 0]), [A + 1 + k, 0]);
74
+ term = ddMul(ddMul(term, ratio), qd);
75
+ sum = ddAdd(sum, term);
76
+ if (term[0] < 1e-18 * sum[0]) {
77
+ const v = pre * (sum[0] + sum[1]);
78
+ return v <= 0.5 ? v : undefined;
79
+ }
80
+ }
81
+ return undefined;
82
+ }
83
+ function quantileRaw(p, par, lowerTail, logP) {
84
+ // Starting value from the Cornish-Fisher expansion
85
+ const prob = par.pd[0] + par.pd[1];
86
+ const Q = 1 / prob;
87
+ const P = (par.qd[0] + par.qd[1]) * Q;
88
+ const mu = par.size * P;
89
+ const sigma = Math.sqrt(par.size * P * Q);
90
+ const gamma = (Q + P) / sigma;
91
+ let y0;
92
+ if (prob < 0.05) {
93
+ // For small p, X (-log q) is nearly gamma (shape = size). Even for highly skewed distributions (small size)
94
+ // this is a much closer starting value than Cornish-Fisher, reducing distribution-function evaluations
95
+ // -log q = log(1 + p/q) (taking log of a rounded q loses digits for small p)
96
+ y0 = qgamma1(p, par.size, 1, lowerTail, logP) / Math.log1p(prob / (par.qd[0] + par.qd[1]));
97
+ }
98
+ else {
99
+ const z = qnorm(p, { lowerTail, logP });
100
+ y0 = mu + sigma * (z + (gamma * (z * z - 1)) / 6);
101
+ }
102
+ let start = Number.isFinite(y0) ? Math.max(y0, 0) : Number.isFinite(mu) ? mu : 0;
103
+ if (start > 1e4)
104
+ start = refineContinuous(start, p, par, lowerTail, logP);
105
+ return discreteQuantile({
106
+ p,
107
+ lowerTail,
108
+ logP,
109
+ cdf: (y, l, lg) => pnbinomRaw(y, par, l, lg),
110
+ y0: start,
111
+ lo: 0,
112
+ hi: Infinity,
113
+ });
114
+ }
115
+ /**
116
+ * Before the integer search, refine the starting value by Newton's method treating y as real.
117
+ * The discrete search widens from step 1 by doubling, so a starting value off by 1e15 units would need more than
118
+ * 100 distribution-function evaluations. Solves for the log probability of the smaller tail. The final integer
119
+ * search determines the answer, so this only needs to be close
120
+ */
121
+ function refineContinuous(y0, p, par, lowerTail, logP) {
122
+ // Use the same slack-adjusted target as the integer search (src/internal/discrete.ts)
123
+ // (when p is close to 1, the slack changes the opposite-tail probability considerably)
124
+ const E = Number.EPSILON;
125
+ let t = p;
126
+ if (logP)
127
+ t = lowerTail ? p * (1 + 2 * E) : p * (1 - 2 * E);
128
+ else if (lowerTail)
129
+ t = p * (1 - 8 * E);
130
+ else if (1 - p > 32 * E)
131
+ t = p * (1 + 8 * E);
132
+ const lp = logP ? t : Math.log(t);
133
+ // Switch to the smaller tail
134
+ const useLower = lp <= -Math.LN2 ? lowerTail : !lowerTail;
135
+ const target = lp <= -Math.LN2 ? lp : logP ? Math.log(-Math.expm1(t)) : Math.log1p(-t);
136
+ // First step: Newton's method with the slope approximated by the probability mass function; then the secant method
137
+ let y = y0;
138
+ let r = pnbinomRaw(y, par, useLower, true) - target;
139
+ let yPrev = NaN;
140
+ let rPrev = NaN;
141
+ for (let i = 0; i < 8 && r !== 0; i++) {
142
+ let next;
143
+ if (Number.isNaN(yPrev) || r === rPrev) {
144
+ const slope = (useLower ? 1 : -1) * Math.exp(dnbinomRaw(y, par, true) - (r + target));
145
+ next = y - r / slope;
146
+ }
147
+ else {
148
+ next = y - (r * (y - yPrev)) / (r - rPrev);
149
+ }
150
+ if (!Number.isFinite(next) || next <= 0)
151
+ break;
152
+ if (Math.abs(next - y) < 0.5)
153
+ return next;
154
+ yPrev = y;
155
+ rPrev = r;
156
+ y = next;
157
+ r = pnbinomRaw(y, par, useLower, true) - target;
158
+ }
159
+ return y;
160
+ }
161
+ // ---- Parameterized by prob ----
162
+ export function dnbinom1(x, size, prob, log) {
163
+ if (Number.isNaN(x) || Number.isNaN(size) || Number.isNaN(prob))
164
+ return NaN;
165
+ if (size < 0 || !(prob > 0) || prob > 1)
166
+ return NaN;
167
+ if (nonInt(x) || x < 0 || !Number.isFinite(x))
168
+ return d0(log);
169
+ const n = Math.round(x);
170
+ if (size === 0 || prob === 1)
171
+ return n === 0 ? (log ? 0 : 1) : d0(log);
172
+ if (size === Infinity)
173
+ return d0(log);
174
+ return dnbinomRaw(n, fromProb(size, prob), log);
175
+ }
176
+ export function pnbinom1(q, size, prob, lowerTail, logP) {
177
+ if (Number.isNaN(q) || Number.isNaN(size) || Number.isNaN(prob))
178
+ return NaN;
179
+ if (size < 0 || !(prob > 0) || prob > 1 || size === Infinity)
180
+ return NaN;
181
+ if (q < 0)
182
+ return dt0(lowerTail, logP);
183
+ if (size === 0 || prob === 1 || !Number.isFinite(q))
184
+ return dt1(lowerTail, logP);
185
+ return pnbinomRaw(Math.floor(q + 1e-7), fromProb(size, prob), lowerTail, logP);
186
+ }
187
+ export function qnbinom1(p, size, prob, lowerTail, logP) {
188
+ if (Number.isNaN(p) || Number.isNaN(size) || Number.isNaN(prob))
189
+ return NaN;
190
+ // Same order as R: parameter range → point mass (prob = 1 or size = 0 gives 0 regardless of p) → prob = 0 → range of p
191
+ if (size < 0 || prob < 0 || prob > 1)
192
+ return NaN;
193
+ if (prob === 1 || size === 0)
194
+ return 0;
195
+ if (prob === 0)
196
+ return NaN;
197
+ const b = quantileBoundaries(p, 0, Infinity, lowerTail, logP);
198
+ if (b !== undefined)
199
+ return b;
200
+ // size = Inf (prob < 1) has its mass at X = ∞ (R gives Inf or NaN depending on p)
201
+ if (size === Infinity)
202
+ return Infinity;
203
+ return quantileRaw(p, fromProb(size, prob), lowerTail, logP);
204
+ }
205
+ // ---- Parameterized by mu ----
206
+ export function dnbinomMu1(x, size, mu, log) {
207
+ if (Number.isNaN(x) || Number.isNaN(size) || Number.isNaN(mu))
208
+ return NaN;
209
+ if (size < 0 || mu < 0)
210
+ return NaN;
211
+ if (nonInt(x) || x < 0 || !Number.isFinite(x))
212
+ return d0(log);
213
+ const n = Math.round(x);
214
+ if (size === 0) {
215
+ if (n === 0)
216
+ return log ? 0 : 1;
217
+ return mu === 0 ? NaN : d0(log);
218
+ }
219
+ if (mu === Infinity)
220
+ return d0(log);
221
+ if (size === Infinity)
222
+ return dpoisRaw(n, mu, log);
223
+ if (mu === 0)
224
+ return n === 0 ? (log ? 0 : 1) : d0(log);
225
+ return dnbinomRaw(n, fromMu(size, mu), log);
226
+ }
227
+ export function pnbinomMu1(q, size, mu, lowerTail, logP) {
228
+ if (Number.isNaN(q) || Number.isNaN(size) || Number.isNaN(mu))
229
+ return NaN;
230
+ if (size < 0 || mu < 0 || mu === Infinity)
231
+ return NaN;
232
+ if (q < 0)
233
+ return dt0(lowerTail, logP);
234
+ if (size === 0 || mu === 0 || !Number.isFinite(q))
235
+ return dt1(lowerTail, logP);
236
+ if (size === Infinity)
237
+ return ppois1(q, mu, lowerTail, logP);
238
+ return pnbinomRaw(Math.floor(q + 1e-7), fromMu(size, mu), lowerTail, logP);
239
+ }
240
+ export function qnbinomMu1(p, size, mu, lowerTail, logP) {
241
+ if (Number.isNaN(p) || Number.isNaN(size) || Number.isNaN(mu))
242
+ return NaN;
243
+ // As in R, size = Inf is the Poisson distribution, and size = 0 or mu = 0 gives 0 regardless of the other parameter or p
244
+ if (size === Infinity)
245
+ return qpois1(p, mu, lowerTail, logP);
246
+ if (size === 0 || mu === 0)
247
+ return 0;
248
+ if (size < 0 || mu < 0)
249
+ return NaN;
250
+ // mu = Inf answers only at the ends (p = 0, 1) and is NaN otherwise (as in R)
251
+ if (mu === Infinity)
252
+ return quantileBoundaries(p, 0, Infinity, lowerTail, logP) ?? NaN;
253
+ const b = quantileBoundaries(p, 0, Infinity, lowerTail, logP);
254
+ if (b !== undefined)
255
+ return b;
256
+ return quantileRaw(p, fromMu(size, mu), lowerTail, logP);
257
+ }
258
+ /** Accept exactly one of prob and mu (as in R, giving both or neither is an error) */
259
+ function which(options) {
260
+ const { prob, mu } = options;
261
+ if (prob !== undefined && mu !== undefined)
262
+ throw new TypeError("specify exactly one of prob and mu");
263
+ if (prob !== undefined)
264
+ return { kind: "prob", v: prob };
265
+ if (mu !== undefined)
266
+ return { kind: "mu", v: mu };
267
+ throw new TypeError("specify exactly one of prob and mu");
268
+ }
269
+ /**
270
+ * Probability mass function of the negative binomial distribution (number of failures before the `size`-th success). Equivalent to R's `dnbinom(x, size, prob, mu, log = FALSE)`.
271
+ *
272
+ * The distribution is parameterized by `size` and exactly one of `prob` or `mu`. With `mu`, `size = Infinity` gives the Poisson distribution.
273
+ *
274
+ * @throws TypeError unless exactly one of `prob` and `mu` is given.
275
+ *
276
+ * @param x - Value(s) at which to evaluate: a number, `number[]` or `Float64Array`. The result has the same shape.
277
+ * @param size - Target number of successes (need not be an integer).
278
+ * @param options - `prob` or `mu` (see {@link NegBinomialParams}; exactly one is required) and `log` (see {@link DensityOptions}).
279
+ * @returns The probability (or log probability).
280
+ */
281
+ export function dnbinom(x, size, options) {
282
+ const log = options.log ?? false;
283
+ const w = which(options);
284
+ return w.kind === "prob"
285
+ ? vectorize(x, (v) => dnbinom1(v, size, w.v, log))
286
+ : vectorize(x, (v) => dnbinomMu1(v, size, w.v, log));
287
+ }
288
+ /**
289
+ * Distribution function of the negative binomial distribution (number of failures before the `size`-th success). Equivalent to R's `pnbinom(q, size, prob, mu, lower.tail = TRUE, log.p = FALSE)`.
290
+ *
291
+ * The distribution is parameterized by `size` and exactly one of `prob` or `mu`. With `mu`, `size = Infinity` gives the Poisson distribution.
292
+ *
293
+ * @throws TypeError unless exactly one of `prob` and `mu` is given.
294
+ *
295
+ * @param q - Quantile(s): a number, `number[]` or `Float64Array`. The result has the same shape.
296
+ * @param size - Target number of successes (need not be an integer).
297
+ * @param options - `prob` or `mu` (see {@link NegBinomialParams}; exactly one is required), `lowerTail` and `logP` (see {@link TailOptions}).
298
+ * @returns The probability (or log probability).
299
+ */
300
+ export function pnbinom(q, size, options) {
301
+ const { lowerTail = true, logP = false } = options;
302
+ const w = which(options);
303
+ return w.kind === "prob"
304
+ ? vectorize(q, (v) => pnbinom1(v, size, w.v, lowerTail, logP))
305
+ : vectorize(q, (v) => pnbinomMu1(v, size, w.v, lowerTail, logP));
306
+ }
307
+ /**
308
+ * Quantile function of the negative binomial distribution (number of failures before the `size`-th success). Equivalent to R's `qnbinom(p, size, prob, mu, lower.tail = TRUE, log.p = FALSE)`.
309
+ *
310
+ * The distribution is parameterized by `size` and exactly one of `prob` or `mu`. With `mu`, `size = Infinity` gives the Poisson distribution.
311
+ *
312
+ * @throws TypeError unless exactly one of `prob` and `mu` is given.
313
+ *
314
+ * @param p - Probability (or log probability with `logP`): a number, `number[]` or `Float64Array`. The result has the same shape.
315
+ * @param size - Target number of successes (need not be an integer).
316
+ * @param options - `prob` or `mu` (see {@link NegBinomialParams}; exactly one is required), `lowerTail` and `logP` (see {@link TailOptions}).
317
+ * @returns The quantile(s).
318
+ */
319
+ export function qnbinom(p, size, options) {
320
+ const { lowerTail = true, logP = false } = options;
321
+ const w = which(options);
322
+ return w.kind === "prob"
323
+ ? vectorize(p, (v) => qnbinom1(v, size, w.v, lowerTail, logP))
324
+ : vectorize(p, (v) => qnbinomMu1(v, size, w.v, lowerTail, logP));
325
+ }
@@ -0,0 +1,66 @@
1
+ import { type Vectorizable, type VectorResult } from "../internal/vectorize.js";
2
+ /** Options for {@link pnorm} and {@link qnorm}. */
3
+ export interface NormalOptions {
4
+ /** Mean. Default `0`. */
5
+ mean?: number;
6
+ /** Standard deviation. Default `1`. */
7
+ sd?: number;
8
+ /** If `true`, probabilities are P[X <= x]; if `false`, P[X > x]. Default `true`. */
9
+ lowerTail?: boolean;
10
+ /** If `true`, probabilities are given (to `qnorm`) and returned (from `pnorm`) as log(p). Default `false`. */
11
+ logP?: boolean;
12
+ }
13
+ /**
14
+ * Density of the normal distribution. Equivalent to R's `dnorm(x, mean = 0, sd = 1, log = FALSE)`.
15
+ *
16
+ * @param x - Value(s) at which to evaluate: a number, `number[]` or `Float64Array`. The result has the same shape.
17
+ * @param options - Distribution parameters and output scale.
18
+ * @param options.mean - Mean. Default `0`.
19
+ * @param options.sd - Standard deviation. Default `1`.
20
+ * @param options.log - If `true`, return the log density. Default `false`.
21
+ * @returns The density (or log density).
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * dnorm(0); // 0.3989422804014327
26
+ * dnorm([-1, 0, 1]); // [0.24197072451914337, 0.3989422804014327, 0.24197072451914337]
27
+ * dnorm(1, { mean: 1, sd: 2, log: true }); // -1.612085713764618
28
+ * ```
29
+ */
30
+ export declare function dnorm<T extends Vectorizable>(x: T, options?: {
31
+ mean?: number;
32
+ sd?: number;
33
+ log?: boolean;
34
+ }): VectorResult<T>;
35
+ /**
36
+ * Distribution function of the normal distribution.
37
+ * Equivalent to R's `pnorm(q, mean = 0, sd = 1, lower.tail = TRUE, log.p = FALSE)`.
38
+ *
39
+ * @param q - Quantile(s): a number, `number[]` or `Float64Array`. The result has the same shape.
40
+ * @param options - `mean`, `sd`, `lowerTail` and `logP` (see {@link NormalOptions}).
41
+ * @returns The probability (or log probability).
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * pnorm(1.96); // 0.9750021048517795
46
+ * pnorm(1.96, { lowerTail: false }); // 0.024997895148220435
47
+ * pnorm(new Float64Array([-1, 0, 1])); // Float64Array [0.15865525393145707, 0.5, 0.8413447460685429]
48
+ * pnorm(-40, { logP: true }); // -804.6084420137538
49
+ * ```
50
+ */
51
+ export declare function pnorm<T extends Vectorizable>(q: T, options?: NormalOptions): VectorResult<T>;
52
+ /**
53
+ * Quantile function of the normal distribution.
54
+ * Equivalent to R's `qnorm(p, mean = 0, sd = 1, lower.tail = TRUE, log.p = FALSE)`.
55
+ *
56
+ * @param p - Probability (or log probability with `logP`): a number, `number[]` or `Float64Array`. The result has the same shape.
57
+ * @param options - `mean`, `sd`, `lowerTail` and `logP` (see {@link NormalOptions}).
58
+ * @returns The quantile(s).
59
+ *
60
+ * @example
61
+ * ```ts
62
+ * qnorm(0.975); // 1.959963984540054
63
+ * qnorm(0.05, { mean: 100, sd: 15 }); // 75.32719559572791
64
+ * ```
65
+ */
66
+ export declare function qnorm<T extends Vectorizable>(p: T, options?: NormalOptions): VectorResult<T>;
@@ -0,0 +1,184 @@
1
+ // Normal distribution function pnorm and quantile function qnorm.
2
+ //
3
+ // The building blocks are ports of xsf (Cephes) erf / erfc / ndtri / ndtri_exp
4
+ // (src/special/). Argument handling (NaN, ±Inf, sd <= 0, lowerTail, logP) is matched to
5
+ // R's output (test/fixtures/). R's source code was not consulted.
6
+ import { erf, erfcxNonNegative } from "../special/erf.js";
7
+ import { ndtri, ndtriExp } from "../special/ndtri.js";
8
+ import { vectorize } from "../internal/vectorize.js";
9
+ const SQRT1_2 = Math.SQRT1_2;
10
+ /**
11
+ * exp(-a^2 / 2) (a >= 0) multiplied by scale.
12
+ *
13
+ * Computing a^2 directly lets its rounding error be amplified by the exponential, reaching a relative error of
14
+ * 1e-13 around a = 38. Split a = hi + lo (hi on a 1/16 grid so hi^2 is exact) and compute
15
+ * exp(-hi^2/2) * exp(-lo (a + hi) / 2).
16
+ * Where the result is subnormal, rounding happens only in the final multiplication.
17
+ */
18
+ function scaledExpNegHalfSq(a, scale) {
19
+ const hi = Math.floor(a * 16) / 16;
20
+ const lo = a - hi;
21
+ const halfHiSq = (hi * hi) / 2;
22
+ const m = scale * Math.exp((-lo * (a + hi)) / 2);
23
+ if (halfHiSq <= 700)
24
+ return m * Math.exp(-halfHiSq);
25
+ return m * Math.exp(700 - halfHiSq) * Math.exp(-700);
26
+ }
27
+ /** Lower probability P[Z <= t] of the standard normal (t <= 0) */
28
+ function lowerTailProb(t) {
29
+ const a = -t;
30
+ if (a < Math.SQRT2)
31
+ return 0.5 - 0.5 * erf(a * SQRT1_2);
32
+ return scaledExpNegHalfSq(a, 0.5 * erfcxNonNegative(a * SQRT1_2));
33
+ }
34
+ /** P[Z <= t] of the standard normal (log if logP). t is not NaN */
35
+ function pnormStd(t, logP) {
36
+ if (!logP)
37
+ return t <= 0 ? lowerTailProb(t) : 1 - lowerTailProb(-t);
38
+ if (t > -1)
39
+ return Math.log1p(-lowerTailProb(-t));
40
+ const a = -t;
41
+ return Math.log(0.5 * erfcxNonNegative(a * SQRT1_2)) - (a * a) / 2;
42
+ }
43
+ function pnorm1(q, mean, sd, lowerTail, logP) {
44
+ if (Number.isNaN(q) || Number.isNaN(mean) || Number.isNaN(sd))
45
+ return NaN;
46
+ if (!Number.isFinite(q) && mean === q)
47
+ return NaN; // Inf - Inf
48
+ const p0 = lowerTail ? (logP ? -Infinity : 0) : logP ? 0 : 1;
49
+ const p1 = lowerTail ? (logP ? 0 : 1) : logP ? -Infinity : 0;
50
+ if (sd < 0)
51
+ return NaN;
52
+ if (sd === 0)
53
+ return q < mean ? p0 : p1;
54
+ const x = (q - mean) / sd;
55
+ if (!Number.isFinite(x))
56
+ return q < mean ? p0 : p1;
57
+ return pnormStd(lowerTail ? x : -x, logP);
58
+ }
59
+ function qnorm1(p, mean, sd, lowerTail, logP) {
60
+ if (Number.isNaN(p) || Number.isNaN(mean) || Number.isNaN(sd))
61
+ return NaN;
62
+ // Endpoints corresponding to probabilities 0 and 1
63
+ const left = lowerTail ? -Infinity : Infinity;
64
+ if (logP) {
65
+ if (p > 0)
66
+ return NaN;
67
+ if (p === 0)
68
+ return -left;
69
+ if (p === -Infinity)
70
+ return left;
71
+ }
72
+ else {
73
+ if (p < 0 || p > 1)
74
+ return NaN;
75
+ if (p === 0)
76
+ return left;
77
+ if (p === 1)
78
+ return -left;
79
+ }
80
+ if (sd < 0)
81
+ return NaN;
82
+ if (sd === 0)
83
+ return mean;
84
+ const z = logP ? ndtriLogRefined(p) : ndtri(p);
85
+ return mean + sd * (lowerTail ? z : -z);
86
+ }
87
+ const SQRT_2_OVER_PI = 0.797884560802865355879892119869;
88
+ /**
89
+ * ndtriExp with one Newton correction applied only outside the design range of the Cephes approximation.
90
+ *
91
+ * The tail approximation of Cephes' ndtri assumes z = sqrt(-2 log p) up to 64 (log p >= -2048). Beyond that
92
+ * the relative error grows to about 5e-13 (measured at log p = -1e5). So log Φ(x) = y is re-solved using the
93
+ * logP of pnormStd, which is accurate in the tails as well. The derivative φ(x)/Φ(x) is √(2/π) / erfcx(a/√2),
94
+ * since Φ(x) = erfcx(a/√2) φ(x) √(π/2) for x = -a < 0. This avoids dividing exp(-a²/2) by itself.
95
+ */
96
+ function ndtriLogRefined(y) {
97
+ const x = ndtriExp(y);
98
+ if (!(y < -2048) || !Number.isFinite(x))
99
+ return x;
100
+ const slope = SQRT_2_OVER_PI / erfcxNonNegative(-x * SQRT1_2);
101
+ const step = (pnormStd(x, true) - y) / slope;
102
+ // For |x| > 1.3e154, a² in pnormStd overflows; return without correction there
103
+ return Number.isFinite(step) ? x - step : x;
104
+ }
105
+ const LN_SQRT_2PI = 0.918938533204672741780329736406;
106
+ const INV_SQRT_2PI = 0.398942280401432677939946059934;
107
+ function dnorm1(x, mean, sd, log) {
108
+ if (Number.isNaN(x) || Number.isNaN(mean) || Number.isNaN(sd))
109
+ return NaN;
110
+ if (sd < 0)
111
+ return NaN;
112
+ if (!Number.isFinite(sd))
113
+ return log ? -Infinity : 0;
114
+ if (!Number.isFinite(x) && mean === x)
115
+ return NaN; // Inf - Inf
116
+ if (sd === 0)
117
+ return x === mean ? Infinity : log ? -Infinity : 0;
118
+ const z = (x - mean) / sd;
119
+ if (!Number.isFinite(z))
120
+ return log ? -Infinity : 0;
121
+ if (log)
122
+ return -(LN_SQRT_2PI + 0.5 * z * z + Math.log(sd));
123
+ // Rounding of z^2 is amplified by the exponential in exp(-z^2/2), so split it as in pnorm
124
+ return scaledExpNegHalfSq(Math.abs(z), INV_SQRT_2PI / sd);
125
+ }
126
+ /**
127
+ * Density of the normal distribution. Equivalent to R's `dnorm(x, mean = 0, sd = 1, log = FALSE)`.
128
+ *
129
+ * @param x - Value(s) at which to evaluate: a number, `number[]` or `Float64Array`. The result has the same shape.
130
+ * @param options - Distribution parameters and output scale.
131
+ * @param options.mean - Mean. Default `0`.
132
+ * @param options.sd - Standard deviation. Default `1`.
133
+ * @param options.log - If `true`, return the log density. Default `false`.
134
+ * @returns The density (or log density).
135
+ *
136
+ * @example
137
+ * ```ts
138
+ * dnorm(0); // 0.3989422804014327
139
+ * dnorm([-1, 0, 1]); // [0.24197072451914337, 0.3989422804014327, 0.24197072451914337]
140
+ * dnorm(1, { mean: 1, sd: 2, log: true }); // -1.612085713764618
141
+ * ```
142
+ */
143
+ export function dnorm(x, options = {}) {
144
+ const { mean = 0, sd = 1, log = false } = options;
145
+ return vectorize(x, (v) => dnorm1(v, mean, sd, log));
146
+ }
147
+ /**
148
+ * Distribution function of the normal distribution.
149
+ * Equivalent to R's `pnorm(q, mean = 0, sd = 1, lower.tail = TRUE, log.p = FALSE)`.
150
+ *
151
+ * @param q - Quantile(s): a number, `number[]` or `Float64Array`. The result has the same shape.
152
+ * @param options - `mean`, `sd`, `lowerTail` and `logP` (see {@link NormalOptions}).
153
+ * @returns The probability (or log probability).
154
+ *
155
+ * @example
156
+ * ```ts
157
+ * pnorm(1.96); // 0.9750021048517795
158
+ * pnorm(1.96, { lowerTail: false }); // 0.024997895148220435
159
+ * pnorm(new Float64Array([-1, 0, 1])); // Float64Array [0.15865525393145707, 0.5, 0.8413447460685429]
160
+ * pnorm(-40, { logP: true }); // -804.6084420137538
161
+ * ```
162
+ */
163
+ export function pnorm(q, options = {}) {
164
+ const { mean = 0, sd = 1, lowerTail = true, logP = false } = options;
165
+ return vectorize(q, (v) => pnorm1(v, mean, sd, lowerTail, logP));
166
+ }
167
+ /**
168
+ * Quantile function of the normal distribution.
169
+ * Equivalent to R's `qnorm(p, mean = 0, sd = 1, lower.tail = TRUE, log.p = FALSE)`.
170
+ *
171
+ * @param p - Probability (or log probability with `logP`): a number, `number[]` or `Float64Array`. The result has the same shape.
172
+ * @param options - `mean`, `sd`, `lowerTail` and `logP` (see {@link NormalOptions}).
173
+ * @returns The quantile(s).
174
+ *
175
+ * @example
176
+ * ```ts
177
+ * qnorm(0.975); // 1.959963984540054
178
+ * qnorm(0.05, { mean: 100, sd: 15 }); // 75.32719559572791
179
+ * ```
180
+ */
181
+ export function qnorm(p, options = {}) {
182
+ const { mean = 0, sd = 1, lowerTail = true, logP = false } = options;
183
+ return vectorize(p, (v) => qnorm1(v, mean, sd, lowerTail, logP));
184
+ }
@@ -0,0 +1,3 @@
1
+ export declare function dnt1(x: number, df: number, ncp: number, log: boolean): number;
2
+ export declare function pnt1(q: number, df: number, ncp: number, lowerTail: boolean, logP: boolean): number;
3
+ export declare function qnt1(p: number, df: number, ncp: number, lowerTail: boolean, logP: boolean): number;