@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,214 @@
1
+ // F distribution df / pf / qf. Passing ncp gives the noncentral F (src/distributions/nbeta.ts).
2
+ //
3
+ // The density uses the relation to the binomial probability. With p = m x/(n + m x) and q = n/(n + m x),
4
+ // m >= 2: f(x) = m q / 2 * dbinom((m-2)/2; (m+n-2)/2, p)
5
+ // m < 2: f(x) = m/(n + m x) * m n / (2 (m + n) p) * dbinom(m/2; (m+n)/2, p)
6
+ // (both derived by rewriting the F density in terms of Gamma functions).
7
+ // The distribution function is P[F <= x] = I_p(m/2, n/2); quantiles use Newton's method in u = log x.
8
+ import { vectorize } from "../internal/vectorize.js";
9
+ import { d0, dt0, dt1, quantileBoundaries, smallerTail } from "../internal/dpq.js";
10
+ import { invertContinuous } from "../internal/invert.js";
11
+ import { lbeta } from "../special/beta.js";
12
+ import { betaInc, betaIncBoth, betaIncBothDD, betaIncDD } from "../special/incbet.js";
13
+ import { dbinomRawDD } from "../special/loader.js";
14
+ import { ddAdd, ddDiv, twoProd } from "../special/dd.js";
15
+ import { dgamma1, pgamma1, qgamma1 } from "./gamma.js";
16
+ import { dnf1, pnf1, qnf1 } from "./nbeta.js";
17
+ import { betaQuantileGuess } from "./beta.js";
18
+ function fDensity(x, m, n, log) {
19
+ // p = m x/(n + m x) and q = n/(n + m x) are formed in double-double. Rounding in p and q would be
20
+ // amplified m/2 and n/2 times respectively (5e-13 relative at df1 = 1e6)
21
+ const mx = twoProd(m, x);
22
+ let pd;
23
+ let qd;
24
+ if (Number.isFinite(mx[0]) && Number.isFinite(mx[1]) && Math.abs(mx[0]) < 1e290) {
25
+ const d = ddAdd(mx, [n, 0]);
26
+ pd = ddDiv(mx, d);
27
+ qd = ddDiv([n, 0], d);
28
+ }
29
+ else {
30
+ // m x is huge (the double-double product overflows): p ≈ 1, q ≈ n/(m x) in double precision
31
+ qd = [n / m / x, 0];
32
+ pd = [1, -qd[0]];
33
+ }
34
+ const p = pd[0];
35
+ const q = qd[0];
36
+ if (m >= 2) {
37
+ const c = (m * q) / 2;
38
+ const v = dbinomRawDD((m - 2) / 2, n / 2, pd, qd, log);
39
+ return log ? Math.log(c) + v : c * v;
40
+ }
41
+ const c = ((m / (n + m * x)) * m * n) / (2 * (m + n) * p);
42
+ const v = dbinomRawDD(m / 2, n / 2, pd, qd, log);
43
+ return log ? Math.log(c) + v : c * v;
44
+ }
45
+ export function dfDensity1(x, m, n, log) {
46
+ if (Number.isNaN(x) || Number.isNaN(m) || Number.isNaN(n))
47
+ return NaN;
48
+ if (m <= 0 || n <= 0)
49
+ return NaN;
50
+ if (x < 0)
51
+ return d0(log);
52
+ if (x === 0) {
53
+ if (m > 2)
54
+ return d0(log);
55
+ if (m === 2)
56
+ return log ? 0 : 1;
57
+ return Infinity;
58
+ }
59
+ if (!Number.isFinite(m) && !Number.isFinite(n))
60
+ return x === 1 ? Infinity : d0(log);
61
+ if (!Number.isFinite(n))
62
+ return dgamma1(x, m / 2, 2 / m, log); // χ²_m / m
63
+ if (!Number.isFinite(m)) {
64
+ // n / χ²_n: 1/x follows a gamma distribution with shape n/2, scale 2/n
65
+ const dens = dgamma1(1 / x, n / 2, 2 / n, log);
66
+ return log ? dens - 2 * Math.log(x) : dens / (x * x);
67
+ }
68
+ if (!Number.isFinite(x))
69
+ return m < 2 ? NaN : d0(log); // R gives NaN at x = Inf when df1 < 2
70
+ return fDensity(x, m, n, log);
71
+ }
72
+ /** P[F <= x] (lower) / P[F > x] (!lower). x finite and positive, m, n finite */
73
+ function pfRaw(x, m, n, lower, log) {
74
+ // Form m x/(n + m x) and n/(n + m x) in double-double (rounding would be amplified m/2 and n/2 times)
75
+ const mx = twoProd(m, x);
76
+ if (Number.isFinite(mx[0]) && Number.isFinite(mx[1]) && Math.abs(mx[0]) < 1e290) {
77
+ const d = ddAdd(mx, [n, 0]);
78
+ return betaIncDD(m / 2, n / 2, ddDiv(mx, d), ddDiv([n, 0], d), lower, log);
79
+ }
80
+ // m x is huge: form 1 - z = n/(n + m x) ≈ (n/m)/x directly
81
+ const zc = n / m / x;
82
+ return betaInc(m / 2, n / 2, 1 - zc, zc, lower, log);
83
+ }
84
+ /** Starting value for quantiles, from F = (n/m) Y/(1 - Y), Y ~ Beta(m/2, n/2), with the A&S 26.5.22 approximation (1 if unusable) */
85
+ function fQuantileGuess(m, n, lower, logTarget) {
86
+ const y = betaQuantileGuess(m / 2, n / 2, lower, logTarget);
87
+ if (y === undefined)
88
+ return 1;
89
+ const f = (n * y) / (m * (1 - y));
90
+ return f > 0 && Number.isFinite(f) ? f : 1;
91
+ }
92
+ /** [log, linear] of pfRaw (each value identical to pfRaw) */
93
+ function pfRawBoth(x, m, n, lower) {
94
+ const mx = twoProd(m, x);
95
+ if (Number.isFinite(mx[0]) && Number.isFinite(mx[1]) && Math.abs(mx[0]) < 1e290) {
96
+ const d = ddAdd(mx, [n, 0]);
97
+ return betaIncBothDD(m / 2, n / 2, ddDiv(mx, d), ddDiv([n, 0], d), lower);
98
+ }
99
+ const zc = n / m / x;
100
+ return betaIncBoth(m / 2, n / 2, 1 - zc, zc, lower);
101
+ }
102
+ export function pf1(q, m, n, lowerTail, logP) {
103
+ if (Number.isNaN(q) || Number.isNaN(m) || Number.isNaN(n))
104
+ return NaN;
105
+ if (m <= 0 || n <= 0)
106
+ return NaN;
107
+ if (q <= 0)
108
+ return dt0(lowerTail, logP);
109
+ if (q === Infinity)
110
+ return dt1(lowerTail, logP);
111
+ if (!Number.isFinite(m) && !Number.isFinite(n)) {
112
+ // Distribution concentrated at 1. R returns 1/2 at q = 1
113
+ if (q === 1)
114
+ return logP ? -Math.LN2 : 0.5;
115
+ return q < 1 ? dt0(lowerTail, logP) : dt1(lowerTail, logP);
116
+ }
117
+ if (!Number.isFinite(n))
118
+ return pgamma1(q * m, m / 2, 2, lowerTail, logP); // χ²_m
119
+ if (!Number.isFinite(m))
120
+ return pgamma1(n / q, n / 2, 2, !lowerTail, logP); // n / χ²_n
121
+ return pfRaw(q, m, n, lowerTail, logP);
122
+ }
123
+ export function qf1(p, m, n, lowerTail, logP) {
124
+ if (Number.isNaN(p) || Number.isNaN(m) || Number.isNaN(n))
125
+ return NaN;
126
+ if (m <= 0 || n <= 0)
127
+ return NaN;
128
+ const b = quantileBoundaries(p, 0, Infinity, lowerTail, logP);
129
+ if (b !== undefined)
130
+ return b;
131
+ if (!Number.isFinite(m) && !Number.isFinite(n))
132
+ return 1;
133
+ if (!Number.isFinite(n))
134
+ return qgamma1(p, m / 2, 2, lowerTail, logP) / m;
135
+ if (!Number.isFinite(m))
136
+ return n / qgamma1(p, n / 2, 2, !lowerTail, logP);
137
+ const { lower, logTarget, linearTarget } = smallerTail(p, lowerTail, logP);
138
+ // Near 0, P[F <= x] ≈ (m x / n)^(m/2) / ((m/2) B(m/2, n/2)). The answer is 0 if it underflows
139
+ if (lower && (logTarget + Math.log(m / 2) + lbeta(m / 2, n / 2)) / (m / 2) + Math.log(n / m) < -746)
140
+ return 0;
141
+ // Near infinity, P[F > x] ≈ (n / (m x))^(n/2) / ((n/2) B(m/2, n/2)). Inf if it overflows
142
+ if (!lower && -(logTarget + Math.log(n / 2) + lbeta(m / 2, n / 2)) / (n / 2) + Math.log(n / m) > 709.78)
143
+ return Infinity;
144
+ return invertContinuous({
145
+ logTarget,
146
+ ...(linearTarget !== undefined && { linearTarget }),
147
+ lower,
148
+ cdf: (x, l) => pfRaw(x, m, n, l, false),
149
+ cdfLog: (x, l) => pfRaw(x, m, n, l, true),
150
+ cdfBoth: (x, l) => pfRawBoth(x, m, n, l),
151
+ pdfLog: (x) => fDensity(x, m, n, true),
152
+ dlogPdf: (x) => (m / 2 - 1) / x - ((m + n) / 2) * (m / (n + m * x)),
153
+ x0: fQuantileGuess(m, n, lower, logTarget),
154
+ lo: 0,
155
+ hi: Infinity,
156
+ positive: true,
157
+ });
158
+ }
159
+ /**
160
+ * Density of the F distribution. Equivalent to R's `df(x, df1, df2, ncp, log = FALSE)`.
161
+ *
162
+ * If `ncp` is omitted the central distribution is used; otherwise the noncentral F distribution.
163
+ *
164
+ * @param x - Value(s) at which to evaluate: a number, `number[]` or `Float64Array`. The result has the same shape.
165
+ * @param df1 - Numerator degrees of freedom.
166
+ * @param df2 - Denominator degrees of freedom.
167
+ * @param options - `ncp` (see {@link NoncentralOption}) and `log` (see {@link DensityOptions}).
168
+ * @returns The density (or log density).
169
+ */
170
+ export function df(x, df1, df2, options = {}) {
171
+ const { log = false, ncp } = options;
172
+ if (ncp !== undefined)
173
+ return vectorize(x, (v) => dnf1(v, df1, df2, ncp, log));
174
+ return vectorize(x, (v) => dfDensity1(v, df1, df2, log));
175
+ }
176
+ /**
177
+ * Distribution function of the F distribution. Equivalent to R's `pf(q, df1, df2, ncp, lower.tail = TRUE, log.p = FALSE)`.
178
+ *
179
+ * If `ncp` is omitted the central distribution is used; otherwise the noncentral F distribution.
180
+ *
181
+ * @param q - Quantile(s): a number, `number[]` or `Float64Array`. The result has the same shape.
182
+ * @param df1 - Numerator degrees of freedom.
183
+ * @param df2 - Denominator degrees of freedom.
184
+ * @param options - `ncp` (see {@link NoncentralOption}), `lowerTail` and `logP` (see {@link TailOptions}).
185
+ * @returns The probability (or log probability).
186
+ *
187
+ * @example
188
+ * ```ts
189
+ * pf(4.1, 2, 20, { lowerTail: false }); // 0.03219651558603902
190
+ * ```
191
+ */
192
+ export function pf(q, df1, df2, options = {}) {
193
+ const { lowerTail = true, logP = false, ncp } = options;
194
+ if (ncp !== undefined)
195
+ return vectorize(q, (v) => pnf1(v, df1, df2, ncp, lowerTail, logP));
196
+ return vectorize(q, (v) => pf1(v, df1, df2, lowerTail, logP));
197
+ }
198
+ /**
199
+ * Quantile function of the F distribution. Equivalent to R's `qf(p, df1, df2, ncp, lower.tail = TRUE, log.p = FALSE)`.
200
+ *
201
+ * If `ncp` is omitted the central distribution is used; otherwise the noncentral F distribution.
202
+ *
203
+ * @param p - Probability (or log probability with `logP`): a number, `number[]` or `Float64Array`. The result has the same shape.
204
+ * @param df1 - Numerator degrees of freedom.
205
+ * @param df2 - Denominator degrees of freedom.
206
+ * @param options - `ncp` (see {@link NoncentralOption}), `lowerTail` and `logP` (see {@link TailOptions}).
207
+ * @returns The quantile(s).
208
+ */
209
+ export function qf(p, df1, df2, options = {}) {
210
+ const { lowerTail = true, logP = false, ncp } = options;
211
+ if (ncp !== undefined)
212
+ return vectorize(p, (v) => qnf1(v, df1, df2, ncp, lowerTail, logP));
213
+ return vectorize(p, (v) => qf1(v, df1, df2, lowerTail, logP));
214
+ }
@@ -0,0 +1,61 @@
1
+ import { type Vectorizable, type VectorResult } from "../internal/vectorize.js";
2
+ import { type DensityOptions, type TailOptions } from "../internal/dpq.js";
3
+ /**
4
+ * Parameters of the gamma distribution. Give either `rate` or `scale`; if both are given,
5
+ * `scale` must equal `1 / rate`.
6
+ */
7
+ export interface GammaParams {
8
+ /** Rate (`rate = 1 / scale`). Default `1`. */
9
+ rate?: number;
10
+ /** Scale. Default `1`. Do not combine with a different `rate`. */
11
+ scale?: number;
12
+ }
13
+ /** Determine scale from rate / scale (as in R, giving both is an error unless scale = 1/rate; also used by rgamma) */
14
+ export declare function gammaScale(params: GammaParams): number;
15
+ /** Log density of the standardized (scale = 1) gamma distribution. x > 0, shape > 0 */
16
+ export declare function dgammaStdLog(x: number, shape: number): number;
17
+ export declare function dgamma1(x: number, shape: number, scale: number, log: boolean): number;
18
+ export declare function pgamma1(q: number, shape: number, scale: number, lowerTail: boolean, logP: boolean): number;
19
+ /** Quantile of the standardized gamma distribution. 0 < p < 1 (range checks are the caller's job) */
20
+ export declare function qgammaStd(p: number, shape: number, lowerTail: boolean, logP: boolean): number;
21
+ export declare function qgamma1(p: number, shape: number, scale: number, lowerTail: boolean, logP: boolean): number;
22
+ /**
23
+ * Density of the gamma distribution. Equivalent to R's `dgamma(x, shape, rate = 1, scale = 1/rate, log = FALSE)`.
24
+ *
25
+ * @throws RangeError if both `rate` and `scale` are given and `scale` is not `1 / rate`.
26
+ *
27
+ * @param x - Value(s) at which to evaluate: a number, `number[]` or `Float64Array`. The result has the same shape.
28
+ * @param shape - Shape parameter.
29
+ * @param options - `rate` or `scale` (see {@link GammaParams}) and `log` (see {@link DensityOptions}).
30
+ * @returns The density (or log density).
31
+ */
32
+ export declare function dgamma<T extends Vectorizable>(x: T, shape: number, options?: GammaParams & DensityOptions): VectorResult<T>;
33
+ /**
34
+ * Distribution function of the gamma distribution. Equivalent to R's `pgamma(q, shape, rate = 1, scale = 1/rate, lower.tail = TRUE, log.p = FALSE)`.
35
+ *
36
+ * @throws RangeError if both `rate` and `scale` are given and `scale` is not `1 / rate`.
37
+ *
38
+ * @param q - Quantile(s): a number, `number[]` or `Float64Array`. The result has the same shape.
39
+ * @param shape - Shape parameter.
40
+ * @param options - `rate` or `scale` (see {@link GammaParams}), `lowerTail` and `logP` (see {@link TailOptions}).
41
+ * @returns The probability (or log probability).
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * pgamma(2, 2); // 0.5939941502901619
46
+ * pgamma(2, 2, { rate: 3 }); // 0.9826487347633355
47
+ * pgamma(2, 2, { scale: 3 }); // 0.14430480161234657
48
+ * ```
49
+ */
50
+ export declare function pgamma<T extends Vectorizable>(q: T, shape: number, options?: GammaParams & TailOptions): VectorResult<T>;
51
+ /**
52
+ * Quantile function of the gamma distribution. Equivalent to R's `qgamma(p, shape, rate = 1, scale = 1/rate, lower.tail = TRUE, log.p = FALSE)`.
53
+ *
54
+ * @throws RangeError if both `rate` and `scale` are given and `scale` is not `1 / rate`.
55
+ *
56
+ * @param p - Probability (or log probability with `logP`): a number, `number[]` or `Float64Array`. The result has the same shape.
57
+ * @param shape - Shape parameter.
58
+ * @param options - `rate` or `scale` (see {@link GammaParams}), `lowerTail` and `logP` (see {@link TailOptions}).
59
+ * @returns The quantile(s).
60
+ */
61
+ export declare function qgamma<T extends Vectorizable>(p: T, shape: number, options?: GammaParams & TailOptions): VectorResult<T>;
@@ -0,0 +1,188 @@
1
+ // Gamma distribution dgamma / pgamma / qgamma.
2
+ //
3
+ // The density uses the relation to the Poisson probability (Loader 2000). With y = x/scale,
4
+ // f(x) = dpois(shape - 1; y) / scale (shape >= 1)
5
+ // = dpois(shape; y) * shape / x (shape < 1)
6
+ // The distribution function uses the incomplete gamma function (src/special/igam.ts); quantiles use Newton's
7
+ // method (src/internal/invert.ts) from the DiDonato & Morris starting value.
8
+ import { vectorize } from "../internal/vectorize.js";
9
+ import { d0, dt0, dt1, quantileBoundaries, smallerTail } from "../internal/dpq.js";
10
+ import { invertContinuous } from "../internal/invert.js";
11
+ import { gammaInc, gammaIncBoth } from "../special/igam.js";
12
+ import { findInverseGamma } from "../special/igami.js";
13
+ import { lgamma, lgamma1p } from "../special/gamma.js";
14
+ import { dpoisRaw } from "../special/loader.js";
15
+ import { qnorm } from "./normal.js";
16
+ /** Determine scale from rate / scale (as in R, giving both is an error unless scale = 1/rate; also used by rgamma) */
17
+ export function gammaScale(params) {
18
+ const { rate, scale } = params;
19
+ if (rate !== undefined && scale !== undefined) {
20
+ if (Math.abs(rate * scale - 1) >= 1e-15) {
21
+ throw new RangeError("rate and scale can both be given only when scale = 1 / rate");
22
+ }
23
+ return scale;
24
+ }
25
+ if (scale !== undefined)
26
+ return scale;
27
+ if (rate !== undefined)
28
+ return 1 / rate;
29
+ return 1;
30
+ }
31
+ /** Log density of the standardized (scale = 1) gamma distribution. x > 0, shape > 0 */
32
+ export function dgammaStdLog(x, shape) {
33
+ if (shape < 1) {
34
+ const pr = dpoisRaw(shape, x, true);
35
+ const r = shape / x;
36
+ return pr + (Number.isFinite(r) ? Math.log(r) : Math.log(shape) - Math.log(x));
37
+ }
38
+ return dpoisRaw(shape - 1, x, true);
39
+ }
40
+ export function dgamma1(x, shape, scale, log) {
41
+ if (Number.isNaN(x) || Number.isNaN(shape) || Number.isNaN(scale))
42
+ return NaN;
43
+ if (shape < 0 || scale <= 0)
44
+ return NaN;
45
+ if (x < 0)
46
+ return d0(log);
47
+ if (shape === 0)
48
+ return x === 0 ? Infinity : d0(log);
49
+ if (x === 0) {
50
+ if (shape < 1)
51
+ return Infinity;
52
+ if (shape > 1)
53
+ return d0(log);
54
+ return log ? -Math.log(scale) : 1 / scale;
55
+ }
56
+ if (shape < 1) {
57
+ const pr = dpoisRaw(shape, x / scale, log);
58
+ if (log) {
59
+ const r = shape / x;
60
+ return pr + (Number.isFinite(r) ? Math.log(r) : Math.log(shape) - Math.log(x));
61
+ }
62
+ return (pr * shape) / x;
63
+ }
64
+ const pr = dpoisRaw(shape - 1, x / scale, log);
65
+ return log ? pr - Math.log(scale) : pr / scale;
66
+ }
67
+ export function pgamma1(q, shape, scale, lowerTail, logP) {
68
+ if (Number.isNaN(q) || Number.isNaN(shape) || Number.isNaN(scale))
69
+ return NaN;
70
+ if (shape < 0 || scale <= 0)
71
+ return NaN;
72
+ const x = q / scale;
73
+ if (Number.isNaN(x))
74
+ return x;
75
+ if (shape === 0)
76
+ return x <= 0 ? dt0(lowerTail, logP) : dt1(lowerTail, logP);
77
+ if (x <= 0)
78
+ return dt0(lowerTail, logP);
79
+ return gammaInc(shape, x, lowerTail, logP);
80
+ }
81
+ /** Quantile of the standardized gamma distribution. 0 < p < 1 (range checks are the caller's job) */
82
+ export function qgammaStd(p, shape, lowerTail, logP) {
83
+ const { lower, logTarget, linearTarget } = smallerTail(p, lowerTail, logP);
84
+ // If the distribution function at the smallest positive number already passes the target, the answer is too small to represent (0)
85
+ const atMin = gammaInc(shape, Number.MIN_VALUE, lower, true);
86
+ if (lower ? atMin >= logTarget : atMin <= logTarget)
87
+ return 0;
88
+ let x0;
89
+ if (shape > 1 && logTarget <= -700) {
90
+ // Deep tail: Wilson-Hilferty approximation x = a (1 - 1/(9a) + z / sqrt(9a))^3
91
+ const z = qnorm(logTarget, { lowerTail: lower, logP: true });
92
+ const w = 1 - 1 / (9 * shape) + z / Math.sqrt(9 * shape);
93
+ x0 = w > 0 ? shape * w * w * w : shape * 1e-3;
94
+ }
95
+ else if (logTarget > -700) {
96
+ const t = Math.exp(logTarget);
97
+ const c = -Math.expm1(logTarget);
98
+ x0 = lower ? findInverseGamma(shape, t, c) : findInverseGamma(shape, c, t);
99
+ }
100
+ else if (lower) {
101
+ // P(a, x) ≈ x^a / Gamma(a + 1)
102
+ x0 = Math.exp((logTarget + lgamma1p(shape)) / shape);
103
+ }
104
+ else {
105
+ // Q(a, x) ≈ x^(a-1) e^-x / Gamma(a)
106
+ x0 = -logTarget;
107
+ for (let i = 0; i < 3; i++)
108
+ x0 = -logTarget - lgamma(shape) + (shape - 1) * Math.log(x0);
109
+ }
110
+ return invertContinuous({
111
+ logTarget,
112
+ ...(linearTarget !== undefined && { linearTarget }),
113
+ lower,
114
+ cdf: (x, l) => gammaInc(shape, x, l, false),
115
+ cdfLog: (x, l) => gammaInc(shape, x, l, true),
116
+ cdfBoth: (x, l) => gammaIncBoth(shape, x, l),
117
+ pdfLog: (x) => dgammaStdLog(x, shape),
118
+ dlogPdf: (x) => (shape - 1) / x - 1,
119
+ x0,
120
+ lo: 0,
121
+ hi: Infinity,
122
+ positive: true,
123
+ });
124
+ }
125
+ export function qgamma1(p, shape, scale, lowerTail, logP) {
126
+ if (Number.isNaN(p) || Number.isNaN(shape) || Number.isNaN(scale))
127
+ return NaN;
128
+ const b = quantileBoundaries(p, 0, Infinity, lowerTail, logP);
129
+ if (b !== undefined)
130
+ return b;
131
+ if (shape < 0 || scale <= 0)
132
+ return NaN;
133
+ if (shape === 0)
134
+ return 0;
135
+ return qgammaStd(p, shape, lowerTail, logP) * scale;
136
+ }
137
+ /**
138
+ * Density of the gamma distribution. Equivalent to R's `dgamma(x, shape, rate = 1, scale = 1/rate, log = FALSE)`.
139
+ *
140
+ * @throws RangeError if both `rate` and `scale` are given and `scale` is not `1 / rate`.
141
+ *
142
+ * @param x - Value(s) at which to evaluate: a number, `number[]` or `Float64Array`. The result has the same shape.
143
+ * @param shape - Shape parameter.
144
+ * @param options - `rate` or `scale` (see {@link GammaParams}) and `log` (see {@link DensityOptions}).
145
+ * @returns The density (or log density).
146
+ */
147
+ export function dgamma(x, shape, options = {}) {
148
+ const scale = gammaScale(options);
149
+ const log = options.log ?? false;
150
+ return vectorize(x, (v) => dgamma1(v, shape, scale, log));
151
+ }
152
+ /**
153
+ * Distribution function of the gamma distribution. Equivalent to R's `pgamma(q, shape, rate = 1, scale = 1/rate, lower.tail = TRUE, log.p = FALSE)`.
154
+ *
155
+ * @throws RangeError if both `rate` and `scale` are given and `scale` is not `1 / rate`.
156
+ *
157
+ * @param q - Quantile(s): a number, `number[]` or `Float64Array`. The result has the same shape.
158
+ * @param shape - Shape parameter.
159
+ * @param options - `rate` or `scale` (see {@link GammaParams}), `lowerTail` and `logP` (see {@link TailOptions}).
160
+ * @returns The probability (or log probability).
161
+ *
162
+ * @example
163
+ * ```ts
164
+ * pgamma(2, 2); // 0.5939941502901619
165
+ * pgamma(2, 2, { rate: 3 }); // 0.9826487347633355
166
+ * pgamma(2, 2, { scale: 3 }); // 0.14430480161234657
167
+ * ```
168
+ */
169
+ export function pgamma(q, shape, options = {}) {
170
+ const scale = gammaScale(options);
171
+ const { lowerTail = true, logP = false } = options;
172
+ return vectorize(q, (v) => pgamma1(v, shape, scale, lowerTail, logP));
173
+ }
174
+ /**
175
+ * Quantile function of the gamma distribution. Equivalent to R's `qgamma(p, shape, rate = 1, scale = 1/rate, lower.tail = TRUE, log.p = FALSE)`.
176
+ *
177
+ * @throws RangeError if both `rate` and `scale` are given and `scale` is not `1 / rate`.
178
+ *
179
+ * @param p - Probability (or log probability with `logP`): a number, `number[]` or `Float64Array`. The result has the same shape.
180
+ * @param shape - Shape parameter.
181
+ * @param options - `rate` or `scale` (see {@link GammaParams}), `lowerTail` and `logP` (see {@link TailOptions}).
182
+ * @returns The quantile(s).
183
+ */
184
+ export function qgamma(p, shape, options = {}) {
185
+ const scale = gammaScale(options);
186
+ const { lowerTail = true, logP = false } = options;
187
+ return vectorize(p, (v) => qgamma1(v, shape, scale, lowerTail, logP));
188
+ }
@@ -0,0 +1,38 @@
1
+ import { type Vectorizable, type VectorResult } from "../internal/vectorize.js";
2
+ import { type DensityOptions, type TailOptions } from "../internal/dpq.js";
3
+ import { type DD } from "../special/dd.js";
4
+ /** p and q = 1 - p in double-double (the smaller one is the exact base) */
5
+ export declare function probPair(prob: number): {
6
+ pd: DD;
7
+ qd: DD;
8
+ };
9
+ export declare function dgeom1(x: number, prob: number, log: boolean): number;
10
+ export declare function pgeom1(q: number, prob: number, lowerTail: boolean, logP: boolean): number;
11
+ export declare function qgeom1(p: number, prob: number, lowerTail: boolean, logP: boolean): number;
12
+ /**
13
+ * Probability mass function of the geometric distribution (number of failures before the first success). Equivalent to R's `dgeom(x, prob, log = FALSE)`.
14
+ *
15
+ * @param x - Value(s) at which to evaluate: a number, `number[]` or `Float64Array`. The result has the same shape.
16
+ * @param prob - Probability of success on each trial.
17
+ * @param options - `log` (see {@link DensityOptions}).
18
+ * @returns The probability (or log probability).
19
+ */
20
+ export declare function dgeom<T extends Vectorizable>(x: T, prob: number, options?: DensityOptions): VectorResult<T>;
21
+ /**
22
+ * Distribution function of the geometric distribution (number of failures before the first success). Equivalent to R's `pgeom(q, prob, lower.tail = TRUE, log.p = FALSE)`.
23
+ *
24
+ * @param q - Quantile(s): a number, `number[]` or `Float64Array`. The result has the same shape.
25
+ * @param prob - Probability of success on each trial.
26
+ * @param options - `lowerTail` and `logP` (see {@link TailOptions}).
27
+ * @returns The probability (or log probability).
28
+ */
29
+ export declare function pgeom<T extends Vectorizable>(q: T, prob: number, options?: TailOptions): VectorResult<T>;
30
+ /**
31
+ * Quantile function of the geometric distribution (number of failures before the first success). Equivalent to R's `qgeom(p, prob, lower.tail = TRUE, log.p = FALSE)`.
32
+ *
33
+ * @param p - Probability (or log probability with `logP`): a number, `number[]` or `Float64Array`. The result has the same shape.
34
+ * @param prob - Probability of success on each trial.
35
+ * @param options - `lowerTail` and `logP` (see {@link TailOptions}).
36
+ * @returns The quantile(s).
37
+ */
38
+ export declare function qgeom<T extends Vectorizable>(p: T, prob: number, options?: TailOptions): VectorResult<T>;
@@ -0,0 +1,116 @@
1
+ // Geometric distribution dgeom / pgeom / qgeom.
2
+ //
3
+ // P[X = x] = p q^x, P[X > x] = q^(x+1) (q = 1 - p). q^n is computed with Loader's (2000) building blocks
4
+ // (dbinomRawDD in src/special/loader.ts, as the binomial probability of 0 successes and n failures)
5
+ // (the exponent is kept in double-double so that the rounding of n log q is not amplified n times).
6
+ // Quantiles use a closed form.
7
+ import { vectorize } from "../internal/vectorize.js";
8
+ import { d0, dt0, dt1, quantileBoundaries } from "../internal/dpq.js";
9
+ import { log1mExp } from "../internal/ddexp.js";
10
+ import { nonInt } from "../internal/integer.js";
11
+ import { dbinomRawDD } from "../special/loader.js";
12
+ import { twoSum } from "../special/dd.js";
13
+ /** p and q = 1 - p in double-double (the smaller one is the exact base) */
14
+ export function probPair(prob) {
15
+ return prob <= 0.5
16
+ ? { pd: [prob, 0], qd: twoSum(1, -prob) }
17
+ : { pd: twoSum(1, -(1 - prob)), qd: [1 - prob, 0] };
18
+ }
19
+ function badProb(prob) {
20
+ return !(prob > 0) || prob > 1;
21
+ }
22
+ export function dgeom1(x, prob, log) {
23
+ if (Number.isNaN(x) || Number.isNaN(prob))
24
+ return NaN;
25
+ if (badProb(prob))
26
+ return NaN;
27
+ if (nonInt(x) || x < 0 || !Number.isFinite(x))
28
+ return d0(log);
29
+ const n = Math.round(x);
30
+ const { pd, qd } = probPair(prob);
31
+ // q^n
32
+ const qn = dbinomRawDD(0, n, pd, qd, log);
33
+ return log ? Math.log(prob) + qn : prob * qn;
34
+ }
35
+ /** P[X > x] = q^(x+1) (x a nonnegative integer) */
36
+ function upper(x, prob, log) {
37
+ const { pd, qd } = probPair(prob);
38
+ return dbinomRawDD(0, x + 1, pd, qd, log);
39
+ }
40
+ function pgeomRaw(x, prob, lowerTail, logP) {
41
+ if (!lowerTail)
42
+ return upper(x, prob, logP);
43
+ const lu = upper(x, prob, true);
44
+ if (!logP)
45
+ return -Math.expm1(lu);
46
+ // When the upper tail is small, use log1p of the linear value (whose exponent was computed in double-double)
47
+ return lu < -Math.LN2 ? Math.log1p(-upper(x, prob, false)) : Math.log(-Math.expm1(lu));
48
+ }
49
+ export function pgeom1(q, prob, lowerTail, logP) {
50
+ if (Number.isNaN(q) || Number.isNaN(prob))
51
+ return NaN;
52
+ if (badProb(prob))
53
+ return NaN;
54
+ if (q < 0)
55
+ return dt0(lowerTail, logP);
56
+ if (!Number.isFinite(q) || prob === 1)
57
+ return dt1(lowerTail, logP);
58
+ return pgeomRaw(Math.floor(q + 1e-7), prob, lowerTail, logP);
59
+ }
60
+ export function qgeom1(p, prob, lowerTail, logP) {
61
+ if (Number.isNaN(p) || Number.isNaN(prob))
62
+ return NaN;
63
+ if (badProb(prob))
64
+ return NaN;
65
+ const b = quantileBoundaries(p, 0, Infinity, lowerTail, logP);
66
+ if (Number.isNaN(b))
67
+ return NaN;
68
+ if (prob === 1)
69
+ return 0;
70
+ if (b !== undefined)
71
+ return b;
72
+ // Smallest integer y with P[X > y] = q^(y+1) <= (upper probability), solved in closed form
73
+ // (matches R's output. The distribution function is flat, so in an integer search the probability slack would move the answer a lot)
74
+ let lu;
75
+ if (logP)
76
+ lu = lowerTail ? log1mExp(p) : p;
77
+ else
78
+ lu = lowerTail ? Math.log1p(-p) : Math.log(p);
79
+ return Math.max(0, Math.ceil(lu / Math.log1p(-prob) - 1 - 1e-12));
80
+ }
81
+ /**
82
+ * Probability mass function of the geometric distribution (number of failures before the first success). Equivalent to R's `dgeom(x, prob, log = FALSE)`.
83
+ *
84
+ * @param x - Value(s) at which to evaluate: a number, `number[]` or `Float64Array`. The result has the same shape.
85
+ * @param prob - Probability of success on each trial.
86
+ * @param options - `log` (see {@link DensityOptions}).
87
+ * @returns The probability (or log probability).
88
+ */
89
+ export function dgeom(x, prob, options = {}) {
90
+ const log = options.log ?? false;
91
+ return vectorize(x, (v) => dgeom1(v, prob, log));
92
+ }
93
+ /**
94
+ * Distribution function of the geometric distribution (number of failures before the first success). Equivalent to R's `pgeom(q, prob, lower.tail = TRUE, log.p = FALSE)`.
95
+ *
96
+ * @param q - Quantile(s): a number, `number[]` or `Float64Array`. The result has the same shape.
97
+ * @param prob - Probability of success on each trial.
98
+ * @param options - `lowerTail` and `logP` (see {@link TailOptions}).
99
+ * @returns The probability (or log probability).
100
+ */
101
+ export function pgeom(q, prob, options = {}) {
102
+ const { lowerTail = true, logP = false } = options;
103
+ return vectorize(q, (v) => pgeom1(v, prob, lowerTail, logP));
104
+ }
105
+ /**
106
+ * Quantile function of the geometric distribution (number of failures before the first success). Equivalent to R's `qgeom(p, prob, lower.tail = TRUE, log.p = FALSE)`.
107
+ *
108
+ * @param p - Probability (or log probability with `logP`): a number, `number[]` or `Float64Array`. The result has the same shape.
109
+ * @param prob - Probability of success on each trial.
110
+ * @param options - `lowerTail` and `logP` (see {@link TailOptions}).
111
+ * @returns The quantile(s).
112
+ */
113
+ export function qgeom(p, prob, options = {}) {
114
+ const { lowerTail = true, logP = false } = options;
115
+ return vectorize(p, (v) => qgeom1(v, prob, lowerTail, logP));
116
+ }
@@ -0,0 +1,38 @@
1
+ import { type Vectorizable, type VectorResult } from "../internal/vectorize.js";
2
+ import { type DensityOptions, type TailOptions } from "../internal/dpq.js";
3
+ export declare function dhyper1(x: number, m: number, n: number, k: number, log: boolean): number;
4
+ export declare function phyper1(q: number, m: number, n: number, k: number, lowerTail: boolean, logP: boolean): number;
5
+ export declare function qhyper1(p: number, m: number, n: number, k: number, lowerTail: boolean, logP: boolean): number;
6
+ /**
7
+ * Probability mass function of the hypergeometric distribution (number of white balls among `k` drawn without replacement from an urn of `m` white and `n` black balls). Equivalent to R's `dhyper(x, m, n, k, log = FALSE)`.
8
+ *
9
+ * @param x - Value(s) at which to evaluate: a number, `number[]` or `Float64Array`. The result has the same shape.
10
+ * @param m - Number of white balls in the urn.
11
+ * @param n - Number of black balls in the urn.
12
+ * @param k - Number of balls drawn.
13
+ * @param options - `log` (see {@link DensityOptions}).
14
+ * @returns The probability (or log probability).
15
+ */
16
+ export declare function dhyper<T extends Vectorizable>(x: T, m: number, n: number, k: number, options?: DensityOptions): VectorResult<T>;
17
+ /**
18
+ * Distribution function of the hypergeometric distribution (number of white balls among `k` drawn without replacement from an urn of `m` white and `n` black balls). Equivalent to R's `phyper(q, m, n, k, lower.tail = TRUE, log.p = FALSE)`.
19
+ *
20
+ * @param q - Quantile(s): a number, `number[]` or `Float64Array`. The result has the same shape.
21
+ * @param m - Number of white balls in the urn.
22
+ * @param n - Number of black balls in the urn.
23
+ * @param k - Number of balls drawn.
24
+ * @param options - `lowerTail` and `logP` (see {@link TailOptions}).
25
+ * @returns The probability (or log probability).
26
+ */
27
+ export declare function phyper<T extends Vectorizable>(q: T, m: number, n: number, k: number, options?: TailOptions): VectorResult<T>;
28
+ /**
29
+ * Quantile function of the hypergeometric distribution (number of white balls among `k` drawn without replacement from an urn of `m` white and `n` black balls). Equivalent to R's `qhyper(p, m, n, k, lower.tail = TRUE, log.p = FALSE)`.
30
+ *
31
+ * @param p - Probability (or log probability with `logP`): a number, `number[]` or `Float64Array`. The result has the same shape.
32
+ * @param m - Number of white balls in the urn.
33
+ * @param n - Number of black balls in the urn.
34
+ * @param k - Number of balls drawn.
35
+ * @param options - `lowerTail` and `logP` (see {@link TailOptions}).
36
+ * @returns The quantile(s).
37
+ */
38
+ export declare function qhyper<T extends Vectorizable>(p: T, m: number, n: number, k: number, options?: TailOptions): VectorResult<T>;