bigint-decimal 1.0.20 → 1.0.21

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.
@@ -1,240 +1,789 @@
1
- export class BigintDecimal {
2
- value = 0n;
3
- decimals = 0;
4
- static precision = 20;
5
- precision = BigintDecimal.precision;
6
- constructor(val = "0") {
7
- if (val instanceof BigintDecimal) {
8
- this.value = val.value;
9
- this.decimals = val.decimals;
10
- return;
11
- }
12
- if (!this.isBigintDecimal(val)) {
13
- throw new Error(`"${val}" is not safe parameter .`);
14
- }
15
- const numberInfo = this.getNumberIFoForBigintDecimal(val);
16
- this.value = numberInfo.value;
17
- this.decimals = numberInfo.decimals;
18
- this.trim();
19
- }
20
- static setPrecisionGlobal(newPrecision) {
21
- this.precision = newPrecision;
22
- }
23
- setPrecision(newPrecision) {
24
- this.precision = newPrecision;
25
- }
26
- trim() {
27
- if (this.value === 0n) {
28
- this.value = 0n;
29
- this.decimals = 0;
30
- }
31
- // While we have decimals AND the last digit is 0
32
- while (this.decimals > 0 && this.value !== 0n && this.value % 10n === 0n) {
33
- this.value /= 10n; // Shrink the number
34
- this.decimals--; // Shrink the scale
35
- }
36
- return this;
37
- }
38
- toSafeValue(val) {
39
- if (typeof val === "string") {
40
- return val.trim();
41
- }
42
- return `${val}`;
43
- }
44
- getNumberIFoForBigintDecimal(val) {
45
- const safeVal = this.toSafeValue(val);
46
- let isDot = false, value = 0n, decimals = 0, sign = 1n;
47
- for (let i = 0; i < safeVal.length; i++) {
48
- const code = safeVal.charCodeAt(i);
49
- if (code >= 48 && code <= 57) {
50
- value = (value * 10n) + BigInt(code - 48);
51
- if (isDot)
52
- decimals++;
53
- continue;
54
- }
55
- if (code === 46 && !isDot) {
56
- isDot = true;
57
- continue;
1
+ // ".js" is required for the ESM build: Node's ESM loader needs the file extension
2
+ import { BigintDecimalBase } from "./bigintDecimalBase.js";
3
+ /**
4
+ * BigintDecimal with the JS `Math` object's members as statics
5
+ * (except fround, f16round, clz32 and imul, which only make sense for JS floats and 32-bit integers).
6
+ * Functions take the same values as the constructor and return BigintDecimal,
7
+ * computed to `BigintDecimal.precision` decimal places.
8
+ * Where JS Math would return NaN or ±Infinity, they throw a RangeError.
9
+ */
10
+ export class BigintDecimal extends BigintDecimalBase {
11
+ constructor(...args) {
12
+ super(...args);
13
+ }
14
+ // ---------------------------------------------------------------- constants
15
+ /** Euler's number, e ≈ 2.718 */
16
+ static get E() { return BigintDecimal.exp(1); }
17
+ /** Natural logarithm of 10 ≈ 2.303 */
18
+ static get LN10() { return BigintDecimal.compute((w) => BigintDecimal.ln10Fixed(w)); }
19
+ /** Natural logarithm of 2 ≈ 0.693 */
20
+ static get LN2() { return BigintDecimal.compute((w) => BigintDecimal.ln2Fixed(w)); }
21
+ /** Base-10 logarithm of e ≈ 0.434 */
22
+ static get LOG10E() { return BigintDecimal.compute((w) => BigintDecimal.one(2 * w) / BigintDecimal.ln10Fixed(w)); }
23
+ /** Base-2 logarithm of e ≈ 1.443 */
24
+ static get LOG2E() { return BigintDecimal.compute((w) => BigintDecimal.one(2 * w) / BigintDecimal.ln2Fixed(w)); }
25
+ /** Ratio of a circle's circumference to its diameter ≈ 3.14159 */
26
+ static get PI() { return BigintDecimal.compute((w) => BigintDecimal.piFixed(w)); }
27
+ /** Square root of 1/2 ≈ 0.707 */
28
+ static get SQRT1_2() { return BigintDecimal.sqrt("0.5"); }
29
+ /** Square root of 2 ≈ 1.414 */
30
+ static get SQRT2() { return BigintDecimal.sqrt(2); }
31
+ // ---------------------------------------------------------------- rounding & sign
32
+ /** Absolute value */
33
+ static abs(x) {
34
+ const p = BigintDecimal.parts(x);
35
+ return BigintDecimal.fromParts(BigintDecimal.absBig(p.v), p.d);
36
+ }
37
+ /** -1, 0 or 1, depending on the sign of x */
38
+ static sign(x) {
39
+ const v = BigintDecimal.parts(x).v;
40
+ return new BigintDecimal(v > 0n ? 1 : v < 0n ? -1 : 0);
41
+ }
42
+ /** Integer part of x (removes the fractional digits) */
43
+ static trunc(x) {
44
+ return new BigintDecimal(BigintDecimal.integerPart(BigintDecimal.parts(x)));
45
+ }
46
+ /** Largest integer less than or equal to x */
47
+ static floor(x) {
48
+ const p = BigintDecimal.parts(x);
49
+ return new BigintDecimal(BigintDecimal.floorDiv(p.v, BigintDecimal.pow10(p.d)));
50
+ }
51
+ /** Smallest integer greater than or equal to x */
52
+ static ceil(x) {
53
+ const p = BigintDecimal.parts(x);
54
+ return new BigintDecimal(-BigintDecimal.floorDiv(-p.v, BigintDecimal.pow10(p.d)));
55
+ }
56
+ /** Rounds to the nearest integer; halves go up (toward +Infinity), like Math.round */
57
+ static round(x) {
58
+ const p = BigintDecimal.parts(x);
59
+ const scale = BigintDecimal.pow10(p.d);
60
+ return new BigintDecimal(BigintDecimal.floorDiv(2n * p.v + scale, 2n * scale));
61
+ }
62
+ // ---------------------------------------------------------------- aggregates
63
+ /** Largest of the arguments (at least one is required) */
64
+ static max(...values) {
65
+ return BigintDecimal.pick(values, "max", (best, next) => next.gt(best));
66
+ }
67
+ /** Smallest of the arguments (at least one is required) */
68
+ static min(...values) {
69
+ return BigintDecimal.pick(values, "min", (best, next) => next.lt(best));
70
+ }
71
+ /** Exact sum of all values in an iterable */
72
+ static sumPrecise(values) {
73
+ let sum = new BigintDecimal(0);
74
+ for (const value of values)
75
+ sum = sum.plus(value);
76
+ return sum;
77
+ }
78
+ /** Square root of the sum of squares of the arguments */
79
+ static hypot(...values) {
80
+ let sum = new BigintDecimal(0);
81
+ for (const value of values)
82
+ sum = sum.plus(new BigintDecimal(value).times(value));
83
+ return BigintDecimal.sqrt(sum);
84
+ }
85
+ // ---------------------------------------------------------------- powers & roots
86
+ /** Square root. Argument must be >= 0. Always correctly rounded. */
87
+ static sqrt(x) {
88
+ const p = BigintDecimal.parts(x);
89
+ if (p.v < 0n)
90
+ throw new RangeError("sqrt: argument must be >= 0 (NaN)");
91
+ // Computed in decimal: r = floor(sqrt(x) * 10^P), then
92
+ // sqrt(x) >= r + 1/2 (in units of 10^-P) <=> floor(4x * 10^2P) >= (2r + 1)^2, exactly.
93
+ const P = BigintDecimal.precision;
94
+ const r = BigintDecimal.isqrt(BigintDecimal.floorScale(p.v, p.d, 2 * P));
95
+ const roundUp = BigintDecimal.floorScale(4n * p.v, p.d, 2 * P) >= (2n * r + 1n) ** 2n;
96
+ return BigintDecimal.fromParts(roundUp ? r + 1n : r, P);
97
+ }
98
+ /** Cube root. Always correctly rounded. */
99
+ static cbrt(x) {
100
+ const p = BigintDecimal.parts(x);
101
+ const v = BigintDecimal.absBig(p.v);
102
+ // Same as sqrt: r = floor(cbrt(|x|) * 10^P), rounded up when floor(8|x| * 10^3P) >= (2r + 1)^3
103
+ const P = BigintDecimal.precision;
104
+ const r = BigintDecimal.icbrt(BigintDecimal.floorScale(v, p.d, 3 * P));
105
+ const rounded = BigintDecimal.floorScale(8n * v, p.d, 3 * P) >= (2n * r + 1n) ** 3n ? r + 1n : r;
106
+ return BigintDecimal.fromParts(p.v < 0n ? -rounded : rounded, P);
107
+ }
108
+ /** base raised to the power of exponent. Integer powers are exact where the result stays reasonably sized. */
109
+ static pow(base, exponent) {
110
+ const x = BigintDecimal.parts(base), y = BigintDecimal.parts(exponent);
111
+ if (y.v === 0n)
112
+ return new BigintDecimal(1);
113
+ if (x.v === 0n) {
114
+ if (y.v > 0n)
115
+ return new BigintDecimal(0);
116
+ throw new RangeError("pow: 0 to a negative power is Infinity");
117
+ }
118
+ if (y.d === 0) {
119
+ // Integer exponent: exact power, unless the base's decimals would multiply into far more
120
+ // digits than the rounded result keeps (e.g. 1.0001^1e9); then e^(y * ln x) is much cheaper.
121
+ const n = BigintDecimal.absBig(y.v);
122
+ if (x.d === 0 || BigInt(x.d) * n <= BigInt(4 * BigintDecimal.precision + 100)) {
123
+ const v = x.v ** n;
124
+ const d = x.d * Number(n);
125
+ return y.v > 0n
126
+ ? BigintDecimal.roundDecimal(v, d)
127
+ : BigintDecimal.compute((w) => (BigintDecimal.pow10(d) << BigInt(w)) / v);
58
128
  }
59
- if (i === 0 && (code === 45 || code === 43)) {
60
- if (code === 45)
61
- sign = -1n;
62
- continue;
129
+ }
130
+ else if (x.v < 0n) {
131
+ throw new RangeError("pow: negative base with a non-integer exponent is NaN");
132
+ }
133
+ // x^y = e^(y * ln|x|). The result's integer bits need extra working precision,
134
+ // estimated from a cheap low-precision ln|x|.
135
+ const absX = { v: BigintDecimal.absBig(x.v), d: x.d };
136
+ const roughLnX = Number(BigintDecimal.lnFixed(absX.v, 0, absX.d, 64)) / 2 ** 64;
137
+ const magnitude = Math.max(0, Math.ceil(Number(new BigintDecimal(exponent).toString()) * roughLnX * Math.LOG2E)) + 1;
138
+ if (!Number.isFinite(magnitude))
139
+ throw new RangeError("pow: result is too large to represent");
140
+ const negative = x.v < 0n && (y.v & 1n) === 1n;
141
+ return BigintDecimal.compute((w) => {
142
+ // ln|x| is multiplied by y, so it needs y's integer bits as extra precision too
143
+ const ww = w + BigintDecimal.GUARD_BITS + magnitude + BigintDecimal.bitLength(BigintDecimal.integerPart(y));
144
+ const t = (BigintDecimal.toFixed(y, ww) * BigintDecimal.lnFixed(absX.v, 0, absX.d, ww)) >> BigInt(ww);
145
+ const r = BigintDecimal.expFixed(t, ww, w);
146
+ return negative ? -r : r;
147
+ });
148
+ }
149
+ // ---------------------------------------------------------------- exponentials & logarithms
150
+ /** e raised to the power of x */
151
+ static exp(x) {
152
+ const p = BigintDecimal.parts(x);
153
+ return BigintDecimal.compute((w) => BigintDecimal.expParts(p, w));
154
+ }
155
+ /** e^x - 1 */
156
+ static expm1(x) {
157
+ const p = BigintDecimal.parts(x);
158
+ return BigintDecimal.compute((w) => BigintDecimal.expParts(p, w) - BigintDecimal.one(w));
159
+ }
160
+ /** Natural logarithm (base e). Argument must be > 0. */
161
+ static log(x) {
162
+ return BigintDecimal.logBase(x, "log");
163
+ }
164
+ /** Base-10 logarithm. Argument must be > 0. */
165
+ static log10(x) {
166
+ return BigintDecimal.logBase(x, "log10", (w) => BigintDecimal.ln10Fixed(w));
167
+ }
168
+ /** Base-2 logarithm. Argument must be > 0. */
169
+ static log2(x) {
170
+ return BigintDecimal.logBase(x, "log2", (w) => BigintDecimal.ln2Fixed(w));
171
+ }
172
+ /** Natural logarithm of 1 + x. Argument must be > -1. */
173
+ static log1p(x) {
174
+ return BigintDecimal.logBase(new BigintDecimal(x).plus(1), "log1p");
175
+ }
176
+ // ---------------------------------------------------------------- trigonometry (radians)
177
+ /** Sine */
178
+ static sin(x) {
179
+ const p = BigintDecimal.parts(x);
180
+ return BigintDecimal.compute((w) => BigintDecimal.sinCosParts(p, w)[0]);
181
+ }
182
+ /** Cosine */
183
+ static cos(x) {
184
+ const p = BigintDecimal.parts(x);
185
+ return BigintDecimal.compute((w) => BigintDecimal.sinCosParts(p, w)[1]);
186
+ }
187
+ /** Tangent */
188
+ static tan(x) {
189
+ const p = BigintDecimal.parts(x);
190
+ return BigintDecimal.compute((w) => {
191
+ // Near the poles cos(x) is tiny and sin / cos multiplies its error by 1 / cos^2,
192
+ // so the working precision grows until cos has enough correct bits.
193
+ // cos is never exactly 0 for a decimal x (pi is irrational), so this ends.
194
+ let ww = w + BigintDecimal.GUARD_BITS;
195
+ while (true) {
196
+ const [sin, cos] = BigintDecimal.sinCosParts(p, ww);
197
+ const needed = w + 2 * (ww - BigintDecimal.bitLength(cos)) + BigintDecimal.GUARD_BITS;
198
+ if (cos !== 0n && ww >= needed)
199
+ return ((sin << BigInt(ww)) / cos) >> BigInt(ww - w);
200
+ ww = cos === 0n ? 2 * ww : needed;
63
201
  }
64
- // Scientific Notation Handle
65
- if (code === 101 || code === 69) {
66
- const exp = parseInt(safeVal.slice(i + 1), 10);
67
- if (isNaN(exp))
68
- throw new Error("Invalid exponent");
69
- decimals -= exp;
70
- break;
202
+ });
203
+ }
204
+ /** Arcsine. Argument must be in [-1, 1]. */
205
+ static asin(x) {
206
+ const p = BigintDecimal.parts(x);
207
+ return BigintDecimal.compute((w) => BigintDecimal.asinFixed(p, w));
208
+ }
209
+ /** Arccosine. Argument must be in [-1, 1]. */
210
+ static acos(x) {
211
+ const p = BigintDecimal.parts(x);
212
+ return BigintDecimal.compute((w) => (BigintDecimal.piFixed(w) >> 1n) - BigintDecimal.asinFixed(p, w));
213
+ }
214
+ /** Arctangent */
215
+ static atan(x) {
216
+ const p = BigintDecimal.parts(x);
217
+ return BigintDecimal.compute((w) => BigintDecimal.atanFixed(BigintDecimal.toFixed(p, w), w));
218
+ }
219
+ /** Angle between the positive x-axis and the point (x, y). Note the order: (y, x). */
220
+ static atan2(y, x) {
221
+ const yp = BigintDecimal.parts(y), xp = BigintDecimal.parts(x);
222
+ return BigintDecimal.compute((w) => {
223
+ const pi = BigintDecimal.piFixed(w);
224
+ if (xp.v === 0n)
225
+ return yp.v > 0n ? pi >> 1n : yp.v < 0n ? -(pi >> 1n) : 0n;
226
+ // y / x computed exactly from the parts
227
+ const ratio = ((yp.v * BigintDecimal.pow10(xp.d)) << BigInt(w)) / (xp.v * BigintDecimal.pow10(yp.d));
228
+ const a = BigintDecimal.atanFixed(ratio, w);
229
+ if (xp.v > 0n)
230
+ return a;
231
+ return yp.v >= 0n ? a + pi : a - pi;
232
+ });
233
+ }
234
+ // ---------------------------------------------------------------- hyperbolic
235
+ /** Hyperbolic sine */
236
+ static sinh(x) {
237
+ return BigintDecimal.expPair(x, (ePos, eNeg) => (ePos - eNeg) >> 1n);
238
+ }
239
+ /** Hyperbolic cosine */
240
+ static cosh(x) {
241
+ return BigintDecimal.expPair(x, (ePos, eNeg) => (ePos + eNeg) >> 1n);
242
+ }
243
+ /** Hyperbolic tangent */
244
+ static tanh(x) {
245
+ const p = BigintDecimal.parts(x);
246
+ return BigintDecimal.compute((w) => {
247
+ // tanh|x| = (1 - e^(-2|x|)) / (1 + e^(-2|x|)), which never builds huge numbers
248
+ const one = BigintDecimal.one(w);
249
+ const t = BigintDecimal.expParts({ v: -2n * BigintDecimal.absBig(p.v), d: p.d }, w);
250
+ const r = ((one - t) << BigInt(w)) / (one + t);
251
+ return p.v < 0n ? -r : r;
252
+ });
253
+ }
254
+ /** Hyperbolic arcsine */
255
+ static asinh(x) {
256
+ const value = new BigintDecimal(x);
257
+ // asinh(-x) = -asinh(x)
258
+ return BigintDecimal.lnPlusRoot(BigintDecimal.abs(value), 1, value.lt(0));
259
+ }
260
+ /** Hyperbolic arccosine. Argument must be >= 1. */
261
+ static acosh(x) {
262
+ const value = new BigintDecimal(x);
263
+ if (value.lt(1))
264
+ throw new RangeError("acosh: argument must be >= 1 (NaN)");
265
+ return BigintDecimal.lnPlusRoot(value, -1);
266
+ }
267
+ /** Hyperbolic arctangent. Argument must be in (-1, 1). */
268
+ static atanh(x) {
269
+ const value = new BigintDecimal(x);
270
+ if (value.gte(1) || value.lte(-1))
271
+ throw new RangeError("atanh: argument must be between -1 and 1 (exclusive)");
272
+ // (ln(1 + x) - ln(1 - x)) / 2, both arguments exact
273
+ const plus = BigintDecimal.parts(value.plus(1));
274
+ const minus = BigintDecimal.parts(new BigintDecimal(1).minus(value));
275
+ return BigintDecimal.compute((w) => (BigintDecimal.lnFixed(plus.v, 0, plus.d, w) - BigintDecimal.lnFixed(minus.v, 0, minus.d, w)) >> 1n);
276
+ }
277
+ // ---------------------------------------------------------------- random
278
+ /** Random number in [0, 1) with `BigintDecimal.precision` decimal digits */
279
+ static random() {
280
+ const p = BigintDecimal.precision;
281
+ const limit = BigintDecimal.pow10(p);
282
+ // 8 extra bytes make the modulo bias negligible
283
+ const bytes = new Uint8Array(Math.ceil(BigintDecimal.bitLength(limit) / 8) + 8);
284
+ if (typeof globalThis.crypto?.getRandomValues === "function") {
285
+ globalThis.crypto.getRandomValues(bytes);
286
+ }
287
+ else {
288
+ for (let i = 0; i < bytes.length; i++)
289
+ bytes[i] = Math.floor(Math.random() * 256);
290
+ }
291
+ let r = 0n;
292
+ for (const byte of bytes)
293
+ r = (r << 8n) | BigInt(byte);
294
+ return BigintDecimal.fromParts(r % limit, p);
295
+ }
296
+ // ---------------------------------------------------------------- cache
297
+ /**
298
+ * Frees the cached constants (pi, ln2, ln10, the log table) and powers of ten.
299
+ * They are kept at the highest precision used so far and rebuilt on demand, so this is only
300
+ * useful to give memory back after very high precision work. Results do not change.
301
+ */
302
+ static clearCache() {
303
+ BigintDecimal.constants.clear();
304
+ BigintDecimal.pow10Cache.length = 0;
305
+ }
306
+ // =========================================================================
307
+ // Private helpers.
308
+ // The algorithms work on binary fixed-point bigints: an integer meaning `value / 2^w`.
309
+ // A binary scale turns every rescaling into a cheap bit shift instead of a big division.
310
+ // `compute` runs them with GUARD_BITS extra bits, then rounds to `BigintDecimal.precision` decimals.
311
+ // Helpers are called through the class name, not `this`, so detached calls
312
+ // like `values.map(BigintDecimal.sqrt)` keep working, as they do with Math.
313
+ // =========================================================================
314
+ // Extra working bits (~12 decimal digits) that absorb rounding errors
315
+ static GUARD_BITS = 40;
316
+ // Measured crossovers (working scale in bits) where the asymptotically faster algorithm starts to win:
317
+ // atan by Newton's method instead of its series (~1500 digits)
318
+ static ATAN_NEWTON_BITS = 5000;
319
+ // series by rectangular splitting instead of term by term (~410 digits)
320
+ static RECTANGULAR_BITS = 1400;
321
+ // constants by binary splitting instead of small-integer series (~1200 digits)
322
+ static BINARY_SPLIT_BITS = 4000;
323
+ // Cached constants (pi, ln2, ln10 and the ln(1 + j/256) table) at the highest scale computed so far
324
+ static constants = new Map();
325
+ static POW10_CACHE_SIZE = 1024;
326
+ static pow10Cache = [];
327
+ // ---------------------------------------------------------------- conversion
328
+ static compute(fn) {
329
+ const w = Math.ceil(BigintDecimal.precision * Math.log2(10)) + BigintDecimal.GUARD_BITS;
330
+ const p = BigintDecimal.precision;
331
+ // fixed value / 2^w -> decimal digits, rounded half away from zero
332
+ const v = fn(w);
333
+ const rounded = ((BigintDecimal.absBig(v) * BigintDecimal.pow10(p)) + BigintDecimal.one(w - 1)) >> BigInt(w);
334
+ return BigintDecimal.fromParts(v < 0n ? -rounded : rounded, p);
335
+ }
336
+ // v * 10^-d -> BigintDecimal rounded half away from zero to the current precision
337
+ static roundDecimal(v, d) {
338
+ const p = BigintDecimal.precision;
339
+ if (d <= p)
340
+ return BigintDecimal.fromParts(v, d);
341
+ const unit = BigintDecimal.pow10(d - p);
342
+ const half = unit / 2n;
343
+ return BigintDecimal.fromParts(v >= 0n ? (v + half) / unit : (v - half) / unit, p);
344
+ }
345
+ static parts(x) {
346
+ const n = x instanceof BigintDecimal ? x : new BigintDecimal(x);
347
+ return { v: n.value, d: n.decimals };
348
+ }
349
+ // v * 10^-d -> BigintDecimal, without going through a string
350
+ static fromParts(v, d) {
351
+ const result = new BigintDecimal(v);
352
+ result.decimals = d;
353
+ return result.trim();
354
+ }
355
+ // v * 10^-d -> floor(v * 10^(to - d)) for v >= 0, i.e. the value as an integer count of 10^-to
356
+ static floorScale(v, d, to) {
357
+ return to >= d ? v * BigintDecimal.pow10(to - d) : v / BigintDecimal.pow10(d - to);
358
+ }
359
+ // Decimal parts -> binary fixed point at scale w (truncated toward zero)
360
+ static toFixed(x, w) {
361
+ const shifted = x.v << BigInt(w);
362
+ return x.d === 0 ? shifted : shifted / BigintDecimal.pow10(x.d);
363
+ }
364
+ // Integer part, truncated toward zero
365
+ static integerPart(x) {
366
+ return x.v / BigintDecimal.pow10(x.d);
367
+ }
368
+ // ---------------------------------------------------------------- bigint basics
369
+ // 1 at binary scale w, i.e. 2^w
370
+ static one(w) {
371
+ return 1n << BigInt(w);
372
+ }
373
+ // Moves a fixed value from binary scale `from` to scale `to`
374
+ static shift(v, from, to) {
375
+ return to >= from ? v << BigInt(to - from) : v >> BigInt(from - to);
376
+ }
377
+ // 10^n, cached for the small exponents every call uses (precision, decimals)
378
+ static pow10(n) {
379
+ if (n >= BigintDecimal.POW10_CACHE_SIZE)
380
+ return 10n ** BigInt(n);
381
+ let cached = BigintDecimal.pow10Cache[n];
382
+ if (cached === undefined) {
383
+ cached = 10n ** BigInt(n);
384
+ BigintDecimal.pow10Cache[n] = cached;
385
+ }
386
+ return cached;
387
+ }
388
+ static absBig(n) {
389
+ return n < 0n ? -n : n;
390
+ }
391
+ static bitLength(n) {
392
+ if (n === 0n)
393
+ return 0;
394
+ const hex = BigintDecimal.absBig(n).toString(16);
395
+ // 4 bits per hex digit, minus the unused high bits of the first digit
396
+ return hex.length * 4 - (Math.clz32(parseInt(hex.charAt(0), 16)) - 28);
397
+ }
398
+ static floorDiv(a, b) {
399
+ const q = a / b;
400
+ return a % b !== 0n && (a < 0n) !== (b < 0n) ? q - 1n : q;
401
+ }
402
+ // floor(sqrt(n)), Newton's method.
403
+ // Starts from a float estimate of the top bits (already ~50 bits correct, and never below the root),
404
+ // so only one or two Newton steps are needed.
405
+ static isqrt(n) {
406
+ if (n < 2n)
407
+ return n;
408
+ const s = Math.max(0, BigintDecimal.bitLength(n) - 100) & ~1; // even, so the root shifts by s / 2
409
+ let x = (BigInt(Math.ceil(Math.sqrt(Number(n >> BigInt(s))))) + 1n) << BigInt(s >> 1);
410
+ while (true) {
411
+ const y = (x + n / x) >> 1n;
412
+ if (y >= x)
413
+ return x;
414
+ x = y;
415
+ }
416
+ }
417
+ // floor(cbrt(n)), Newton's method, seeded like isqrt
418
+ static icbrt(n) {
419
+ if (n < 2n)
420
+ return n;
421
+ const s = Math.floor(Math.max(0, BigintDecimal.bitLength(n) - 150) / 3) * 3; // multiple of 3
422
+ let x = (BigInt(Math.ceil(Math.cbrt(Number(n >> BigInt(s))))) + 1n) << BigInt(s / 3);
423
+ while (true) {
424
+ const y = (2n * x + n / (x * x)) / 3n;
425
+ if (y >= x)
426
+ return x;
427
+ x = y;
428
+ }
429
+ }
430
+ // ---------------------------------------------------------------- shared by public functions
431
+ static pick(values, name, isBetter) {
432
+ const [first, ...rest] = values;
433
+ if (first === undefined)
434
+ throw new RangeError(`${name}: needs at least one argument`);
435
+ let best = new BigintDecimal(first);
436
+ for (const value of rest) {
437
+ const next = new BigintDecimal(value);
438
+ if (isBetter(best, next))
439
+ best = next;
440
+ }
441
+ return best;
442
+ }
443
+ // ln(x), or ln(x) / ln(base) when `lnBase` is given
444
+ static logBase(x, name, lnBase) {
445
+ const p = BigintDecimal.parts(x);
446
+ if (p.v <= 0n)
447
+ throw new RangeError(`${name}: argument is out of range`);
448
+ return BigintDecimal.compute((w) => {
449
+ const ln = BigintDecimal.lnFixed(p.v, 0, p.d, w);
450
+ return lnBase ? (ln << BigInt(w)) / lnBase(w) : ln;
451
+ });
452
+ }
453
+ // ±ln(x + sqrt(x^2 + c)), x >= 0: asinh (c = 1) and acosh (c = -1)
454
+ static lnPlusRoot(x, c, negate = false) {
455
+ const xp = BigintDecimal.parts(x);
456
+ const square = BigintDecimal.parts(x.times(x).plus(c));
457
+ return BigintDecimal.compute((w) => {
458
+ const sum = BigintDecimal.toFixed(xp, w) + BigintDecimal.isqrt(BigintDecimal.toFixed(square, 2 * w));
459
+ const r = BigintDecimal.lnFixed(sum, w, 0, w);
460
+ return negate ? -r : r;
461
+ });
462
+ }
463
+ // Combines e^x and e^-x: sinh and cosh
464
+ static expPair(x, combine) {
465
+ const p = BigintDecimal.parts(x);
466
+ return BigintDecimal.compute((w) => combine(BigintDecimal.expParts(p, w), BigintDecimal.expParts({ v: -p.v, d: p.d }, w)));
467
+ }
468
+ // ---------------------------------------------------------------- fixed-point algorithms
469
+ static constant(name, w, compute) {
470
+ let cached = BigintDecimal.constants.get(name);
471
+ if (!cached || w > cached.scale) {
472
+ const scale = w + BigintDecimal.GUARD_BITS;
473
+ cached = { scale, value: compute(scale) };
474
+ BigintDecimal.constants.set(name, cached);
475
+ }
476
+ return cached.value >> BigInt(cached.scale - w);
477
+ }
478
+ // pi at scale w. Low precision: Machin's formula, pi = 16 atan(1/5) - 4 atan(1/239).
479
+ // High precision: the Chudnovsky series (~47 bits per term) by binary splitting:
480
+ // pi = 426880 sqrt(10005) / Σ (-1)^k (6k)! (13591409 + 545140134 k) / ((3k)! (k!)^3 640320^(3k))
481
+ static piFixed(w) {
482
+ return BigintDecimal.constant("pi", w, (s) => {
483
+ if (s < BigintDecimal.BINARY_SPLIT_BITS) {
484
+ return 4n * (4n * BigintDecimal.atanRatio(1n, 5n, s) - BigintDecimal.atanRatio(1n, 239n, s));
71
485
  }
72
- throw new Error(`Unexpected character: ${String.fromCharCode(code)}`);
73
- }
74
- // Handle negative decimals (large integers)
75
- if (decimals < 0) {
76
- value = value * (10n ** BigInt(-decimals));
77
- decimals = 0;
78
- }
79
- ;
80
- return { value: value * sign, decimals };
81
- }
82
- toString() {
83
- // 1. Handle Integer case (Fastest exit, Zero Allocation overhead)
84
- if (this.decimals === 0)
85
- return this.value.toString();
86
- // 2. Separate Sign and Magnitude
87
- // Check sign using BigInt math (fastest)
88
- const isNegative = this.value < 0n;
89
- // Get the string of digits ONLY.
90
- // isNegative ? -this.value : this.value -> creates a temporary BigInt
91
- // .toString() -> creates the string of digits "123"
92
- // This is cheaper than creating "-123" and slicing it.
93
- const s = (isNegative ? -this.value : this.value).toString();
94
- const len = s.length;
95
- // 3. Logic for insertion vs padding
96
- if (len > this.decimals) {
97
- // Case A: 12345 (2 dec) -> 123.45
98
- // splitIndex is purely digits before the dot
99
- const splitIndex = len - this.decimals;
100
- // Build the absolute decimal string
101
- const result = s.substring(0, splitIndex) + "." + s.substring(splitIndex);
102
- // Add sign if needed
103
- return isNegative ? "-" + result : result;
104
- }
105
- // Case B: 5 (2 dec) -> 0.05
106
- // Calculate zeros needed: 2 decimals - length 1 = 1 zero
107
- const zerosNeeded = this.decimals - len;
108
- // Build the result directly
109
- // "0." + "0" + "5"
110
- return (isNegative ? "-0." : "0.") + "0".repeat(zerosNeeded) + s;
111
- }
112
- toJSON() {
113
- return this.toString();
114
- }
115
- [Symbol.for('nodejs.util.inspect.custom')]() {
116
- return this.toString();
117
- }
118
- isBigintDecimal(val) {
119
- const s = this.toSafeValue(val);
120
- if (s.length === 0)
121
- return false;
122
- let dot = false, e = false;
123
- for (let i = 0; i < s.length; i++) {
124
- const c = s.charCodeAt(i);
125
- if (c >= 48 && c <= 57)
126
- continue; // 0-9
127
- if (c === 46 && !dot && !e) {
128
- dot = true;
129
- continue;
130
- } // .
131
- if (i === 0 && (c === 45 || c === 43))
132
- continue; // +/- at start
133
- if ((c === 101 || c === 69) && !e) { // e/E
134
- e = true;
135
- const next = s.charCodeAt(i + 1);
136
- if (next === 45 || next === 43)
137
- i++; // Skip sign after 'e'
138
- continue;
486
+ const C3 = 10939058860032000n; // 640320^3 / 24
487
+ const [, Q, T] = BigintDecimal.binarySplit(0, Math.ceil(s / 47.11) + 2, (k) => 13591409n + 545140134n * k, (k) => -(6n * k - 5n) * (2n * k - 1n) * (6n * k - 1n), (k) => k * k * k * C3);
488
+ return (426880n * BigintDecimal.isqrt(10005n << BigInt(2 * s)) * Q) / T;
489
+ });
490
+ }
491
+ // ln(2) = 2 atanh(1/3)
492
+ static ln2Fixed(w) {
493
+ return BigintDecimal.constant("ln2", w, (s) => 2n * BigintDecimal.atanhRatio(1n, 3n, s));
494
+ }
495
+ // ln(10) = 3 ln(2) + ln(1.25), ln(1.25) = 2 atanh(1/9)
496
+ static ln10Fixed(w) {
497
+ return BigintDecimal.constant("ln10", w, (s) => 3n * BigintDecimal.ln2Fixed(s) + 2n * BigintDecimal.atanhRatio(1n, 9n, s));
498
+ }
499
+ // atanh(j/c) at scale w, for small integers 0 < j < c.
500
+ // High precision: Euler's form atanh(y) = y / (1 - y^2) Σ (2k)!! / (2k + 1)!! (-y^2 / (1 - y^2))^k
501
+ // has small-integer term ratios, so it can be summed by binary splitting.
502
+ static atanhRatio(j, c, w) {
503
+ if (w < BigintDecimal.BINARY_SPLIT_BITS)
504
+ return BigintDecimal.atanRatio(j, c, w, false);
505
+ const j2 = j * j, d = c * c - j2;
506
+ const terms = Math.ceil(w / Math.log2(Number(d) / Number(j2))) + 2;
507
+ const [, Q, T] = BigintDecimal.binarySplit(0, terms, () => 1n, (k) => -2n * k * j2, (k) => (2n * k + 1n) * d);
508
+ return (((j * c) << BigInt(w)) * T) / (d * Q);
509
+ }
510
+ // Σ_{k=lo}^{hi-1} a(k) Π_{t=lo+1..k} p(t)/q(t) as [P, Q, T] with the sum = T / Q (binary splitting).
511
+ // Halving the range recursively turns the work into a few large, balanced multiplications,
512
+ // where V8's fast multiplication algorithms do best.
513
+ static binarySplit(lo, hi, a, p, q) {
514
+ if (hi - lo === 1) {
515
+ const k = BigInt(lo);
516
+ const P = lo === 0 ? 1n : p(k), Q = lo === 0 ? 1n : q(k);
517
+ return [P, Q, a(k) * P];
518
+ }
519
+ const mid = (lo + hi) >> 1;
520
+ const [P1, Q1, T1] = BigintDecimal.binarySplit(lo, mid, a, p, q);
521
+ const [P2, Q2, T2] = BigintDecimal.binarySplit(mid, hi, a, p, q);
522
+ return [P1 * P2, Q1 * Q2, T1 * Q2 + P1 * T2];
523
+ }
524
+ // Σ_{k>=0} x^k Π_{t=1..k} p(t)/q(t) at scale w, for x fixed at scale w (|x| < 1, |p(t)| <= q(t)),
525
+ // by rectangular splitting (Paterson–Stockmeyer): with m ≈ sqrt(N) powers x^1..x^m, each block of
526
+ // m terms costs one full multiplication, and everything else multiplies or divides by small integers.
527
+ // Used from RECTANGULAR_BITS up; below that the callers' direct term-by-term loops are faster.
528
+ static series(x, w, p, q) {
529
+ const W = BigInt(w), one = 1n << W;
530
+ if (x === 0n)
531
+ return one;
532
+ // Terms needed: until |x|^N Π p/q < 2^-(w + 8)
533
+ const log2x = BigintDecimal.bitLength(x) - w;
534
+ let terms = 0;
535
+ for (let size = 0; size > -(w + 8);) {
536
+ terms++;
537
+ const t = BigInt(terms);
538
+ size += log2x + Math.log2(Math.abs(Number(p(t))) / Number(q(t)));
539
+ }
540
+ const m = Math.ceil(Math.sqrt(terms));
541
+ // x^(m-1) .. x^0, highest first, and x^m
542
+ const descendingPowers = [one];
543
+ let xm = one;
544
+ for (let i = 1; i <= m; i++) {
545
+ xm = (xm * x) >> W;
546
+ if (i < m)
547
+ descendingPowers.unshift(xm);
548
+ }
549
+ // Blocks from the last one down, Horner-style in x^m. For a block starting at term b:
550
+ // sum_b = (Σ_i x^i num_i den_i + x^m sum_{b+m} num_m / q(b+m)) / den_0
551
+ // with num_i = Π_{t=b+1..b+i} p(t) and den_i = Π_{t=b+i+1..b+m-1} q(t), both built while i goes down.
552
+ let sum = 0n;
553
+ for (let b = (Math.ceil(terms / m) - 1) * m; b >= 0; b -= m) {
554
+ const base = BigInt(b);
555
+ let num = 1n;
556
+ for (let t = 1; t <= m; t++)
557
+ num *= p(base + BigInt(t));
558
+ let block = sum === 0n ? 0n : (((xm * sum) >> W) * num) / q(base + BigInt(m));
559
+ let den = 1n, i = m;
560
+ for (const power of descendingPowers) {
561
+ i--;
562
+ num /= p(base + BigInt(i + 1)); // exact: num was a product including this factor
563
+ if (i < m - 1)
564
+ den *= q(base + BigInt(i + 1));
565
+ block += power * num * den;
139
566
  }
140
- return false;
141
- }
142
- return true;
143
- }
144
- plus(val) {
145
- const plusValue = new BigintDecimal(val);
146
- // 1. Determine which number has more decimals
147
- const d1 = this.decimals;
148
- const d2 = plusValue.decimals;
149
- const newDecimal = d1 > d2 ? d1 : d2;
150
- // 2. Align values (Common Denominator)
151
- // If 'this' has fewer decimals, scale it up.
152
- const v1 = d1 < newDecimal ? this.value * (10n ** BigInt(newDecimal - d1)) : this.value;
153
- // If 'plusValue' has fewer decimals, scale it up.
154
- const v2 = d2 < newDecimal ? plusValue.value * (10n ** BigInt(newDecimal - d2)) : plusValue.value;
155
- // 3. Now add and return
156
- const result = new BigintDecimal("0");
157
- result.value = v1 + v2;
158
- result.decimals = newDecimal;
159
- return result.trim();
567
+ sum = block / den;
568
+ }
569
+ return sum;
160
570
  }
161
- minus(val) {
162
- const other = new BigintDecimal(val);
163
- const d1 = this.decimals;
164
- const d2 = other.decimals;
165
- const newDecimal = d1 > d2 ? d1 : d2;
166
- // Scale values to the same precision
167
- const v1 = d1 < newDecimal ? this.value * (10n ** BigInt(newDecimal - d1)) : this.value;
168
- const v2 = d2 < newDecimal ? other.value * (10n ** BigInt(newDecimal - d2)) : other.value;
169
- const result = new BigintDecimal("0"); // Fastest instantiation
170
- result.value = v1 - v2;
171
- result.decimals = newDecimal;
172
- return result.trim();
571
+ // Halvings of the argument before a Taylor series (each costs one squaring or double-angle step afterwards).
572
+ // Term-by-term summation favours more halvings (~sqrt(w)/2); rectangular splitting fewer (~1.5 cbrt(w)).
573
+ static halvings(w) {
574
+ return w < BigintDecimal.RECTANGULAR_BITS ? Math.ceil(Math.sqrt(w) / 2) : Math.round(1.5 * Math.cbrt(w));
173
575
  }
174
- times(val) {
175
- const other = new BigintDecimal(val);
176
- const result = new BigintDecimal("0");
177
- // Multiplication: (A * 10^d1) * (B * 10^d2) = (A * B) * 10^(d1 + d2)
178
- result.value = this.value * other.value;
179
- result.decimals = this.decimals + other.decimals;
180
- return result.trim();
576
+ // atan(p/q), or atanh(p/q) when `alternating` is false, at scale w, for small integers p < q.
577
+ // Every step multiplies and divides by small integers, which is much cheaper than a full multiplication.
578
+ static atanRatio(p, q, w, alternating = true) {
579
+ const p2 = p * p, q2 = q * q;
580
+ let term = (p << BigInt(w)) / q, sum = term, negative = alternating;
581
+ for (let k = 3n; term !== 0n; k += 2n) {
582
+ term = (term * p2) / q2;
583
+ sum = negative ? sum - term / k : sum + term / k;
584
+ negative = alternating && !negative;
585
+ }
586
+ return sum;
181
587
  }
182
- round(toDecimals) {
183
- const result = new BigintDecimal("0");
184
- const diff = this.decimals - toDecimals;
185
- if (diff <= 0)
186
- return this; // Already more precise than requested
187
- const power = 10n ** BigInt(diff);
188
- const half = power / 2n;
189
- // Standard rounding: add half of the divisor before dividing
190
- // This turns 0.999... into 1.0
191
- const roundedValue = (this.value >= 0n) ? (this.value + half) / power : (this.value - half) / power;
192
- result.value = roundedValue;
193
- result.decimals = toDecimals;
194
- return result.trim();
588
+ // ln(m) for m (fixed at scale w) in [1, 2], using ln(m) = 2 atanh(y), y = (m - 1) / (m + 1)
589
+ static lnNear1(m, w) {
590
+ const W = BigInt(w), one = BigintDecimal.one(w);
591
+ const y = ((m - one) << W) / (m + one);
592
+ const y2 = (y * y) >> W;
593
+ if (w >= BigintDecimal.RECTANGULAR_BITS) {
594
+ // Euler's form: atanh(y) = y / (1 - y^2) Σ (2k)!! / (2k + 1)!! (-y^2 / (1 - y^2))^k
595
+ const oneMinusY2 = one - y2;
596
+ const sum = BigintDecimal.series(-(y2 << W) / oneMinusY2, w, (t) => 2n * t, (t) => 2n * t + 1n);
597
+ return 2n * ((((y << W) / oneMinusY2) * sum) >> W);
598
+ }
599
+ let term = y, sum = y;
600
+ for (let k = 3n; term !== 0n; k += 2n) {
601
+ term = (term * y2) >> W;
602
+ sum += term / k;
603
+ }
604
+ return 2n * sum;
195
605
  }
196
- div(val) {
197
- const other = val instanceof BigintDecimal ? val : new BigintDecimal(val);
198
- if (other.value === 0n)
199
- throw new Error("Division by zero");
200
- let v1 = this.value;
201
- const v2 = other.value;
202
- // 1. Scale up once to your "Max Allowed" precision
203
- const shift = this.precision + other.decimals - this.decimals;
204
- if (shift > 0)
205
- v1 *= 10n ** BigInt(shift);
206
- else if (shift < 0)
207
- v1 /= 10n ** BigInt(-shift);
208
- const result = new BigintDecimal("0");
209
- result.value = v1 / v2;
210
- result.decimals = this.precision;
211
- // 2. Immediately trim it!
212
- // This is where 0.500000... becomes 0.5 automatically.
213
- return result.trim();
606
+ // ln(v * 2^-binaryExp * 10^-decimalExp) at scale w, for v > 0.
607
+ // Uses v = m * 2^k with m in [1, 2), so ln = ln(m) + (k - binaryExp) ln2 - decimalExp ln10.
608
+ // m is then split as (1 + j/256) * m' using its top 8 bits j. ln(1 + j/256) = 2 atanh(j / (512 + j)) comes
609
+ // from a table, computed once per j and precision, and m' < 1 + 2^-8 makes the remaining series short.
610
+ static lnFixed(v, binaryExp, decimalExp, w) {
611
+ const k = BigintDecimal.bitLength(v) - 1;
612
+ const powerOf2 = k - binaryExp;
613
+ // the constants are multiplied by these exponents, so they need their bits as extra precision
614
+ const ww = w + BigintDecimal.bitLength(BigInt(Math.abs(powerOf2) + decimalExp)) + 8;
615
+ const m = BigintDecimal.shift(v, k, ww);
616
+ const j = (m >> BigInt(ww - 8)) - 256n;
617
+ let r = BigInt(powerOf2) * BigintDecimal.ln2Fixed(ww) - BigInt(decimalExp) * BigintDecimal.ln10Fixed(ww);
618
+ if (j === 0n) {
619
+ r += BigintDecimal.lnNear1(m, ww);
620
+ }
621
+ else {
622
+ r += BigintDecimal.lnNear1((m << 8n) / (256n + j), ww)
623
+ + BigintDecimal.constant(`ln(1+${j}/256)`, ww, (s) => 2n * BigintDecimal.atanhRatio(j, 512n + j, s));
624
+ }
625
+ return r >> BigInt(ww - w);
214
626
  }
215
- compare(val) {
216
- const other = val instanceof BigintDecimal ? val : new BigintDecimal(val);
217
- const maxD = Math.max(this.decimals, other.decimals);
218
- const v1 = this.decimals < maxD ? this.value * (10n ** BigInt(maxD - this.decimals)) : this.value;
219
- const v2 = other.decimals < maxD ? other.value * (10n ** BigInt(maxD - other.decimals)) : other.value;
220
- return v1 > v2 ? 1 : v1 < v2 ? -1 : 0;
627
+ // e^x for exact decimal parts, at scale w
628
+ static expParts(x, w) {
629
+ // The result has about integerPart * log2(e) integer bits; x needs that much extra precision.
630
+ const integerPart = BigintDecimal.integerPart(x);
631
+ const magnitude = Math.max(0, Math.ceil(Number(integerPart) * Math.LOG2E));
632
+ if (!Number.isFinite(magnitude))
633
+ throw new RangeError("exp: result is too large to represent");
634
+ const xw = w + magnitude + BigintDecimal.GUARD_BITS + BigintDecimal.bitLength(integerPart);
635
+ return BigintDecimal.expFixed(BigintDecimal.toFixed(x, xw), xw, w);
221
636
  }
222
- gt(val) {
223
- return this.compare(val) === 1;
637
+ // e^(x / 2^xw) at scale w.
638
+ // e^x = 2^n * e^r with r = x - n * ln2, |r| <= 0.35. Multiplying by 2^n is a shift.
639
+ // e^r: the argument is halved k times, summed as a Taylor series, then squared k times.
640
+ static expFixed(x, xw, w) {
641
+ // n only has to be close to x / ln2, so a low-precision estimate is enough
642
+ const roughScale = BigintDecimal.bitLength(x >> BigInt(xw)) + 64;
643
+ const roughX = BigintDecimal.shift(x, xw, roughScale);
644
+ const roughLn2 = BigintDecimal.ln2Fixed(roughScale);
645
+ const n = BigintDecimal.floorDiv(2n * roughX + roughLn2, 2n * roughLn2);
646
+ // e^x < 2^-(w + 1): rounds to zero
647
+ if (n < -BigInt(w + 2))
648
+ return 0n;
649
+ const integerBits = Math.max(0, Number(n));
650
+ const k = BigintDecimal.halvings(w + integerBits);
651
+ // squaring k times doubles the error k times; the result's integer bits need precision too
652
+ const ww = w + integerBits + k + BigintDecimal.GUARD_BITS;
653
+ const nBits = BigintDecimal.bitLength(n);
654
+ const r = (BigintDecimal.shift(x, xw, ww + nBits) - n * BigintDecimal.ln2Fixed(ww + nBits)) >> BigInt(nBits + k);
655
+ // e^r = Σ r^t / t!
656
+ const WW = BigInt(ww);
657
+ let sum;
658
+ if (ww >= BigintDecimal.RECTANGULAR_BITS) {
659
+ sum = BigintDecimal.series(r, ww, () => 1n, (t) => t);
660
+ }
661
+ else {
662
+ let term = BigintDecimal.one(ww);
663
+ sum = term;
664
+ for (let t = 1n; term !== 0n; t++) {
665
+ term = ((term * r) >> WW) / t;
666
+ sum += term;
667
+ }
668
+ }
669
+ for (let i = 0; i < k; i++)
670
+ sum = (sum * sum) >> WW;
671
+ return sum >> BigInt(ww - w - Number(n));
672
+ }
673
+ // [sin(x), cos(x)] for exact decimal parts, at scale w
674
+ static sinCosParts(x, w) {
675
+ // Large arguments need pi with as many extra bits as the integer part has.
676
+ const base = w + BigintDecimal.GUARD_BITS + BigintDecimal.bitLength(BigintDecimal.integerPart(x));
677
+ const ww = base + BigintDecimal.sinCosLoss(base);
678
+ const [s, c] = BigintDecimal.sinCosFixed(BigintDecimal.toFixed(x, ww), ww);
679
+ return [s >> BigInt(ww - w), c >> BigInt(ww - w)];
680
+ }
681
+ // Bits of precision sinCosFixed loses at scale w (the argument halvings), which callers add as guard bits
682
+ static sinCosLoss(w) {
683
+ return BigintDecimal.halvings(w) + 4;
224
684
  }
225
- lt(val) {
226
- return this.compare(val) === -1;
685
+ // [sin(x), cos(x)] for x fixed at scale w.
686
+ // x = q * (pi / 2) + r with |r| <= pi / 4; r is halved k times, both Taylor series are summed,
687
+ // then the double-angle formulas are applied k times.
688
+ static sinCosFixed(x, w) {
689
+ const halfPi = BigintDecimal.piFixed(w) >> 1n;
690
+ const q = BigintDecimal.floorDiv(2n * x + halfPi, 2n * halfPi);
691
+ const k = BigintDecimal.halvings(w);
692
+ const r = (x - q * halfPi) >> BigInt(k);
693
+ const W = BigInt(w);
694
+ const minusR2 = -((r * r) >> W);
695
+ // sin r = r Σ (-r^2)^t / (2t + 1)!, cos r = Σ (-r^2)^t / (2t)!
696
+ let sin, cos;
697
+ if (w >= BigintDecimal.RECTANGULAR_BITS) {
698
+ sin = (r * BigintDecimal.series(minusR2, w, () => 1n, (t) => 2n * t * (2n * t + 1n))) >> W;
699
+ cos = BigintDecimal.series(minusR2, w, () => 1n, (t) => (2n * t - 1n) * 2n * t);
700
+ }
701
+ else {
702
+ let term = r;
703
+ sin = r;
704
+ for (let n = 2n; term !== 0n; n += 2n) {
705
+ term = ((term * minusR2) >> W) / (n * (n + 1n));
706
+ sin += term;
707
+ }
708
+ term = BigintDecimal.one(w);
709
+ cos = term;
710
+ for (let n = 1n; term !== 0n; n += 2n) {
711
+ term = ((term * minusR2) >> W) / (n * (n + 1n));
712
+ cos += term;
713
+ }
714
+ }
715
+ // sin(2a) = 2 sin(a) cos(a), cos(2a) = cos(a)^2 - sin(a)^2
716
+ for (let i = 0; i < k; i++) {
717
+ [sin, cos] = [(2n * sin * cos) >> W, (cos * cos - sin * sin) >> W];
718
+ }
719
+ const quadrant = Number(((q % 4n) + 4n) % 4n);
720
+ return quadrant === 0 ? [sin, cos] :
721
+ quadrant === 1 ? [cos, -sin] :
722
+ quadrant === 2 ? [-sin, -cos] :
723
+ [-cos, sin];
227
724
  }
228
- gte(val) {
229
- return this.compare(val) >= 0;
725
+ // atan(x) for x fixed at scale w, result at scale w
726
+ static atanFixed(x, w) {
727
+ if (x < 0n)
728
+ return -BigintDecimal.atanFixed(-x, w);
729
+ const one = BigintDecimal.one(w);
730
+ if (x > one)
731
+ return (BigintDecimal.piFixed(w) >> 1n) - BigintDecimal.atanFixed(BigintDecimal.one(2 * w) / x, w);
732
+ if (w > BigintDecimal.ATAN_NEWTON_BITS)
733
+ return BigintDecimal.atanNewton(x, w);
734
+ // atan(x) = 2 * atan(x / (1 + sqrt(1 + x^2))), until x < 1/8
735
+ let halvings = 0n;
736
+ while (x << 3n > one) {
737
+ x = (x << BigInt(w)) / (one + BigintDecimal.isqrt(BigintDecimal.one(2 * w) + x * x));
738
+ halvings++;
739
+ }
740
+ const W = BigInt(w);
741
+ const x2 = (x * x) >> W;
742
+ if (w >= BigintDecimal.RECTANGULAR_BITS) {
743
+ // Euler's form: atan(x) = x / (1 + x^2) Σ (2k)!! / (2k + 1)!! (x^2 / (1 + x^2))^k
744
+ const sum = BigintDecimal.series((x2 << W) / (one + x2), w, (t) => 2n * t, (t) => 2n * t + 1n);
745
+ return ((((x << W) / (one + x2)) * sum) >> W) << halvings;
746
+ }
747
+ let sum = x, term = x;
748
+ for (let k = 3n; term !== 0n; k += 2n) {
749
+ term = -((term * x2) >> W);
750
+ sum += term / k;
751
+ }
752
+ return sum << halvings;
230
753
  }
231
- lte(val) {
232
- return this.compare(val) <= 0;
754
+ // atan(x) for x in [0, 1] fixed at scale w, by Newton's method on tan(y) = x:
755
+ // y += (x cos y - sin y) / (cos y + x sin y). Each step doubles the correct bits,
756
+ // so it starts from Math.atan and works at twice the precision every step.
757
+ static atanNewton(x, w) {
758
+ let scale = 53;
759
+ let y = BigInt(Math.round(Math.atan(Number(BigintDecimal.shift(x, w, 53)) / 2 ** 53) * 2 ** 53));
760
+ let correctBits = 50;
761
+ const target = w + 8;
762
+ while (correctBits < target) {
763
+ correctBits = Math.min(2 * correctBits, target);
764
+ const s = correctBits + BigintDecimal.sinCosLoss(correctBits) + 16;
765
+ y = BigintDecimal.shift(y, scale, s);
766
+ scale = s;
767
+ const xs = BigintDecimal.shift(x, w, s);
768
+ const [sin, cos] = BigintDecimal.sinCosFixed(y, s);
769
+ const numerator = ((xs * cos) >> BigInt(s)) - sin;
770
+ const denominator = cos + ((xs * sin) >> BigInt(s));
771
+ y += (numerator << BigInt(s)) / denominator;
772
+ }
773
+ return BigintDecimal.shift(y, scale, w);
233
774
  }
234
- eq(val) {
235
- return this.compare(val) === 0;
775
+ // asin(x) at scale w, |x| <= 1
776
+ static asinFixed(x, w) {
777
+ // 1 - x^2 computed exactly from the decimal parts: near x = ±1 it is tiny, and rounding x first
778
+ // would lose it. asin(x) = atan(x / sqrt(1 - x^2)).
779
+ const oneMinusSquare = BigintDecimal.pow10(2 * x.d) - x.v * x.v;
780
+ if (oneMinusSquare < 0n)
781
+ throw new RangeError("asin/acos: argument must be between -1 and 1 (NaN)");
782
+ const c = BigintDecimal.isqrt(BigintDecimal.toFixed({ v: oneMinusSquare, d: 2 * x.d }, 2 * w));
783
+ if (c === 0n)
784
+ return x.v > 0n ? BigintDecimal.piFixed(w) >> 1n : -(BigintDecimal.piFixed(w) >> 1n);
785
+ return BigintDecimal.atanFixed((BigintDecimal.toFixed(x, w) << BigInt(w)) / c, w);
236
786
  }
237
787
  }
238
- ;
239
788
  export { BigintDecimal as default };
240
789
  //# sourceMappingURL=index.js.map