arbitrary-numbers 1.1.0 → 2.0.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.
- package/README.md +455 -513
- package/dist/index.cjs +803 -875
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +328 -629
- package/dist/index.d.ts +328 -629
- package/dist/index.js +802 -872
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -43,13 +43,17 @@ var POW10 = [
|
|
|
43
43
|
1e12,
|
|
44
44
|
1e13,
|
|
45
45
|
1e14,
|
|
46
|
-
1e15
|
|
46
|
+
1e15,
|
|
47
|
+
1e16,
|
|
48
|
+
1e17,
|
|
49
|
+
1e18,
|
|
50
|
+
1e19,
|
|
51
|
+
1e20,
|
|
52
|
+
1e21,
|
|
53
|
+
1e22
|
|
47
54
|
];
|
|
48
55
|
function pow10(n) {
|
|
49
|
-
return n >= 0 && n <
|
|
50
|
-
}
|
|
51
|
-
function pow10Int(n) {
|
|
52
|
-
return n >= 0 && n < 16 ? POW10[n] : Math.pow(10, n);
|
|
56
|
+
return n >= 0 && n < 23 ? POW10[n] : Math.pow(10, n);
|
|
53
57
|
}
|
|
54
58
|
|
|
55
59
|
// src/errors.ts
|
|
@@ -73,42 +77,27 @@ var ArbitraryNumberDomainError = class extends ArbitraryNumberError {
|
|
|
73
77
|
this.context = context;
|
|
74
78
|
}
|
|
75
79
|
};
|
|
80
|
+
var ArbitraryNumberMutationError = class extends ArbitraryNumberDomainError {
|
|
81
|
+
constructor(message) {
|
|
82
|
+
super(message, {});
|
|
83
|
+
this.name = "ArbitraryNumberMutationError";
|
|
84
|
+
}
|
|
85
|
+
};
|
|
76
86
|
|
|
77
87
|
// src/core/ArbitraryNumber.ts
|
|
88
|
+
var _scaleCutoff = 15;
|
|
78
89
|
var _ArbitraryNumber = class _ArbitraryNumber {
|
|
79
|
-
/**
|
|
80
|
-
* Creates an `ArbitraryNumber` from a plain JavaScript `number`.
|
|
81
|
-
*
|
|
82
|
-
* Prefer this over `new ArbitraryNumber(value, 0)` when working with
|
|
83
|
-
* ordinary numeric literals - it reads clearly at the call site and
|
|
84
|
-
* validates the input.
|
|
85
|
-
*
|
|
86
|
-
* @example
|
|
87
|
-
* ArbitraryNumber.from(1500); // { coefficient: 1.5, exponent: 3 }
|
|
88
|
-
* ArbitraryNumber.from(0.005); // { coefficient: 5, exponent: -3 }
|
|
89
|
-
* ArbitraryNumber.from(0); // ArbitraryNumber.Zero
|
|
90
|
-
* ArbitraryNumber.from(-0); // ArbitraryNumber.Zero (signed zero is normalised to +0)
|
|
91
|
-
*
|
|
92
|
-
* @param value - Any finite number. Signed zero (`-0`) is treated as `0`.
|
|
93
|
-
* @throws `"ArbitraryNumber.from: value must be finite"` for `NaN`, `Infinity`, or `-Infinity`.
|
|
94
|
-
*/
|
|
95
|
-
static from(value) {
|
|
96
|
-
if (!isFinite(value)) {
|
|
97
|
-
throw new ArbitraryNumberInputError("ArbitraryNumber.from: value must be finite", value);
|
|
98
|
-
}
|
|
99
|
-
return new _ArbitraryNumber(value, 0);
|
|
100
|
-
}
|
|
101
90
|
/**
|
|
102
91
|
* Constructs a new `ArbitraryNumber` and immediately normalises it so that
|
|
103
92
|
* `1 <= |coefficient| < 10` (or `coefficient === 0`).
|
|
104
93
|
*
|
|
94
|
+
* Always two numeric arguments — this keeps the constructor monomorphic so V8
|
|
95
|
+
* locks in a hidden class on first use (~5 ns allocation).
|
|
96
|
+
*
|
|
105
97
|
* @example
|
|
106
98
|
* new ArbitraryNumber(15, 3); // stored as { coefficient: 1.5, exponent: 4 }
|
|
107
99
|
*
|
|
108
|
-
* @
|
|
109
|
-
* @param exponent - The power of 10. Must be a finite number.
|
|
110
|
-
* @throws `"ArbitraryNumber: coefficient must be finite"` for `NaN`, `Infinity`, or `-Infinity`.
|
|
111
|
-
* @throws `"ArbitraryNumber: exponent must be finite"` for non-finite exponents.
|
|
100
|
+
* @throws `ArbitraryNumberInputError` for `NaN`/`Infinity` coefficient or exponent.
|
|
112
101
|
*/
|
|
113
102
|
constructor(coefficient, exponent) {
|
|
114
103
|
if (coefficient === 0) {
|
|
@@ -134,182 +123,329 @@ var _ArbitraryNumber = class _ArbitraryNumber {
|
|
|
134
123
|
this.exponent = exponent + shift;
|
|
135
124
|
}
|
|
136
125
|
/**
|
|
137
|
-
* @internal Fast-path factory for already-normalised values.
|
|
126
|
+
* @internal Fast-path factory for already-normalised values. Not exported from `index.ts`.
|
|
138
127
|
*
|
|
139
|
-
*
|
|
140
|
-
* Only valid when
|
|
141
|
-
*
|
|
128
|
+
* Allocates a new instance and writes the two fields directly — bypasses validation
|
|
129
|
+
* and normalisation. Only valid when `|coefficient|` is already in `[1, 10)` (or 0)
|
|
130
|
+
* and `exponent` is correct.
|
|
142
131
|
*/
|
|
143
|
-
static
|
|
144
|
-
|
|
145
|
-
console.warn(`ArbitraryNumber.createNormalized: zero coefficient with non-zero exponent (${exponent}). Use ArbitraryNumber.Zero instead.`);
|
|
146
|
-
}
|
|
147
|
-
const n = Object.create(_ArbitraryNumber.prototype);
|
|
132
|
+
static unsafe(coefficient, exponent) {
|
|
133
|
+
const n = new _ArbitraryNumber(0, 0);
|
|
148
134
|
n.coefficient = coefficient;
|
|
149
135
|
n.exponent = exponent;
|
|
150
136
|
return n;
|
|
151
137
|
}
|
|
152
|
-
|
|
138
|
+
/** @internal Normalise an arbitrary `(c, e)` pair into a new instance. */
|
|
139
|
+
static _normalizeNew(c, e) {
|
|
153
140
|
if (c === 0) return _ArbitraryNumber.Zero;
|
|
154
141
|
const abs = Math.abs(c);
|
|
155
142
|
const shift = Math.floor(Math.log10(abs));
|
|
156
143
|
const scale = pow10(shift);
|
|
157
144
|
if (scale === 0) return _ArbitraryNumber.Zero;
|
|
158
|
-
return _ArbitraryNumber.
|
|
145
|
+
return _ArbitraryNumber.unsafe(c / scale, e + shift);
|
|
146
|
+
}
|
|
147
|
+
/** @internal Normalise `(c, e)` into `this` (mutates). Returns `this`. */
|
|
148
|
+
_normalizeInto(c, e) {
|
|
149
|
+
if (c === 0) {
|
|
150
|
+
this.coefficient = 0;
|
|
151
|
+
this.exponent = 0;
|
|
152
|
+
return this;
|
|
153
|
+
}
|
|
154
|
+
const abs = c < 0 ? -c : c;
|
|
155
|
+
if (abs < 10) {
|
|
156
|
+
if (abs >= 1) {
|
|
157
|
+
this.coefficient = c;
|
|
158
|
+
this.exponent = e;
|
|
159
|
+
return this;
|
|
160
|
+
}
|
|
161
|
+
this.coefficient = c * 10;
|
|
162
|
+
this.exponent = e - 1;
|
|
163
|
+
return this;
|
|
164
|
+
}
|
|
165
|
+
if (abs < 100) {
|
|
166
|
+
this.coefficient = c / 10;
|
|
167
|
+
this.exponent = e + 1;
|
|
168
|
+
return this;
|
|
169
|
+
}
|
|
170
|
+
if (!isFinite(c)) {
|
|
171
|
+
this.coefficient = 0;
|
|
172
|
+
this.exponent = 0;
|
|
173
|
+
return this;
|
|
174
|
+
}
|
|
175
|
+
const shift = Math.floor(Math.log10(abs));
|
|
176
|
+
const scale = pow10(shift);
|
|
177
|
+
if (scale === 0) {
|
|
178
|
+
this.coefficient = 0;
|
|
179
|
+
this.exponent = 0;
|
|
180
|
+
return this;
|
|
181
|
+
}
|
|
182
|
+
this.coefficient = c / scale;
|
|
183
|
+
this.exponent = e + shift;
|
|
184
|
+
return this;
|
|
185
|
+
}
|
|
186
|
+
// ── Factories ──────────────────────────────────────────────────────────────
|
|
187
|
+
/**
|
|
188
|
+
* Returns a fresh, unfrozen copy of this number. The canonical way to preserve
|
|
189
|
+
* a value before mutating it:
|
|
190
|
+
*
|
|
191
|
+
* ```ts
|
|
192
|
+
* const before = gold.clone();
|
|
193
|
+
* gold.add(drop);
|
|
194
|
+
* ```
|
|
195
|
+
*/
|
|
196
|
+
clone() {
|
|
197
|
+
return _ArbitraryNumber.unsafe(this.coefficient, this.exponent);
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Creates an `ArbitraryNumber` from a plain JavaScript `number`.
|
|
201
|
+
*
|
|
202
|
+
* @throws `ArbitraryNumberInputError` for `NaN`, `Infinity`, or `-Infinity`.
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* ArbitraryNumber.from(1500); // { coefficient: 1.5, exponent: 3 }
|
|
206
|
+
*/
|
|
207
|
+
static from(value) {
|
|
208
|
+
if (!isFinite(value)) {
|
|
209
|
+
throw new ArbitraryNumberInputError("ArbitraryNumber.from: value must be finite", value);
|
|
210
|
+
}
|
|
211
|
+
return new _ArbitraryNumber(value, 0);
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Like `from`, but returns `null` instead of throwing for non-finite inputs.
|
|
215
|
+
*
|
|
216
|
+
* Use at system boundaries (form inputs, external APIs) where bad input should
|
|
217
|
+
* be handled gracefully.
|
|
218
|
+
*
|
|
219
|
+
* @example
|
|
220
|
+
* ArbitraryNumber.tryFrom(Infinity) // null
|
|
221
|
+
* ArbitraryNumber.tryFrom(1500) // ArbitraryNumber { coefficient: 1.5, exponent: 3 }
|
|
222
|
+
*/
|
|
223
|
+
static tryFrom(value) {
|
|
224
|
+
if (!isFinite(value)) return null;
|
|
225
|
+
return new _ArbitraryNumber(value, 0);
|
|
226
|
+
}
|
|
227
|
+
// ── Static arithmetic (allocate) ───────────────────────────────────────────
|
|
228
|
+
/**
|
|
229
|
+
* Returns a **new** instance equal to `a + b`.
|
|
230
|
+
*
|
|
231
|
+
* Static methods always allocate — use instance `.add()` on hot paths.
|
|
232
|
+
*/
|
|
233
|
+
static add(a, b) {
|
|
234
|
+
return a.clone().add(b);
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Returns a **new** instance equal to `a - b`.
|
|
238
|
+
*/
|
|
239
|
+
static sub(a, b) {
|
|
240
|
+
return a.clone().sub(b);
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Returns a **new** instance equal to `a * b`.
|
|
244
|
+
*/
|
|
245
|
+
static mul(a, b) {
|
|
246
|
+
return a.clone().mul(b);
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Returns a **new** instance equal to `a / b`.
|
|
250
|
+
*
|
|
251
|
+
* @throws `"Division by zero"` when `b` is zero.
|
|
252
|
+
*/
|
|
253
|
+
static div(a, b) {
|
|
254
|
+
return a.clone().div(b);
|
|
159
255
|
}
|
|
256
|
+
// ── Instance arithmetic (mutate this) ──────────────────────────────────────
|
|
160
257
|
/**
|
|
161
|
-
*
|
|
258
|
+
* Adds `other` to this number in-place.
|
|
162
259
|
*
|
|
163
|
-
* When the exponent difference exceeds
|
|
164
|
-
* operand has no effect and
|
|
260
|
+
* When the exponent difference exceeds `defaults.scaleCutoff`, the smaller
|
|
261
|
+
* operand has no effect and `this` is returned unchanged.
|
|
262
|
+
*
|
|
263
|
+
* **Mutates `this`. Returns `this`.**
|
|
165
264
|
*
|
|
166
265
|
* @example
|
|
167
|
-
*
|
|
266
|
+
* gold.add(drop); // gold is mutated
|
|
168
267
|
*/
|
|
169
268
|
add(other) {
|
|
170
|
-
if (this.coefficient === 0) return other;
|
|
171
269
|
if (other.coefficient === 0) return this;
|
|
172
|
-
|
|
270
|
+
if (this.coefficient === 0) {
|
|
271
|
+
this.coefficient = other.coefficient;
|
|
272
|
+
this.exponent = other.exponent;
|
|
273
|
+
return this;
|
|
274
|
+
}
|
|
275
|
+
const cutoff = _scaleCutoff;
|
|
173
276
|
const diff = this.exponent - other.exponent;
|
|
174
277
|
if (diff > cutoff) return this;
|
|
175
|
-
if (diff < -cutoff)
|
|
176
|
-
|
|
177
|
-
|
|
278
|
+
if (diff < -cutoff) {
|
|
279
|
+
this.coefficient = other.coefficient;
|
|
280
|
+
this.exponent = other.exponent;
|
|
281
|
+
return this;
|
|
282
|
+
}
|
|
283
|
+
const oc = other.coefficient;
|
|
284
|
+
const oe = other.exponent;
|
|
285
|
+
let c, e;
|
|
178
286
|
if (diff >= 0) {
|
|
179
|
-
c = this.coefficient +
|
|
287
|
+
c = this.coefficient + oc / pow10(diff);
|
|
180
288
|
e = this.exponent;
|
|
181
289
|
} else {
|
|
182
|
-
c =
|
|
183
|
-
e =
|
|
290
|
+
c = oc + this.coefficient / pow10(-diff);
|
|
291
|
+
e = oe;
|
|
184
292
|
}
|
|
185
|
-
return
|
|
293
|
+
return this._normalizeInto(c, e);
|
|
186
294
|
}
|
|
187
295
|
/**
|
|
188
|
-
*
|
|
296
|
+
* Subtracts `other` from this number in-place.
|
|
189
297
|
*
|
|
190
|
-
*
|
|
191
|
-
* new ArbitraryNumber(3.5, 3).sub(new ArbitraryNumber(1.5, 3)); // 2*10^3
|
|
298
|
+
* **Mutates `this`. Returns `this`.**
|
|
192
299
|
*/
|
|
193
300
|
sub(other) {
|
|
194
301
|
if (other.coefficient === 0) return this;
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
const
|
|
198
|
-
|
|
302
|
+
const oc = other.coefficient;
|
|
303
|
+
const oe = other.exponent;
|
|
304
|
+
const negOc = -oc;
|
|
305
|
+
if (this.coefficient === 0) {
|
|
306
|
+
this.coefficient = negOc;
|
|
307
|
+
this.exponent = oe;
|
|
308
|
+
return this;
|
|
309
|
+
}
|
|
310
|
+
const cutoff = _scaleCutoff;
|
|
311
|
+
const diff = this.exponent - oe;
|
|
199
312
|
if (diff > cutoff) return this;
|
|
200
|
-
if (diff < -cutoff)
|
|
201
|
-
|
|
202
|
-
|
|
313
|
+
if (diff < -cutoff) {
|
|
314
|
+
this.coefficient = negOc;
|
|
315
|
+
this.exponent = oe;
|
|
316
|
+
return this;
|
|
317
|
+
}
|
|
318
|
+
let c, e;
|
|
203
319
|
if (diff >= 0) {
|
|
204
|
-
c = this.coefficient +
|
|
320
|
+
c = this.coefficient + negOc / pow10(diff);
|
|
205
321
|
e = this.exponent;
|
|
206
322
|
} else {
|
|
207
|
-
c =
|
|
208
|
-
e =
|
|
323
|
+
c = negOc + this.coefficient / pow10(-diff);
|
|
324
|
+
e = oe;
|
|
209
325
|
}
|
|
210
|
-
return
|
|
326
|
+
return this._normalizeInto(c, e);
|
|
211
327
|
}
|
|
212
328
|
/**
|
|
213
|
-
*
|
|
329
|
+
* Multiplies this number by `other` in-place.
|
|
214
330
|
*
|
|
215
|
-
*
|
|
216
|
-
* new ArbitraryNumber(2, 3).mul(new ArbitraryNumber(3, 4)); // 6*10^7
|
|
331
|
+
* **Mutates `this`. Returns `this`.**
|
|
217
332
|
*/
|
|
218
333
|
mul(other) {
|
|
219
|
-
if (this.coefficient === 0
|
|
334
|
+
if (this.coefficient === 0) return this;
|
|
335
|
+
if (other.coefficient === 0) {
|
|
336
|
+
this.coefficient = 0;
|
|
337
|
+
this.exponent = 0;
|
|
338
|
+
return this;
|
|
339
|
+
}
|
|
220
340
|
const c = this.coefficient * other.coefficient;
|
|
221
341
|
const e = this.exponent + other.exponent;
|
|
222
342
|
const absC = c < 0 ? -c : c;
|
|
223
|
-
if (absC >= 10)
|
|
224
|
-
|
|
343
|
+
if (absC >= 10) {
|
|
344
|
+
this.coefficient = c / 10;
|
|
345
|
+
this.exponent = e + 1;
|
|
346
|
+
} else {
|
|
347
|
+
this.coefficient = c;
|
|
348
|
+
this.exponent = e;
|
|
349
|
+
}
|
|
350
|
+
return this;
|
|
225
351
|
}
|
|
226
352
|
/**
|
|
227
|
-
*
|
|
353
|
+
* Divides this number by `other` in-place.
|
|
228
354
|
*
|
|
229
|
-
*
|
|
230
|
-
* new ArbitraryNumber(6, 7).div(new ArbitraryNumber(3, 4)); // 2*10^3
|
|
355
|
+
* **Mutates `this`. Returns `this`.**
|
|
231
356
|
*
|
|
232
357
|
* @throws `"Division by zero"` when `other` is zero.
|
|
233
358
|
*/
|
|
234
359
|
div(other) {
|
|
235
|
-
if (other.coefficient === 0)
|
|
360
|
+
if (other.coefficient === 0) {
|
|
361
|
+
throw new ArbitraryNumberDomainError("Division by zero", { dividend: this.toNumber(), divisor: 0 });
|
|
362
|
+
}
|
|
363
|
+
if (this.coefficient === 0) return this;
|
|
236
364
|
const c = this.coefficient / other.coefficient;
|
|
237
365
|
const e = this.exponent - other.exponent;
|
|
238
|
-
if (c === 0)
|
|
366
|
+
if (c === 0) {
|
|
367
|
+
this.coefficient = 0;
|
|
368
|
+
this.exponent = 0;
|
|
369
|
+
return this;
|
|
370
|
+
}
|
|
239
371
|
const absC = c < 0 ? -c : c;
|
|
240
|
-
if (absC < 1)
|
|
241
|
-
|
|
372
|
+
if (absC < 1) {
|
|
373
|
+
this.coefficient = c * 10;
|
|
374
|
+
this.exponent = e - 1;
|
|
375
|
+
} else {
|
|
376
|
+
this.coefficient = c;
|
|
377
|
+
this.exponent = e;
|
|
378
|
+
}
|
|
379
|
+
return this;
|
|
242
380
|
}
|
|
243
381
|
/**
|
|
244
|
-
*
|
|
382
|
+
* Negates this number in-place.
|
|
245
383
|
*
|
|
246
|
-
*
|
|
247
|
-
* new ArbitraryNumber(1.5, 3).negate(); // -1.5*10^3
|
|
384
|
+
* **Mutates `this`. Returns `this`.**
|
|
248
385
|
*/
|
|
249
386
|
negate() {
|
|
250
|
-
|
|
251
|
-
return
|
|
387
|
+
this.coefficient = -this.coefficient;
|
|
388
|
+
return this;
|
|
252
389
|
}
|
|
253
390
|
/**
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
* Returns `this` unchanged when the number is already non-negative.
|
|
391
|
+
* Sets this number to its absolute value in-place.
|
|
257
392
|
*
|
|
258
|
-
*
|
|
259
|
-
* new ArbitraryNumber(-1.5, 3).abs(); // 1.5*10^3
|
|
393
|
+
* **Mutates `this`. Returns `this`.**
|
|
260
394
|
*/
|
|
261
395
|
abs() {
|
|
262
|
-
if (this.coefficient
|
|
263
|
-
return
|
|
396
|
+
if (this.coefficient < 0) this.coefficient = -this.coefficient;
|
|
397
|
+
return this;
|
|
264
398
|
}
|
|
265
399
|
/**
|
|
266
|
-
*
|
|
400
|
+
* Raises this number to the power `n` in-place.
|
|
267
401
|
*
|
|
268
402
|
* Supports integer, fractional, and negative exponents.
|
|
269
|
-
* `x^0` always
|
|
403
|
+
* `x^0` always sets `this` to `1`, including `0^0` (by convention).
|
|
270
404
|
*
|
|
271
|
-
*
|
|
272
|
-
* new ArbitraryNumber(2, 3).pow(2); // 4*10^6
|
|
273
|
-
* new ArbitraryNumber(2, 0).pow(-1); // 5*10^-1 (= 0.5)
|
|
405
|
+
* **Mutates `this`. Returns `this`.**
|
|
274
406
|
*
|
|
275
|
-
* @param n - The exponent to raise this number to.
|
|
276
407
|
* @throws `"Zero cannot be raised to a negative power"` when this is zero and `n < 0`.
|
|
277
408
|
*/
|
|
278
409
|
pow(n) {
|
|
279
410
|
if (n === 0) {
|
|
280
|
-
|
|
411
|
+
this.coefficient = 1;
|
|
412
|
+
this.exponent = 0;
|
|
413
|
+
return this;
|
|
281
414
|
}
|
|
282
415
|
if (this.coefficient === 0) {
|
|
283
416
|
if (n < 0) {
|
|
284
417
|
throw new ArbitraryNumberDomainError("Zero cannot be raised to a negative power", { exponent: n });
|
|
285
418
|
}
|
|
286
|
-
return
|
|
419
|
+
return this;
|
|
287
420
|
}
|
|
288
421
|
if (this.coefficient < 0 && !Number.isInteger(n)) {
|
|
289
|
-
throw new ArbitraryNumberDomainError(
|
|
422
|
+
throw new ArbitraryNumberDomainError(
|
|
423
|
+
"ArbitraryNumber.pow: fractional exponent of a negative base is not supported",
|
|
424
|
+
{ base: this.toNumber(), exponent: n }
|
|
425
|
+
);
|
|
290
426
|
}
|
|
291
427
|
const rawExp = this.exponent * n;
|
|
292
428
|
const intExp = Math.floor(rawExp);
|
|
293
429
|
const fracExp = rawExp - intExp;
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
intExp
|
|
297
|
-
);
|
|
430
|
+
const newC = Math.pow(this.coefficient, n) * pow10(fracExp);
|
|
431
|
+
return this._normalizeInto(newC, intExp);
|
|
298
432
|
}
|
|
299
433
|
/**
|
|
300
|
-
* Fused multiply-add: `(this * multiplier) + addend`.
|
|
301
|
-
*
|
|
302
|
-
* Faster than `.mul(multiplier).add(addend)` because it avoids allocating an
|
|
303
|
-
* intermediate ArbitraryNumber for the product. One normalisation pass total.
|
|
434
|
+
* Fused multiply-add in-place: `this = (this * multiplier) + addend`.
|
|
304
435
|
*
|
|
305
|
-
*
|
|
436
|
+
* Faster than `.mul(multiplier).add(addend)` — one normalisation pass total, no
|
|
437
|
+
* intermediate allocation.
|
|
306
438
|
*
|
|
307
|
-
*
|
|
308
|
-
* // Equivalent to value.mul(mult).add(boost) but ~35-50% faster
|
|
309
|
-
* const prestiged = currentValue.mulAdd(multiplier, boost);
|
|
439
|
+
* **Mutates `this`. Returns `this`.**
|
|
310
440
|
*/
|
|
311
441
|
mulAdd(multiplier, addend) {
|
|
312
|
-
if (this.coefficient === 0 || multiplier.coefficient === 0)
|
|
442
|
+
if (this.coefficient === 0 || multiplier.coefficient === 0) {
|
|
443
|
+
this.coefficient = addend.coefficient;
|
|
444
|
+
this.exponent = addend.exponent;
|
|
445
|
+
return this;
|
|
446
|
+
}
|
|
447
|
+
const ac = addend.coefficient;
|
|
448
|
+
const ae = addend.exponent;
|
|
313
449
|
let cp = this.coefficient * multiplier.coefficient;
|
|
314
450
|
let ep = this.exponent + multiplier.exponent;
|
|
315
451
|
const absCp = cp < 0 ? -cp : cp;
|
|
@@ -317,89 +453,144 @@ var _ArbitraryNumber = class _ArbitraryNumber {
|
|
|
317
453
|
cp /= 10;
|
|
318
454
|
ep += 1;
|
|
319
455
|
}
|
|
320
|
-
if (
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
456
|
+
if (ac === 0) {
|
|
457
|
+
this.coefficient = cp;
|
|
458
|
+
this.exponent = ep;
|
|
459
|
+
return this;
|
|
460
|
+
}
|
|
461
|
+
const cutoff = _scaleCutoff;
|
|
462
|
+
const diff = ep - ae;
|
|
463
|
+
if (diff > cutoff) {
|
|
464
|
+
this.coefficient = cp;
|
|
465
|
+
this.exponent = ep;
|
|
466
|
+
return this;
|
|
467
|
+
}
|
|
468
|
+
if (diff < -cutoff) {
|
|
469
|
+
this.coefficient = ac;
|
|
470
|
+
this.exponent = ae;
|
|
471
|
+
return this;
|
|
472
|
+
}
|
|
325
473
|
let c, e;
|
|
326
474
|
if (diff >= 0) {
|
|
327
|
-
c = cp +
|
|
475
|
+
c = cp + ac / pow10(diff);
|
|
328
476
|
e = ep;
|
|
329
477
|
} else {
|
|
330
|
-
c =
|
|
331
|
-
e =
|
|
478
|
+
c = ac + cp / pow10(-diff);
|
|
479
|
+
e = ae;
|
|
332
480
|
}
|
|
333
|
-
return
|
|
481
|
+
return this._normalizeInto(c, e);
|
|
334
482
|
}
|
|
335
483
|
/**
|
|
336
|
-
*
|
|
337
|
-
*
|
|
338
|
-
*
|
|
339
|
-
*
|
|
484
|
+
* @internal
|
|
485
|
+
* Computes `(this + sign * addendC * 10^addendE)` and writes the normalised
|
|
486
|
+
* result back into `this`. Used by `addMul` and `subMul` to share the alignment
|
|
487
|
+
* logic without duplication.
|
|
340
488
|
*
|
|
341
|
-
*
|
|
342
|
-
*
|
|
343
|
-
*
|
|
344
|
-
* // Equivalent to base.add(bonus).mul(multiplier) but ~20-25% faster
|
|
345
|
-
* const upgraded = baseValue.addMul(bonus, multiplier);
|
|
489
|
+
* Returns the sign-adjusted addend coefficient so callers can detect the
|
|
490
|
+
* zero-result case. Writes the intermediate normalised sum into `this.coefficient`
|
|
491
|
+
* / `this.exponent` ready for the subsequent multiply step.
|
|
346
492
|
*/
|
|
347
|
-
|
|
348
|
-
if (multiplier.coefficient === 0) return _ArbitraryNumber.Zero;
|
|
349
|
-
let cs, es;
|
|
350
|
-
if (this.coefficient === 0 && addend.coefficient === 0) return _ArbitraryNumber.Zero;
|
|
493
|
+
_addScaledInto(addendC, addendE) {
|
|
351
494
|
if (this.coefficient === 0) {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
495
|
+
this.coefficient = addendC;
|
|
496
|
+
this.exponent = addendE;
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
if (addendC === 0) return;
|
|
500
|
+
const cutoff = _scaleCutoff;
|
|
501
|
+
const diff = this.exponent - addendE;
|
|
502
|
+
if (diff > cutoff) return;
|
|
503
|
+
if (diff < -cutoff) {
|
|
504
|
+
this.coefficient = addendC;
|
|
505
|
+
this.exponent = addendE;
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
let cs, es;
|
|
509
|
+
if (diff >= 0) {
|
|
510
|
+
cs = this.coefficient + addendC / pow10(diff);
|
|
356
511
|
es = this.exponent;
|
|
357
512
|
} else {
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
es = addend.exponent;
|
|
373
|
-
}
|
|
374
|
-
if (cs === 0) return _ArbitraryNumber.Zero;
|
|
375
|
-
const abs = Math.abs(cs);
|
|
376
|
-
const shift = Math.floor(Math.log10(abs));
|
|
377
|
-
const scale = pow10(shift);
|
|
378
|
-
if (scale === 0) return _ArbitraryNumber.Zero;
|
|
379
|
-
cs /= scale;
|
|
380
|
-
es += shift;
|
|
513
|
+
cs = addendC + this.coefficient / pow10(-diff);
|
|
514
|
+
es = addendE;
|
|
515
|
+
}
|
|
516
|
+
if (cs === 0) {
|
|
517
|
+
this.coefficient = 0;
|
|
518
|
+
this.exponent = 0;
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
const abs = cs < 0 ? -cs : cs;
|
|
522
|
+
if (abs < 10) {
|
|
523
|
+
if (abs >= 1) {
|
|
524
|
+
this.coefficient = cs;
|
|
525
|
+
this.exponent = es;
|
|
526
|
+
return;
|
|
381
527
|
}
|
|
528
|
+
this.coefficient = cs * 10;
|
|
529
|
+
this.exponent = es - 1;
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
if (abs < 100) {
|
|
533
|
+
this.coefficient = cs / 10;
|
|
534
|
+
this.exponent = es + 1;
|
|
535
|
+
return;
|
|
382
536
|
}
|
|
383
|
-
|
|
384
|
-
|
|
537
|
+
if (!isFinite(cs)) {
|
|
538
|
+
this.coefficient = 0;
|
|
539
|
+
this.exponent = 0;
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
const shift = Math.floor(Math.log10(abs));
|
|
543
|
+
const scale = pow10(shift);
|
|
544
|
+
if (scale === 0) {
|
|
545
|
+
this.coefficient = 0;
|
|
546
|
+
this.exponent = 0;
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
this.coefficient = cs / scale;
|
|
550
|
+
this.exponent = es + shift;
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Fused add-multiply in-place: `this = (this + addend) * multiplier`.
|
|
554
|
+
*
|
|
555
|
+
* **Mutates `this`. Returns `this`.**
|
|
556
|
+
*/
|
|
557
|
+
addMul(addend, multiplier) {
|
|
558
|
+
if (multiplier.coefficient === 0) {
|
|
559
|
+
this.coefficient = 0;
|
|
560
|
+
this.exponent = 0;
|
|
561
|
+
return this;
|
|
562
|
+
}
|
|
563
|
+
const ac = addend.coefficient;
|
|
564
|
+
const ae = addend.exponent;
|
|
565
|
+
const mc = multiplier.coefficient;
|
|
566
|
+
const me = multiplier.exponent;
|
|
567
|
+
this._addScaledInto(ac, ae);
|
|
568
|
+
if (this.coefficient === 0) return this;
|
|
569
|
+
let cp = this.coefficient * mc;
|
|
570
|
+
const ep = this.exponent + me;
|
|
385
571
|
const absCp = cp < 0 ? -cp : cp;
|
|
386
572
|
if (absCp >= 10) {
|
|
387
573
|
cp /= 10;
|
|
388
|
-
|
|
574
|
+
this.coefficient = cp;
|
|
575
|
+
this.exponent = ep + 1;
|
|
576
|
+
return this;
|
|
389
577
|
}
|
|
390
|
-
|
|
578
|
+
this.coefficient = cp;
|
|
579
|
+
this.exponent = ep;
|
|
580
|
+
return this;
|
|
391
581
|
}
|
|
392
582
|
/**
|
|
393
|
-
* Fused multiply-subtract: `(this * multiplier) - subtrahend`.
|
|
394
|
-
*
|
|
395
|
-
* Avoids one intermediate allocation vs `.mul(multiplier).sub(subtrahend)`.
|
|
583
|
+
* Fused multiply-subtract in-place: `this = (this * multiplier) - subtrahend`.
|
|
396
584
|
*
|
|
397
|
-
*
|
|
585
|
+
* **Mutates `this`. Returns `this`.**
|
|
398
586
|
*/
|
|
399
587
|
mulSub(multiplier, subtrahend) {
|
|
588
|
+
const sc = subtrahend.coefficient;
|
|
589
|
+
const se = subtrahend.exponent;
|
|
400
590
|
if (this.coefficient === 0 || multiplier.coefficient === 0) {
|
|
401
|
-
|
|
402
|
-
|
|
591
|
+
this.coefficient = sc === 0 ? 0 : -sc;
|
|
592
|
+
this.exponent = sc === 0 ? 0 : se;
|
|
593
|
+
return this;
|
|
403
594
|
}
|
|
404
595
|
let cp = this.coefficient * multiplier.coefficient;
|
|
405
596
|
let ep = this.exponent + multiplier.exponent;
|
|
@@ -408,87 +599,81 @@ var _ArbitraryNumber = class _ArbitraryNumber {
|
|
|
408
599
|
cp /= 10;
|
|
409
600
|
ep += 1;
|
|
410
601
|
}
|
|
411
|
-
if (
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
602
|
+
if (sc === 0) {
|
|
603
|
+
this.coefficient = cp;
|
|
604
|
+
this.exponent = ep;
|
|
605
|
+
return this;
|
|
606
|
+
}
|
|
607
|
+
const cutoff = _scaleCutoff;
|
|
608
|
+
const diff = ep - se;
|
|
609
|
+
if (diff > cutoff) {
|
|
610
|
+
this.coefficient = cp;
|
|
611
|
+
this.exponent = ep;
|
|
612
|
+
return this;
|
|
613
|
+
}
|
|
415
614
|
if (diff < -cutoff) {
|
|
416
|
-
|
|
615
|
+
this.coefficient = -sc;
|
|
616
|
+
this.exponent = se;
|
|
617
|
+
return this;
|
|
417
618
|
}
|
|
418
619
|
let c, e;
|
|
419
620
|
if (diff >= 0) {
|
|
420
|
-
c = cp -
|
|
621
|
+
c = cp - sc / pow10(diff);
|
|
421
622
|
e = ep;
|
|
422
623
|
} else {
|
|
423
|
-
c = -
|
|
424
|
-
e =
|
|
624
|
+
c = -sc + cp / pow10(-diff);
|
|
625
|
+
e = se;
|
|
425
626
|
}
|
|
426
|
-
return
|
|
627
|
+
return this._normalizeInto(c, e);
|
|
427
628
|
}
|
|
428
629
|
/**
|
|
429
|
-
* Fused subtract-multiply: `(this - subtrahend) * multiplier`.
|
|
630
|
+
* Fused subtract-multiply in-place: `this = (this - subtrahend) * multiplier`.
|
|
430
631
|
*
|
|
431
|
-
*
|
|
432
|
-
*
|
|
433
|
-
* Common pattern - upgrade after penalty: `health.subMul(damage, multiplier)`
|
|
632
|
+
* **Mutates `this`. Returns `this`.**
|
|
434
633
|
*/
|
|
435
634
|
subMul(subtrahend, multiplier) {
|
|
436
|
-
if (multiplier.coefficient === 0)
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
cs = -subtrahend.coefficient;
|
|
441
|
-
es = subtrahend.exponent;
|
|
442
|
-
} else if (subtrahend.coefficient === 0) {
|
|
443
|
-
cs = this.coefficient;
|
|
444
|
-
es = this.exponent;
|
|
445
|
-
} else {
|
|
446
|
-
const cutoff = _ArbitraryNumber.PrecisionCutoff;
|
|
447
|
-
const diff = this.exponent - subtrahend.exponent;
|
|
448
|
-
if (diff > cutoff) {
|
|
449
|
-
cs = this.coefficient;
|
|
450
|
-
es = this.exponent;
|
|
451
|
-
} else if (diff < -cutoff) {
|
|
452
|
-
cs = -subtrahend.coefficient;
|
|
453
|
-
es = subtrahend.exponent;
|
|
454
|
-
} else {
|
|
455
|
-
if (diff >= 0) {
|
|
456
|
-
cs = this.coefficient - subtrahend.coefficient / pow10Int(diff);
|
|
457
|
-
es = this.exponent;
|
|
458
|
-
} else {
|
|
459
|
-
cs = -subtrahend.coefficient + this.coefficient / pow10Int(-diff);
|
|
460
|
-
es = subtrahend.exponent;
|
|
461
|
-
}
|
|
462
|
-
if (cs === 0) return _ArbitraryNumber.Zero;
|
|
463
|
-
const abs = Math.abs(cs);
|
|
464
|
-
const shift = Math.floor(Math.log10(abs));
|
|
465
|
-
const scale = pow10(shift);
|
|
466
|
-
if (scale === 0) return _ArbitraryNumber.Zero;
|
|
467
|
-
cs /= scale;
|
|
468
|
-
es += shift;
|
|
469
|
-
}
|
|
635
|
+
if (multiplier.coefficient === 0) {
|
|
636
|
+
this.coefficient = 0;
|
|
637
|
+
this.exponent = 0;
|
|
638
|
+
return this;
|
|
470
639
|
}
|
|
471
|
-
|
|
472
|
-
const
|
|
640
|
+
const sc = subtrahend.coefficient;
|
|
641
|
+
const se = subtrahend.exponent;
|
|
642
|
+
const mc = multiplier.coefficient;
|
|
643
|
+
const me = multiplier.exponent;
|
|
644
|
+
this._addScaledInto(-sc, se);
|
|
645
|
+
if (this.coefficient === 0) return this;
|
|
646
|
+
let cp = this.coefficient * mc;
|
|
647
|
+
const ep = this.exponent + me;
|
|
473
648
|
const absCp = cp < 0 ? -cp : cp;
|
|
474
649
|
if (absCp >= 10) {
|
|
475
650
|
cp /= 10;
|
|
476
|
-
|
|
651
|
+
this.coefficient = cp;
|
|
652
|
+
this.exponent = ep + 1;
|
|
653
|
+
return this;
|
|
477
654
|
}
|
|
478
|
-
|
|
655
|
+
this.coefficient = cp;
|
|
656
|
+
this.exponent = ep;
|
|
657
|
+
return this;
|
|
479
658
|
}
|
|
480
659
|
/**
|
|
481
|
-
* Fused divide-add: `(this / divisor) + addend`.
|
|
482
|
-
*
|
|
483
|
-
* Avoids one intermediate allocation vs `.div(divisor).add(addend)`.
|
|
660
|
+
* Fused divide-add in-place: `this = (this / divisor) + addend`.
|
|
484
661
|
*
|
|
485
|
-
*
|
|
662
|
+
* **Mutates `this`. Returns `this`.**
|
|
486
663
|
*
|
|
487
664
|
* @throws `"Division by zero"` when divisor is zero.
|
|
488
665
|
*/
|
|
489
666
|
divAdd(divisor, addend) {
|
|
490
|
-
if (divisor.coefficient === 0)
|
|
491
|
-
|
|
667
|
+
if (divisor.coefficient === 0) {
|
|
668
|
+
throw new ArbitraryNumberDomainError("Division by zero", { dividend: this.toNumber(), divisor: 0 });
|
|
669
|
+
}
|
|
670
|
+
const ac = addend.coefficient;
|
|
671
|
+
const ae = addend.exponent;
|
|
672
|
+
if (this.coefficient === 0) {
|
|
673
|
+
this.coefficient = ac;
|
|
674
|
+
this.exponent = ae;
|
|
675
|
+
return this;
|
|
676
|
+
}
|
|
492
677
|
let cd = this.coefficient / divisor.coefficient;
|
|
493
678
|
let ed = this.exponent - divisor.exponent;
|
|
494
679
|
const absC = cd < 0 ? -cd : cd;
|
|
@@ -496,97 +681,112 @@ var _ArbitraryNumber = class _ArbitraryNumber {
|
|
|
496
681
|
cd *= 10;
|
|
497
682
|
ed -= 1;
|
|
498
683
|
}
|
|
499
|
-
if (
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
684
|
+
if (ac === 0) {
|
|
685
|
+
this.coefficient = cd;
|
|
686
|
+
this.exponent = ed;
|
|
687
|
+
return this;
|
|
688
|
+
}
|
|
689
|
+
const cutoff = _scaleCutoff;
|
|
690
|
+
const diff = ed - ae;
|
|
691
|
+
if (diff > cutoff) {
|
|
692
|
+
this.coefficient = cd;
|
|
693
|
+
this.exponent = ed;
|
|
694
|
+
return this;
|
|
695
|
+
}
|
|
696
|
+
if (diff < -cutoff) {
|
|
697
|
+
this.coefficient = ac;
|
|
698
|
+
this.exponent = ae;
|
|
699
|
+
return this;
|
|
700
|
+
}
|
|
504
701
|
let c, e;
|
|
505
702
|
if (diff >= 0) {
|
|
506
|
-
c = cd +
|
|
703
|
+
c = cd + ac / pow10(diff);
|
|
507
704
|
e = ed;
|
|
508
705
|
} else {
|
|
509
|
-
c =
|
|
510
|
-
e =
|
|
706
|
+
c = ac + cd / pow10(-diff);
|
|
707
|
+
e = ae;
|
|
511
708
|
}
|
|
512
|
-
return
|
|
709
|
+
return this._normalizeInto(c, e);
|
|
513
710
|
}
|
|
514
711
|
/**
|
|
515
|
-
* Fused multiply-divide: `(this * multiplier) / divisor`.
|
|
516
|
-
*
|
|
517
|
-
* Avoids one intermediate allocation vs `.mul(multiplier).div(divisor)`.
|
|
518
|
-
* The divisor zero-check is performed before the multiply to avoid unnecessary work.
|
|
519
|
-
*
|
|
520
|
-
* Common pattern - idle-tick math: `(production * deltaTime) / cost`
|
|
712
|
+
* Fused multiply-divide in-place: `this = (this * multiplier) / divisor`.
|
|
521
713
|
*
|
|
522
|
-
*
|
|
523
|
-
* // Equivalent to production.mul(deltaTime).div(cost) but ~30-40% faster
|
|
524
|
-
* const consumed = production.mulDiv(deltaTime, cost);
|
|
714
|
+
* **Mutates `this`. Returns `this`.**
|
|
525
715
|
*
|
|
526
716
|
* @throws `"Division by zero"` when divisor is zero.
|
|
527
717
|
*/
|
|
528
718
|
mulDiv(multiplier, divisor) {
|
|
529
|
-
if (divisor.coefficient === 0)
|
|
530
|
-
|
|
719
|
+
if (divisor.coefficient === 0) {
|
|
720
|
+
throw new ArbitraryNumberDomainError("Division by zero", { dividend: this.toNumber(), divisor: 0 });
|
|
721
|
+
}
|
|
722
|
+
if (this.coefficient === 0) return this;
|
|
723
|
+
if (multiplier.coefficient === 0) {
|
|
724
|
+
this.coefficient = 0;
|
|
725
|
+
this.exponent = 0;
|
|
726
|
+
return this;
|
|
727
|
+
}
|
|
531
728
|
const c = this.coefficient * multiplier.coefficient / divisor.coefficient;
|
|
532
729
|
const e = this.exponent + multiplier.exponent - divisor.exponent;
|
|
533
730
|
const absC = c < 0 ? -c : c;
|
|
534
|
-
if (absC >= 10)
|
|
535
|
-
|
|
536
|
-
|
|
731
|
+
if (absC >= 10) {
|
|
732
|
+
this.coefficient = c / 10;
|
|
733
|
+
this.exponent = e + 1;
|
|
734
|
+
} else if (absC < 1) {
|
|
735
|
+
this.coefficient = c * 10;
|
|
736
|
+
this.exponent = e - 1;
|
|
737
|
+
} else {
|
|
738
|
+
this.coefficient = c;
|
|
739
|
+
this.exponent = e;
|
|
740
|
+
}
|
|
741
|
+
return this;
|
|
537
742
|
}
|
|
743
|
+
// ── Batch (static, allocate) ───────────────────────────────────────────────
|
|
538
744
|
/**
|
|
539
|
-
* Efficiently sums an array of
|
|
745
|
+
* Efficiently sums an array of `ArbitraryNumber`s in a single pass.
|
|
540
746
|
*
|
|
541
|
-
*
|
|
542
|
-
*
|
|
543
|
-
* then normalises once - regardless of array size.
|
|
747
|
+
* Maintains a running pivot exponent and rescales the accumulator when a larger
|
|
748
|
+
* exponent is encountered — one pass, no pre-scan needed.
|
|
544
749
|
*
|
|
545
|
-
*
|
|
546
|
-
* sumArray ~ 50 divisions + 1 log10 call + 1 allocation -> ~9* faster.
|
|
547
|
-
*
|
|
548
|
-
* Common pattern - income aggregation: `total = ArbitraryNumber.sumArray(incomeSourcesPerTick)`
|
|
549
|
-
*
|
|
550
|
-
* @example
|
|
551
|
-
* const total = ArbitraryNumber.sumArray(incomeSources); // far faster than .reduce((a, b) => a.add(b))
|
|
552
|
-
*
|
|
553
|
-
* @param numbers - Array to sum. Empty array returns {@link Zero}. Single element returned as-is.
|
|
750
|
+
* Empty array returns `Zero`. Single element returned as-is (no clone).
|
|
554
751
|
*/
|
|
555
752
|
static sumArray(numbers) {
|
|
556
753
|
const len = numbers.length;
|
|
557
754
|
if (len === 0) return _ArbitraryNumber.Zero;
|
|
558
755
|
if (len === 1) return numbers[0];
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
const n = numbers[i];
|
|
562
|
-
if (n.exponent > pivotExp) pivotExp = n.exponent;
|
|
563
|
-
}
|
|
564
|
-
const cutoff = _ArbitraryNumber.PrecisionCutoff;
|
|
756
|
+
const cutoff = _scaleCutoff;
|
|
757
|
+
let pivot = -Infinity;
|
|
565
758
|
let total = 0;
|
|
566
759
|
for (let i = 0; i < len; i++) {
|
|
567
760
|
const n = numbers[i];
|
|
568
761
|
if (n.coefficient === 0) continue;
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
762
|
+
if (n.exponent > pivot) {
|
|
763
|
+
if (pivot !== -Infinity) {
|
|
764
|
+
const drop = n.exponent - pivot;
|
|
765
|
+
total = drop > cutoff ? 0 : total / pow10(drop);
|
|
766
|
+
}
|
|
767
|
+
pivot = n.exponent;
|
|
768
|
+
total += n.coefficient;
|
|
769
|
+
} else {
|
|
770
|
+
const diff = pivot - n.exponent;
|
|
771
|
+
if (diff <= cutoff) total += n.coefficient / pow10(diff);
|
|
772
|
+
}
|
|
572
773
|
}
|
|
573
|
-
if (total === 0) return _ArbitraryNumber.Zero;
|
|
574
|
-
const abs =
|
|
774
|
+
if (total === 0 || pivot === -Infinity) return _ArbitraryNumber.Zero;
|
|
775
|
+
const abs = total < 0 ? -total : total;
|
|
776
|
+
if (abs < 10) {
|
|
777
|
+
if (abs >= 1) return _ArbitraryNumber.unsafe(total, pivot);
|
|
778
|
+
return _ArbitraryNumber.unsafe(total * 10, pivot - 1);
|
|
779
|
+
}
|
|
780
|
+
if (abs < 100) return _ArbitraryNumber.unsafe(total / 10, pivot + 1);
|
|
575
781
|
const shift = Math.floor(Math.log10(abs));
|
|
576
782
|
const scale = pow10(shift);
|
|
577
783
|
if (scale === 0) return _ArbitraryNumber.Zero;
|
|
578
|
-
return _ArbitraryNumber.
|
|
784
|
+
return _ArbitraryNumber.unsafe(total / scale, pivot + shift);
|
|
579
785
|
}
|
|
580
786
|
/**
|
|
581
787
|
* Multiplies an array of `ArbitraryNumber`s in a single pass.
|
|
582
788
|
*
|
|
583
|
-
*
|
|
584
|
-
* to keep values in [1, 10). Exponents are summed. One total normalisation at the end.
|
|
585
|
-
*
|
|
586
|
-
* Empty array returns {@link One}. Single element returned as-is.
|
|
587
|
-
*
|
|
588
|
-
* @example
|
|
589
|
-
* ArbitraryNumber.productArray([an(2), an(3), an(4)]); // 24
|
|
789
|
+
* Empty array returns `One`. Single element returned as-is.
|
|
590
790
|
*/
|
|
591
791
|
static productArray(numbers) {
|
|
592
792
|
const len = numbers.length;
|
|
@@ -600,21 +800,14 @@ var _ArbitraryNumber = class _ArbitraryNumber {
|
|
|
600
800
|
c *= n.coefficient;
|
|
601
801
|
e += n.exponent;
|
|
602
802
|
if (c >= 10 || c <= -10) {
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
c /= pow10(shift);
|
|
606
|
-
e += shift;
|
|
803
|
+
c /= 10;
|
|
804
|
+
e += 1;
|
|
607
805
|
}
|
|
608
806
|
}
|
|
609
|
-
return _ArbitraryNumber.
|
|
807
|
+
return _ArbitraryNumber._normalizeNew(c, e);
|
|
610
808
|
}
|
|
611
809
|
/**
|
|
612
|
-
* Returns the largest value in an array.
|
|
613
|
-
*
|
|
614
|
-
* Empty array returns {@link Zero}. Single element returned as-is.
|
|
615
|
-
*
|
|
616
|
-
* @example
|
|
617
|
-
* ArbitraryNumber.maxOfArray([an(1), an(3), an(2)]); // an(3)
|
|
810
|
+
* Returns the largest value in an array. Empty array returns `Zero`.
|
|
618
811
|
*/
|
|
619
812
|
static maxOfArray(numbers) {
|
|
620
813
|
if (numbers.length === 0) return _ArbitraryNumber.Zero;
|
|
@@ -625,12 +818,7 @@ var _ArbitraryNumber = class _ArbitraryNumber {
|
|
|
625
818
|
return max;
|
|
626
819
|
}
|
|
627
820
|
/**
|
|
628
|
-
* Returns the smallest value in an array.
|
|
629
|
-
*
|
|
630
|
-
* Empty array returns {@link Zero}. Single element returned as-is.
|
|
631
|
-
*
|
|
632
|
-
* @example
|
|
633
|
-
* ArbitraryNumber.minOfArray([an(3), an(1), an(2)]); // an(1)
|
|
821
|
+
* Returns the smallest value in an array. Empty array returns `Zero`.
|
|
634
822
|
*/
|
|
635
823
|
static minOfArray(numbers) {
|
|
636
824
|
if (numbers.length === 0) return _ArbitraryNumber.Zero;
|
|
@@ -640,167 +828,122 @@ var _ArbitraryNumber = class _ArbitraryNumber {
|
|
|
640
828
|
}
|
|
641
829
|
return min;
|
|
642
830
|
}
|
|
831
|
+
// ── Rounding (mutate) ──────────────────────────────────────────────────────
|
|
643
832
|
/**
|
|
644
|
-
*
|
|
645
|
-
*
|
|
646
|
-
* @returns `1` if `this > other`, `-1` if `this < other`, `0` if equal.
|
|
647
|
-
*
|
|
648
|
-
* @example
|
|
649
|
-
* new ArbitraryNumber(1, 4).compareTo(new ArbitraryNumber(9, 3)); // 1 (10000 > 9000)
|
|
650
|
-
* new ArbitraryNumber(-1, 4).compareTo(new ArbitraryNumber(1, 3)); // -1 (-10000 < 1000)
|
|
651
|
-
*/
|
|
652
|
-
compareTo(other) {
|
|
653
|
-
const thisNegative = this.coefficient < 0;
|
|
654
|
-
const otherNegative = other.coefficient < 0;
|
|
655
|
-
if (thisNegative !== otherNegative) {
|
|
656
|
-
return thisNegative ? -1 : 1;
|
|
657
|
-
}
|
|
658
|
-
if (this.exponent !== other.exponent) {
|
|
659
|
-
if (this.coefficient === 0) return otherNegative ? 1 : -1;
|
|
660
|
-
if (other.coefficient === 0) return thisNegative ? -1 : 1;
|
|
661
|
-
const thisExponentIsHigher = this.exponent > other.exponent;
|
|
662
|
-
return thisNegative ? thisExponentIsHigher ? -1 : 1 : thisExponentIsHigher ? 1 : -1;
|
|
663
|
-
}
|
|
664
|
-
if (this.coefficient !== other.coefficient) {
|
|
665
|
-
return this.coefficient > other.coefficient ? 1 : -1;
|
|
666
|
-
}
|
|
667
|
-
return 0;
|
|
668
|
-
}
|
|
669
|
-
/** Returns `true` if `this > other`. */
|
|
670
|
-
greaterThan(other) {
|
|
671
|
-
return this.compareTo(other) > 0;
|
|
672
|
-
}
|
|
673
|
-
/** Returns `true` if `this < other`. */
|
|
674
|
-
lessThan(other) {
|
|
675
|
-
return this.compareTo(other) < 0;
|
|
676
|
-
}
|
|
677
|
-
/** Returns `true` if `this >= other`. */
|
|
678
|
-
greaterThanOrEqual(other) {
|
|
679
|
-
return this.compareTo(other) >= 0;
|
|
680
|
-
}
|
|
681
|
-
/** Returns `true` if `this <= other`. */
|
|
682
|
-
lessThanOrEqual(other) {
|
|
683
|
-
return this.compareTo(other) <= 0;
|
|
684
|
-
}
|
|
685
|
-
/** Returns `true` if `this === other` in value. */
|
|
686
|
-
equals(other) {
|
|
687
|
-
return this.compareTo(other) === 0;
|
|
688
|
-
}
|
|
689
|
-
/**
|
|
690
|
-
* Returns the largest integer less than or equal to this number (floor toward -Infinity).
|
|
691
|
-
*
|
|
692
|
-
* Numbers with `exponent >= PrecisionCutoff` are already integers at that scale
|
|
693
|
-
* and are returned unchanged.
|
|
833
|
+
* Rounds down to the nearest integer in-place (floor toward −∞).
|
|
694
834
|
*
|
|
695
|
-
*
|
|
696
|
-
* new ArbitraryNumber(1.7, 0).floor(); // 1
|
|
697
|
-
* new ArbitraryNumber(-1.7, 0).floor(); // -2
|
|
835
|
+
* **Mutates `this`. Returns `this`.**
|
|
698
836
|
*/
|
|
699
837
|
floor() {
|
|
700
|
-
if (this.coefficient === 0)
|
|
701
|
-
|
|
702
|
-
}
|
|
703
|
-
if (this.exponent >= _ArbitraryNumber.PrecisionCutoff) {
|
|
704
|
-
return this;
|
|
705
|
-
}
|
|
838
|
+
if (this.coefficient === 0) return this;
|
|
839
|
+
if (this.exponent >= _scaleCutoff) return this;
|
|
706
840
|
if (this.exponent < 0) {
|
|
707
|
-
|
|
841
|
+
this.coefficient = this.coefficient >= 0 ? 0 : -1;
|
|
842
|
+
this.exponent = 0;
|
|
843
|
+
return this;
|
|
708
844
|
}
|
|
709
|
-
return
|
|
845
|
+
return this._normalizeInto(Math.floor(this.coefficient * pow10(this.exponent)), 0);
|
|
710
846
|
}
|
|
711
847
|
/**
|
|
712
|
-
*
|
|
713
|
-
*
|
|
714
|
-
* Numbers with `exponent >= PrecisionCutoff` are already integers at that scale
|
|
715
|
-
* and are returned unchanged.
|
|
848
|
+
* Rounds up to the nearest integer in-place (ceil toward +∞).
|
|
716
849
|
*
|
|
717
|
-
*
|
|
718
|
-
* new ArbitraryNumber(1.2, 0).ceil(); // 2
|
|
719
|
-
* new ArbitraryNumber(-1.7, 0).ceil(); // -1
|
|
850
|
+
* **Mutates `this`. Returns `this`.**
|
|
720
851
|
*/
|
|
721
852
|
ceil() {
|
|
722
|
-
if (this.coefficient === 0)
|
|
723
|
-
|
|
724
|
-
}
|
|
725
|
-
if (this.exponent >= _ArbitraryNumber.PrecisionCutoff) {
|
|
726
|
-
return this;
|
|
727
|
-
}
|
|
853
|
+
if (this.coefficient === 0) return this;
|
|
854
|
+
if (this.exponent >= _scaleCutoff) return this;
|
|
728
855
|
if (this.exponent < 0) {
|
|
729
|
-
|
|
856
|
+
const ceiled = this.coefficient > 0 ? 1 : 0;
|
|
857
|
+
this.coefficient = ceiled;
|
|
858
|
+
this.exponent = 0;
|
|
859
|
+
return this;
|
|
730
860
|
}
|
|
731
|
-
return
|
|
861
|
+
return this._normalizeInto(Math.ceil(this.coefficient * pow10(this.exponent)), 0);
|
|
732
862
|
}
|
|
733
863
|
/**
|
|
734
|
-
*
|
|
864
|
+
* Rounds to the nearest integer in-place.
|
|
735
865
|
*
|
|
736
|
-
*
|
|
737
|
-
*
|
|
866
|
+
* Uses `Math.round` semantics: half-values round toward positive infinity
|
|
867
|
+
* (`0.5 → 1`, `-0.5 → 0`). This matches JavaScript's built-in convention.
|
|
738
868
|
*
|
|
739
|
-
*
|
|
740
|
-
* @param min - Lower bound (inclusive).
|
|
741
|
-
* @param max - Upper bound (inclusive).
|
|
742
|
-
*/
|
|
743
|
-
static clamp(value, min, max) {
|
|
744
|
-
if (value.lessThan(min)) return min;
|
|
745
|
-
if (value.greaterThan(max)) return max;
|
|
746
|
-
return value;
|
|
747
|
-
}
|
|
748
|
-
/**
|
|
749
|
-
* Returns the smaller of `a` and `b`.
|
|
750
|
-
* @example ArbitraryNumber.min(a, b)
|
|
869
|
+
* **Mutates `this`. Returns `this`.**
|
|
751
870
|
*/
|
|
752
|
-
|
|
753
|
-
|
|
871
|
+
round() {
|
|
872
|
+
if (this.coefficient === 0) return this;
|
|
873
|
+
if (this.exponent >= _scaleCutoff) return this;
|
|
874
|
+
if (this.exponent < 0) {
|
|
875
|
+
if (this.exponent <= -2) {
|
|
876
|
+
this.coefficient = 0;
|
|
877
|
+
this.exponent = 0;
|
|
878
|
+
return this;
|
|
879
|
+
}
|
|
880
|
+
const rounded = Math.round(this.coefficient * 0.1) || 0;
|
|
881
|
+
this.coefficient = rounded;
|
|
882
|
+
this.exponent = 0;
|
|
883
|
+
return this;
|
|
884
|
+
}
|
|
885
|
+
return this._normalizeInto(Math.round(this.coefficient * pow10(this.exponent)), 0);
|
|
754
886
|
}
|
|
755
887
|
/**
|
|
756
|
-
*
|
|
757
|
-
*
|
|
888
|
+
* Truncates toward zero in-place.
|
|
889
|
+
*
|
|
890
|
+
* **Mutates `this`. Returns `this`.**
|
|
758
891
|
*/
|
|
759
|
-
|
|
760
|
-
|
|
892
|
+
trunc() {
|
|
893
|
+
if (this.coefficient === 0) return this;
|
|
894
|
+
if (this.exponent >= _scaleCutoff) return this;
|
|
895
|
+
if (this.exponent < 0) {
|
|
896
|
+
this.coefficient = 0;
|
|
897
|
+
this.exponent = 0;
|
|
898
|
+
return this;
|
|
899
|
+
}
|
|
900
|
+
return this._normalizeInto(Math.trunc(this.coefficient * pow10(this.exponent)), 0);
|
|
761
901
|
}
|
|
902
|
+
// ── Roots & logs ───────────────────────────────────────────────────────────
|
|
762
903
|
/**
|
|
763
|
-
*
|
|
904
|
+
* Returns √this in-place.
|
|
764
905
|
*
|
|
765
|
-
*
|
|
766
|
-
* `t = 0` returns `a`; `t = 1` returns `b`.
|
|
906
|
+
* **Mutates `this`. Returns `this`.**
|
|
767
907
|
*
|
|
768
|
-
* @
|
|
769
|
-
* @example
|
|
770
|
-
* ArbitraryNumber.lerp(an(100), an(200), 0.5); // 150
|
|
908
|
+
* @throws `"Square root of negative number"` when this is negative.
|
|
771
909
|
*/
|
|
772
|
-
|
|
773
|
-
if (
|
|
774
|
-
|
|
775
|
-
|
|
910
|
+
sqrt() {
|
|
911
|
+
if (this.coefficient < 0) {
|
|
912
|
+
throw new ArbitraryNumberDomainError("Square root of negative number", { value: this.toNumber() });
|
|
913
|
+
}
|
|
914
|
+
if (this.coefficient === 0) return this;
|
|
915
|
+
if (this.exponent % 2 !== 0) {
|
|
916
|
+
this.coefficient = Math.sqrt(this.coefficient * 10);
|
|
917
|
+
this.exponent = (this.exponent - 1) / 2;
|
|
918
|
+
} else {
|
|
919
|
+
this.coefficient = Math.sqrt(this.coefficient);
|
|
920
|
+
this.exponent = this.exponent / 2;
|
|
921
|
+
}
|
|
922
|
+
return this;
|
|
776
923
|
}
|
|
777
924
|
/**
|
|
778
|
-
*
|
|
779
|
-
*
|
|
780
|
-
* Useful when one section of code needs different precision than the rest.
|
|
925
|
+
* Returns ∛this in-place.
|
|
781
926
|
*
|
|
782
|
-
*
|
|
783
|
-
* // Run financial calculation with higher precision
|
|
784
|
-
* const result = ArbitraryNumber.withPrecision(50, () => a.add(b));
|
|
927
|
+
* **Mutates `this`. Returns `this`.**
|
|
785
928
|
*/
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
929
|
+
cbrt() {
|
|
930
|
+
if (this.coefficient === 0) return this;
|
|
931
|
+
const rem = (this.exponent % 3 + 3) % 3;
|
|
932
|
+
const baseExp = (this.exponent - rem) / 3;
|
|
933
|
+
if (rem === 0) {
|
|
934
|
+
this.coefficient = Math.cbrt(this.coefficient);
|
|
935
|
+
this.exponent = baseExp;
|
|
936
|
+
} else if (rem === 1) {
|
|
937
|
+
this.coefficient = Math.cbrt(this.coefficient * 10);
|
|
938
|
+
this.exponent = baseExp;
|
|
939
|
+
} else {
|
|
940
|
+
this.coefficient = Math.cbrt(this.coefficient * 100);
|
|
941
|
+
this.exponent = baseExp;
|
|
793
942
|
}
|
|
943
|
+
return this;
|
|
794
944
|
}
|
|
795
945
|
/**
|
|
796
|
-
* Returns `log10(this)` as a plain
|
|
797
|
-
*
|
|
798
|
-
* Because the number is stored as `c * 10^e`, this is computed exactly as
|
|
799
|
-
* `log10(c) + e` - no precision loss from the exponent.
|
|
800
|
-
*
|
|
801
|
-
* @example
|
|
802
|
-
* new ArbitraryNumber(1, 6).log10(); // 6
|
|
803
|
-
* new ArbitraryNumber(1.5, 3).log10(); // log10(1.5) + 3 ~ 3.176
|
|
946
|
+
* Returns `log10(this)` as a plain `number`.
|
|
804
947
|
*
|
|
805
948
|
* @throws `"Logarithm of zero is undefined"` when this is zero.
|
|
806
949
|
* @throws `"Logarithm of a negative number is undefined"` when this is negative.
|
|
@@ -815,61 +958,10 @@ var _ArbitraryNumber = class _ArbitraryNumber {
|
|
|
815
958
|
return Math.log10(this.coefficient) + this.exponent;
|
|
816
959
|
}
|
|
817
960
|
/**
|
|
818
|
-
* Returns
|
|
961
|
+
* Returns `log_base(this)` as a plain `number`.
|
|
819
962
|
*
|
|
820
|
-
*
|
|
821
|
-
*
|
|
822
|
-
* For odd exponents: `sqrt(c * 10) * 10^((e-1)/2)`.
|
|
823
|
-
*
|
|
824
|
-
* @throws `"Square root of negative number"` when this is negative.
|
|
825
|
-
* @example
|
|
826
|
-
* new ArbitraryNumber(4, 0).sqrt(); // 2
|
|
827
|
-
* new ArbitraryNumber(1, 4).sqrt(); // 1*10^2 (= 100)
|
|
828
|
-
*/
|
|
829
|
-
sqrt() {
|
|
830
|
-
if (this.coefficient < 0) throw new ArbitraryNumberDomainError("Square root of negative number", { value: this.toNumber() });
|
|
831
|
-
if (this.coefficient === 0) return _ArbitraryNumber.Zero;
|
|
832
|
-
if (this.exponent % 2 !== 0) {
|
|
833
|
-
return _ArbitraryNumber.createNormalized(Math.sqrt(this.coefficient * 10), (this.exponent - 1) / 2);
|
|
834
|
-
}
|
|
835
|
-
return _ArbitraryNumber.createNormalized(Math.sqrt(this.coefficient), this.exponent / 2);
|
|
836
|
-
}
|
|
837
|
-
/**
|
|
838
|
-
* Returns the cube root of this number: `∛this`.
|
|
839
|
-
*
|
|
840
|
-
* Computed without `Math.log10`. For exponents divisible by 3: `cbrt(c) * 10^(e/3)`.
|
|
841
|
-
* For remainder 1: `cbrt(c * 10) * 10^((e-1)/3)`.
|
|
842
|
-
* For remainder 2: `cbrt(c * 100) * 10^((e-2)/3)`.
|
|
843
|
-
*
|
|
844
|
-
* Supports negative numbers (cube root of a negative is negative).
|
|
845
|
-
*
|
|
846
|
-
* @example
|
|
847
|
-
* new ArbitraryNumber(8, 0).cbrt(); // 2
|
|
848
|
-
* new ArbitraryNumber(1, 9).cbrt(); // 1*10^3 (= 1,000)
|
|
849
|
-
* new ArbitraryNumber(-8, 0).cbrt(); // -2
|
|
850
|
-
*/
|
|
851
|
-
cbrt() {
|
|
852
|
-
if (this.coefficient === 0) return _ArbitraryNumber.Zero;
|
|
853
|
-
const rem = (this.exponent % 3 + 3) % 3;
|
|
854
|
-
const baseExp = (this.exponent - rem) / 3;
|
|
855
|
-
if (rem === 0) return _ArbitraryNumber.createNormalized(Math.cbrt(this.coefficient), baseExp);
|
|
856
|
-
if (rem === 1) return _ArbitraryNumber.createNormalized(Math.cbrt(this.coefficient * 10), baseExp);
|
|
857
|
-
return _ArbitraryNumber.createNormalized(Math.cbrt(this.coefficient * 100), baseExp);
|
|
858
|
-
}
|
|
859
|
-
/**
|
|
860
|
-
* Returns `log_base(this)` as a plain JavaScript `number`.
|
|
861
|
-
*
|
|
862
|
-
* Computed as `log10(this) / log10(base)`. The numerator is exact due to the
|
|
863
|
-
* stored `coefficient × 10^exponent` form.
|
|
864
|
-
*
|
|
865
|
-
* @param base - The logarithm base. Must be positive and not 1.
|
|
866
|
-
* @throws `"Logarithm of zero is undefined"` when this is zero.
|
|
867
|
-
* @throws `"Logarithm of a negative number is undefined"` when this is negative.
|
|
868
|
-
* @throws `"Logarithm base must be positive and not 1"` for invalid base.
|
|
869
|
-
*
|
|
870
|
-
* @example
|
|
871
|
-
* new ArbitraryNumber(8, 0).log(2); // 3
|
|
872
|
-
* new ArbitraryNumber(1, 6).log(10); // 6
|
|
963
|
+
* @param base - Must be positive and not 1.
|
|
964
|
+
* @throws `"Logarithm base must be positive and not 1"` for invalid base.
|
|
873
965
|
*/
|
|
874
966
|
log(base) {
|
|
875
967
|
if (base <= 0 || base === 1 || !isFinite(base)) {
|
|
@@ -878,28 +970,13 @@ var _ArbitraryNumber = class _ArbitraryNumber {
|
|
|
878
970
|
return this.log10() / Math.log10(base);
|
|
879
971
|
}
|
|
880
972
|
/**
|
|
881
|
-
* Returns `ln(this)`
|
|
882
|
-
*
|
|
883
|
-
* Computed as `log10(this) / log10(e)`.
|
|
884
|
-
*
|
|
885
|
-
* @throws `"Logarithm of zero is undefined"` when this is zero.
|
|
886
|
-
* @throws `"Logarithm of a negative number is undefined"` when this is negative.
|
|
887
|
-
*
|
|
888
|
-
* @example
|
|
889
|
-
* new ArbitraryNumber(1, 0).ln(); // 0
|
|
973
|
+
* Returns `ln(this)` as a plain `number`.
|
|
890
974
|
*/
|
|
891
975
|
ln() {
|
|
892
976
|
return this.log10() / Math.LOG10E;
|
|
893
977
|
}
|
|
894
978
|
/**
|
|
895
|
-
* Returns `10^n` as
|
|
896
|
-
*
|
|
897
|
-
* This is the inverse of {@link log10}. Useful for converting a log10 result
|
|
898
|
-
* back into an `ArbitraryNumber`.
|
|
899
|
-
*
|
|
900
|
-
* @example
|
|
901
|
-
* ArbitraryNumber.exp10(6); // 1*10^6 (= 1,000,000)
|
|
902
|
-
* ArbitraryNumber.exp10(3.5); // 10^3.5 ≈ 3162.3
|
|
979
|
+
* Returns `10^n` as a new `ArbitraryNumber`.
|
|
903
980
|
*
|
|
904
981
|
* @throws `"ArbitraryNumber.exp10: n must be finite"` for non-finite `n`.
|
|
905
982
|
*/
|
|
@@ -909,70 +986,139 @@ var _ArbitraryNumber = class _ArbitraryNumber {
|
|
|
909
986
|
}
|
|
910
987
|
const intPart = Math.floor(n);
|
|
911
988
|
const fracPart = n - intPart;
|
|
912
|
-
|
|
913
|
-
return _ArbitraryNumber.createNormalized(c, intPart);
|
|
989
|
+
return _ArbitraryNumber.unsafe(Math.pow(10, fracPart), intPart);
|
|
914
990
|
}
|
|
991
|
+
// ── Comparisons (read-only) ────────────────────────────────────────────────
|
|
915
992
|
/**
|
|
916
|
-
*
|
|
917
|
-
*
|
|
918
|
-
* Numbers with `exponent >= PrecisionCutoff` are already integers at that scale
|
|
919
|
-
* and are returned unchanged.
|
|
993
|
+
* Compares this number to `other`.
|
|
920
994
|
*
|
|
921
|
-
* @
|
|
922
|
-
* new ArbitraryNumber(1.5, 0).round(); // 2
|
|
923
|
-
* new ArbitraryNumber(1.4, 0).round(); // 1
|
|
924
|
-
* new ArbitraryNumber(-1.5, 0).round(); // -1 (half-up toward positive infinity)
|
|
995
|
+
* @returns `1` if `this > other`, `-1` if `this < other`, `0` if equal.
|
|
925
996
|
*/
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
if (
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
997
|
+
compareTo(other) {
|
|
998
|
+
const thisNegative = this.coefficient < 0;
|
|
999
|
+
const otherNegative = other.coefficient < 0;
|
|
1000
|
+
if (thisNegative !== otherNegative) {
|
|
1001
|
+
return thisNegative ? -1 : 1;
|
|
1002
|
+
}
|
|
1003
|
+
if (this.exponent !== other.exponent) {
|
|
1004
|
+
if (this.coefficient === 0) return otherNegative ? 1 : -1;
|
|
1005
|
+
if (other.coefficient === 0) return thisNegative ? -1 : 1;
|
|
1006
|
+
const thisExpHigher = this.exponent > other.exponent;
|
|
1007
|
+
return thisNegative ? thisExpHigher ? -1 : 1 : thisExpHigher ? 1 : -1;
|
|
1008
|
+
}
|
|
1009
|
+
if (this.coefficient !== other.coefficient) {
|
|
1010
|
+
return this.coefficient > other.coefficient ? 1 : -1;
|
|
933
1011
|
}
|
|
934
|
-
return
|
|
1012
|
+
return 0;
|
|
1013
|
+
}
|
|
1014
|
+
/** Returns `true` if `this > other`. */
|
|
1015
|
+
greaterThan(other) {
|
|
1016
|
+
return this.compareTo(other) > 0;
|
|
1017
|
+
}
|
|
1018
|
+
/** Returns `true` if `this < other`. */
|
|
1019
|
+
lessThan(other) {
|
|
1020
|
+
return this.compareTo(other) < 0;
|
|
1021
|
+
}
|
|
1022
|
+
/** Returns `true` if `this >= other`. */
|
|
1023
|
+
greaterThanOrEqual(other) {
|
|
1024
|
+
return this.compareTo(other) >= 0;
|
|
1025
|
+
}
|
|
1026
|
+
/** Returns `true` if `this <= other`. */
|
|
1027
|
+
lessThanOrEqual(other) {
|
|
1028
|
+
return this.compareTo(other) <= 0;
|
|
1029
|
+
}
|
|
1030
|
+
/** Returns `true` if `this === other` in value. */
|
|
1031
|
+
equals(other) {
|
|
1032
|
+
return this.compareTo(other) === 0;
|
|
935
1033
|
}
|
|
1034
|
+
// ── Clamp / min / max (static, allocate) ──────────────────────────────────
|
|
936
1035
|
/**
|
|
937
|
-
*
|
|
938
|
-
*
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
1036
|
+
* Clamps `value` to the inclusive range `[min, max]`. Returns one of the three
|
|
1037
|
+
* inputs (no allocation).
|
|
1038
|
+
*/
|
|
1039
|
+
static clamp(value, min, max) {
|
|
1040
|
+
if (value.lessThan(min)) return min;
|
|
1041
|
+
if (value.greaterThan(max)) return max;
|
|
1042
|
+
return value;
|
|
1043
|
+
}
|
|
1044
|
+
/** Returns the smaller of `a` and `b`. */
|
|
1045
|
+
static min(a, b) {
|
|
1046
|
+
return a.lessThan(b) ? a : b;
|
|
1047
|
+
}
|
|
1048
|
+
/** Returns the larger of `a` and `b`. */
|
|
1049
|
+
static max(a, b) {
|
|
1050
|
+
return a.greaterThan(b) ? a : b;
|
|
1051
|
+
}
|
|
1052
|
+
/**
|
|
1053
|
+
* Linear interpolation: `a + (b - a) * t`.
|
|
944
1054
|
*
|
|
945
|
-
*
|
|
946
|
-
*
|
|
947
|
-
* new ArbitraryNumber(-1.7, 0).trunc(); // -1
|
|
1055
|
+
* Returns `a` unchanged when `t === 0`, `b` unchanged when `t === 1`.
|
|
1056
|
+
* All other values of `t` allocate and return a fresh instance.
|
|
948
1057
|
*/
|
|
949
|
-
|
|
950
|
-
if (
|
|
951
|
-
if (
|
|
952
|
-
|
|
953
|
-
|
|
1058
|
+
static lerp(a, b, t) {
|
|
1059
|
+
if (t === 0) return a;
|
|
1060
|
+
if (t === 1) return b;
|
|
1061
|
+
return a.clone().add(b.clone().sub(a).mul(_ArbitraryNumber.from(t)));
|
|
1062
|
+
}
|
|
1063
|
+
/**
|
|
1064
|
+
* Runs `fn` with `defaults.scaleCutoff` temporarily set to `cutoff`, then restores it.
|
|
1065
|
+
*/
|
|
1066
|
+
static withPrecision(cutoff, fn) {
|
|
1067
|
+
const prev = _ArbitraryNumber.defaults.scaleCutoff;
|
|
1068
|
+
_ArbitraryNumber.defaults.scaleCutoff = cutoff;
|
|
1069
|
+
_scaleCutoff = cutoff;
|
|
1070
|
+
try {
|
|
1071
|
+
return fn();
|
|
1072
|
+
} finally {
|
|
1073
|
+
_ArbitraryNumber.defaults.scaleCutoff = prev;
|
|
1074
|
+
_scaleCutoff = prev;
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
// ── Predicates (read-only) ─────────────────────────────────────────────────
|
|
1078
|
+
/** Returns `true` when this number is zero. */
|
|
1079
|
+
isZero() {
|
|
1080
|
+
return this.coefficient === 0;
|
|
1081
|
+
}
|
|
1082
|
+
/** Returns `true` when this number is strictly positive. */
|
|
1083
|
+
isPositive() {
|
|
1084
|
+
return this.coefficient > 0;
|
|
1085
|
+
}
|
|
1086
|
+
/** Returns `true` when this number is strictly negative. */
|
|
1087
|
+
isNegative() {
|
|
1088
|
+
return this.coefficient < 0;
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* Returns `true` when this number has no fractional part.
|
|
1092
|
+
* Numbers with `exponent >= scaleCutoff` are always considered integers.
|
|
1093
|
+
*/
|
|
1094
|
+
isInteger() {
|
|
1095
|
+
if (this.coefficient === 0) return true;
|
|
1096
|
+
if (this.exponent >= _scaleCutoff) return true;
|
|
1097
|
+
if (this.exponent < 0) return false;
|
|
1098
|
+
return Number.isInteger(this.coefficient * pow10(this.exponent));
|
|
954
1099
|
}
|
|
955
1100
|
/**
|
|
956
1101
|
* Returns `1` if positive, `-1` if negative, `0` if zero.
|
|
957
|
-
*
|
|
958
|
-
* @example
|
|
959
|
-
* new ArbitraryNumber(1.5, 3).sign(); // 1
|
|
960
|
-
* new ArbitraryNumber(-1.5, 3).sign(); // -1
|
|
961
|
-
* ArbitraryNumber.Zero.sign(); // 0
|
|
962
1102
|
*/
|
|
963
1103
|
sign() {
|
|
964
1104
|
return Math.sign(this.coefficient);
|
|
965
1105
|
}
|
|
1106
|
+
// ── Freeze ─────────────────────────────────────────────────────────────────
|
|
966
1107
|
/**
|
|
967
|
-
*
|
|
1108
|
+
* Returns a `FrozenArbitraryNumber` wrapping the same value.
|
|
968
1109
|
*
|
|
969
|
-
*
|
|
970
|
-
*
|
|
971
|
-
|
|
1110
|
+
* Mutating methods on the frozen instance throw `ArbitraryNumberMutationError`.
|
|
1111
|
+
* Call `.clone()` on the frozen instance to get a fresh, mutable copy.
|
|
1112
|
+
*/
|
|
1113
|
+
freeze() {
|
|
1114
|
+
return new FrozenArbitraryNumber(this.coefficient, this.exponent);
|
|
1115
|
+
}
|
|
1116
|
+
// ── Serialization ──────────────────────────────────────────────────────────
|
|
1117
|
+
/**
|
|
1118
|
+
* Converts to a plain JavaScript `number`.
|
|
972
1119
|
*
|
|
973
|
-
*
|
|
974
|
-
*
|
|
975
|
-
* new ArbitraryNumber(1, 400).toNumber(); // Infinity
|
|
1120
|
+
* Returns `Infinity` for exponents beyond float64 range (>=308).
|
|
1121
|
+
* Returns `0` for exponents below float64 range (<=-324).
|
|
976
1122
|
*/
|
|
977
1123
|
toNumber() {
|
|
978
1124
|
return this.coefficient * pow10(this.exponent);
|
|
@@ -980,37 +1126,25 @@ var _ArbitraryNumber = class _ArbitraryNumber {
|
|
|
980
1126
|
/**
|
|
981
1127
|
* Returns a stable, minimal JSON representation: `{ c: number, e: number }`.
|
|
982
1128
|
*
|
|
983
|
-
* The keys `c` and `e` are intentionally short to keep save-game blobs small.
|
|
984
|
-
* Reconstruct via {@link ArbitraryNumber.fromJSON}.
|
|
985
|
-
*
|
|
986
1129
|
* Round-trip guarantee: `ArbitraryNumber.fromJSON(x.toJSON()).equals(x)` is always `true`.
|
|
987
|
-
*
|
|
988
|
-
* @example
|
|
989
|
-
* JSON.stringify(an(1.5, 6)); // '{"c":1.5,"e":6}'
|
|
990
1130
|
*/
|
|
991
1131
|
toJSON() {
|
|
992
1132
|
return { c: this.coefficient, e: this.exponent };
|
|
993
1133
|
}
|
|
994
1134
|
/**
|
|
995
|
-
* Returns a
|
|
1135
|
+
* Returns a compact string representation: `"<coefficient>|<exponent>"`.
|
|
996
1136
|
*
|
|
997
|
-
*
|
|
1137
|
+
* Shorter than JSON for save-game serialisation. Reconstruct via `ArbitraryNumber.parse`.
|
|
998
1138
|
*
|
|
999
1139
|
* @example
|
|
1000
|
-
* an(
|
|
1001
|
-
* an(-2, 6).toRaw(); // "-2|6"
|
|
1140
|
+
* an(1500).toRawString() // "1.5|3"
|
|
1002
1141
|
*/
|
|
1003
|
-
|
|
1142
|
+
toRawString() {
|
|
1004
1143
|
return `${this.coefficient}|${this.exponent}`;
|
|
1005
1144
|
}
|
|
1006
1145
|
/**
|
|
1007
1146
|
* Reconstructs an `ArbitraryNumber` from a `toJSON()` blob.
|
|
1008
1147
|
*
|
|
1009
|
-
* @example
|
|
1010
|
-
* const n = an(1.5, 6);
|
|
1011
|
-
* ArbitraryNumber.fromJSON(n.toJSON()).equals(n); // true
|
|
1012
|
-
*
|
|
1013
|
-
* @param obj - Object with numeric `c` (coefficient) and `e` (exponent) fields.
|
|
1014
1148
|
* @throws `ArbitraryNumberInputError` when the object shape is invalid or values are non-finite.
|
|
1015
1149
|
*/
|
|
1016
1150
|
static fromJSON(obj) {
|
|
@@ -1027,22 +1161,17 @@ var _ArbitraryNumber = class _ArbitraryNumber {
|
|
|
1027
1161
|
if (!isFinite(e)) {
|
|
1028
1162
|
throw new ArbitraryNumberInputError("ArbitraryNumber.fromJSON: e must be finite", e);
|
|
1029
1163
|
}
|
|
1030
|
-
return _ArbitraryNumber.
|
|
1164
|
+
return _ArbitraryNumber._normalizeNew(c, e);
|
|
1031
1165
|
}
|
|
1032
1166
|
/**
|
|
1033
1167
|
* Parses a string into an `ArbitraryNumber`.
|
|
1034
1168
|
*
|
|
1035
1169
|
* Accepted formats:
|
|
1036
|
-
* - Raw pipe format
|
|
1037
|
-
* -
|
|
1170
|
+
* - Raw pipe format: `"1.5|3"`, `"-2.5|-6"`
|
|
1171
|
+
* - Scientific notation: `"1.5e+3"`, `"1.5E3"`
|
|
1038
1172
|
* - Plain decimal: `"1500"`, `"-0.003"`, `"0"`
|
|
1039
1173
|
*
|
|
1040
|
-
* @
|
|
1041
|
-
* ArbitraryNumber.parse("1.5|3"); // same as an(1.5, 3)
|
|
1042
|
-
* ArbitraryNumber.parse("1.5e+3"); // same as ArbitraryNumber.from(1500)
|
|
1043
|
-
* ArbitraryNumber.parse("1500"); // same as ArbitraryNumber.from(1500)
|
|
1044
|
-
*
|
|
1045
|
-
* @throws `ArbitraryNumberInputError` when the string cannot be parsed or produces a non-finite value.
|
|
1174
|
+
* @throws `ArbitraryNumberInputError` for invalid or non-finite input.
|
|
1046
1175
|
*/
|
|
1047
1176
|
static parse(s) {
|
|
1048
1177
|
if (typeof s !== "string" || s.trim() === "") {
|
|
@@ -1058,7 +1187,7 @@ var _ArbitraryNumber = class _ArbitraryNumber {
|
|
|
1058
1187
|
if (!isFinite(c) || !isFinite(e) || cStr === "" || eStr === "") {
|
|
1059
1188
|
throw new ArbitraryNumberInputError("ArbitraryNumber.parse: invalid pipe format", s);
|
|
1060
1189
|
}
|
|
1061
|
-
return _ArbitraryNumber.
|
|
1190
|
+
return _ArbitraryNumber._normalizeNew(c, e);
|
|
1062
1191
|
}
|
|
1063
1192
|
const n = Number(trimmed);
|
|
1064
1193
|
if (!isFinite(n)) {
|
|
@@ -1066,197 +1195,124 @@ var _ArbitraryNumber = class _ArbitraryNumber {
|
|
|
1066
1195
|
}
|
|
1067
1196
|
return new _ArbitraryNumber(n, 0);
|
|
1068
1197
|
}
|
|
1069
|
-
|
|
1070
|
-
isZero() {
|
|
1071
|
-
return this.coefficient === 0;
|
|
1072
|
-
}
|
|
1073
|
-
/** Returns `true` when this number is strictly positive. */
|
|
1074
|
-
isPositive() {
|
|
1075
|
-
return this.coefficient > 0;
|
|
1076
|
-
}
|
|
1077
|
-
/** Returns `true` when this number is strictly negative. */
|
|
1078
|
-
isNegative() {
|
|
1079
|
-
return this.coefficient < 0;
|
|
1080
|
-
}
|
|
1081
|
-
/**
|
|
1082
|
-
* Returns `true` when this number has no fractional part.
|
|
1083
|
-
* Numbers with `exponent >= PrecisionCutoff` are always considered integers.
|
|
1084
|
-
*/
|
|
1085
|
-
isInteger() {
|
|
1086
|
-
if (this.coefficient === 0) return true;
|
|
1087
|
-
if (this.exponent >= _ArbitraryNumber.PrecisionCutoff) return true;
|
|
1088
|
-
if (this.exponent < 0) return false;
|
|
1089
|
-
return Number.isInteger(this.coefficient * pow10(this.exponent));
|
|
1090
|
-
}
|
|
1198
|
+
// ── String coercion ────────────────────────────────────────────────────────
|
|
1091
1199
|
/**
|
|
1092
1200
|
* Allows implicit coercion via `+an(1500)` (returns `toNumber()`) and
|
|
1093
1201
|
* template literals / string concatenation (returns `toString()`).
|
|
1094
|
-
*
|
|
1095
|
-
* `hint === "number"` → `toNumber()`; all other hints → `toString()`.
|
|
1096
1202
|
*/
|
|
1097
1203
|
[Symbol.toPrimitive](hint) {
|
|
1098
1204
|
return hint === "number" ? this.toNumber() : this.toString();
|
|
1099
1205
|
}
|
|
1100
|
-
/**
|
|
1101
|
-
* Custom Node.js inspect output so `console.log(an(1500))` renders `"1.50e+3"`
|
|
1102
|
-
* instead of the raw object representation.
|
|
1103
|
-
*/
|
|
1206
|
+
/** Custom Node.js inspect output so `console.log(an(1500))` renders `"1.50e+3"`. */
|
|
1104
1207
|
[/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")]() {
|
|
1105
1208
|
return this.toString();
|
|
1106
1209
|
}
|
|
1107
1210
|
/**
|
|
1108
1211
|
* Formats this number as a string using the given notation plugin.
|
|
1109
1212
|
*
|
|
1110
|
-
* Defaults to
|
|
1111
|
-
* `decimals` controls
|
|
1112
|
-
*
|
|
1113
|
-
* @example
|
|
1114
|
-
* new ArbitraryNumber(1.5, 3).toString(); // "1.50e+3"
|
|
1115
|
-
* new ArbitraryNumber(1.5, 3).toString(unitNotation); // "1.50 K"
|
|
1116
|
-
* new ArbitraryNumber(1.5, 3).toString(unitNotation, 4); // "1.5000 K"
|
|
1117
|
-
*
|
|
1118
|
-
* @param notation - The formatting plugin to use.
|
|
1119
|
-
* @param decimals - Number of decimal places to render. Defaults to `2`.
|
|
1213
|
+
* Defaults to `scientificNotation` when no plugin is provided.
|
|
1214
|
+
* `decimals` controls decimal places and defaults to `defaults.notationDecimals`.
|
|
1120
1215
|
*/
|
|
1121
|
-
toString(notation = scientificNotation, decimals =
|
|
1216
|
+
toString(notation = scientificNotation, decimals = _ArbitraryNumber.defaults.notationDecimals) {
|
|
1122
1217
|
return notation.format(this.coefficient, this.exponent, decimals);
|
|
1123
1218
|
}
|
|
1124
1219
|
};
|
|
1125
|
-
/**
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
*
|
|
1132
|
-
* Default: 15 (matches float64 coefficient precision of ~15.95 significant digits).
|
|
1133
|
-
* Game patterns: diffs 0-8 (exact), prestige 15-25 (loss <0.0001%), idle 20-50 (~0.1% loss).
|
|
1134
|
-
*
|
|
1135
|
-
* Override globally via assignment, or use {@link withPrecision} for a scoped block.
|
|
1136
|
-
*/
|
|
1137
|
-
_ArbitraryNumber.PrecisionCutoff = 15;
|
|
1138
|
-
/** The additive identity: `0`. */
|
|
1220
|
+
/** Global tunables. Mutating these is a process-level change. */
|
|
1221
|
+
_ArbitraryNumber.defaults = {
|
|
1222
|
+
scaleCutoff: 15,
|
|
1223
|
+
notationDecimals: 2
|
|
1224
|
+
};
|
|
1225
|
+
/** The additive identity: `0`. Frozen — calling mutating methods throws. */
|
|
1139
1226
|
_ArbitraryNumber.Zero = new _ArbitraryNumber(0, 0);
|
|
1140
|
-
/** The multiplicative identity: `1`. */
|
|
1227
|
+
/** The multiplicative identity: `1`. Frozen — calling mutating methods throws. */
|
|
1141
1228
|
_ArbitraryNumber.One = new _ArbitraryNumber(1, 0);
|
|
1142
|
-
/** `10`. */
|
|
1229
|
+
/** `10`. Frozen — calling mutating methods throws. */
|
|
1143
1230
|
_ArbitraryNumber.Ten = new _ArbitraryNumber(1, 1);
|
|
1144
1231
|
var ArbitraryNumber = _ArbitraryNumber;
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
// src/core/AnChain.ts
|
|
1152
|
-
var AnChain = class _AnChain {
|
|
1153
|
-
constructor(start) {
|
|
1154
|
-
this.value = start;
|
|
1232
|
+
var FrozenArbitraryNumber = class extends ArbitraryNumber {
|
|
1233
|
+
/** @internal */
|
|
1234
|
+
constructor(coefficient, exponent) {
|
|
1235
|
+
super(0, 0);
|
|
1236
|
+
this.coefficient = coefficient;
|
|
1237
|
+
this.exponent = exponent;
|
|
1155
1238
|
}
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
return new _AnChain(ArbitraryNumber.from(value));
|
|
1239
|
+
_throwMutation(method) {
|
|
1240
|
+
throw new ArbitraryNumberMutationError(
|
|
1241
|
+
`Cannot call mutating method '${method}' on a frozen ArbitraryNumber`
|
|
1242
|
+
);
|
|
1161
1243
|
}
|
|
1162
|
-
//
|
|
1163
|
-
|
|
1164
|
-
add(
|
|
1165
|
-
this.
|
|
1166
|
-
return this;
|
|
1244
|
+
// Every public mutating method on ArbitraryNumber must have an override here.
|
|
1245
|
+
// If you add a new mutating method above, add a matching override below.
|
|
1246
|
+
add(_other) {
|
|
1247
|
+
this._throwMutation("add");
|
|
1167
1248
|
}
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
this.value = this.value.sub(other);
|
|
1171
|
-
return this;
|
|
1249
|
+
sub(_other) {
|
|
1250
|
+
this._throwMutation("sub");
|
|
1172
1251
|
}
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
this.value = this.value.mul(other);
|
|
1176
|
-
return this;
|
|
1252
|
+
mul(_other) {
|
|
1253
|
+
this._throwMutation("mul");
|
|
1177
1254
|
}
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
this.value = this.value.div(other);
|
|
1181
|
-
return this;
|
|
1255
|
+
div(_other) {
|
|
1256
|
+
this._throwMutation("div");
|
|
1182
1257
|
}
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
this.value = this.value.pow(exp);
|
|
1186
|
-
return this;
|
|
1258
|
+
negate() {
|
|
1259
|
+
this._throwMutation("negate");
|
|
1187
1260
|
}
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
mulAdd(mult, add) {
|
|
1191
|
-
this.value = this.value.mulAdd(mult, add);
|
|
1192
|
-
return this;
|
|
1261
|
+
abs() {
|
|
1262
|
+
this._throwMutation("abs");
|
|
1193
1263
|
}
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
this.value = this.value.addMul(add, mult);
|
|
1197
|
-
return this;
|
|
1264
|
+
pow(_n) {
|
|
1265
|
+
this._throwMutation("pow");
|
|
1198
1266
|
}
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
this.value = this.value.mulSub(mult, sub);
|
|
1202
|
-
return this;
|
|
1267
|
+
mulAdd(_m, _a) {
|
|
1268
|
+
this._throwMutation("mulAdd");
|
|
1203
1269
|
}
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
this.value = this.value.subMul(sub, mult);
|
|
1207
|
-
return this;
|
|
1270
|
+
addMul(_a, _m) {
|
|
1271
|
+
this._throwMutation("addMul");
|
|
1208
1272
|
}
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
this.value = this.value.divAdd(div, add);
|
|
1212
|
-
return this;
|
|
1273
|
+
mulSub(_m, _s) {
|
|
1274
|
+
this._throwMutation("mulSub");
|
|
1213
1275
|
}
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
abs() {
|
|
1217
|
-
this.value = this.value.abs();
|
|
1218
|
-
return this;
|
|
1276
|
+
subMul(_s, _m) {
|
|
1277
|
+
this._throwMutation("subMul");
|
|
1219
1278
|
}
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
this.value = this.value.negate();
|
|
1223
|
-
return this;
|
|
1279
|
+
divAdd(_d, _a) {
|
|
1280
|
+
this._throwMutation("divAdd");
|
|
1224
1281
|
}
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
this.value = this.value.sqrt();
|
|
1228
|
-
return this;
|
|
1282
|
+
mulDiv(_m, _d) {
|
|
1283
|
+
this._throwMutation("mulDiv");
|
|
1229
1284
|
}
|
|
1230
|
-
/** Rounds down to the nearest integer. */
|
|
1231
1285
|
floor() {
|
|
1232
|
-
this.
|
|
1233
|
-
return this;
|
|
1286
|
+
this._throwMutation("floor");
|
|
1234
1287
|
}
|
|
1235
|
-
/** Rounds up to the nearest integer. */
|
|
1236
1288
|
ceil() {
|
|
1237
|
-
this.
|
|
1238
|
-
return this;
|
|
1289
|
+
this._throwMutation("ceil");
|
|
1239
1290
|
}
|
|
1240
|
-
/** Rounds to the nearest integer. */
|
|
1241
1291
|
round() {
|
|
1242
|
-
this.
|
|
1243
|
-
|
|
1292
|
+
this._throwMutation("round");
|
|
1293
|
+
}
|
|
1294
|
+
trunc() {
|
|
1295
|
+
this._throwMutation("trunc");
|
|
1296
|
+
}
|
|
1297
|
+
sqrt() {
|
|
1298
|
+
this._throwMutation("sqrt");
|
|
1244
1299
|
}
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
done() {
|
|
1248
|
-
return this.value;
|
|
1300
|
+
cbrt() {
|
|
1301
|
+
this._throwMutation("cbrt");
|
|
1249
1302
|
}
|
|
1250
1303
|
};
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1304
|
+
ArbitraryNumber.Zero = new FrozenArbitraryNumber(0, 0);
|
|
1305
|
+
ArbitraryNumber.One = new FrozenArbitraryNumber(1, 0);
|
|
1306
|
+
ArbitraryNumber.Ten = new FrozenArbitraryNumber(1, 1);
|
|
1307
|
+
|
|
1308
|
+
// src/core/an.ts
|
|
1309
|
+
var createAn = ((coefficient, exponent = 0) => new ArbitraryNumber(coefficient, exponent));
|
|
1310
|
+
createAn.from = (value) => ArbitraryNumber.from(value);
|
|
1311
|
+
var an = createAn;
|
|
1254
1312
|
|
|
1255
1313
|
// src/core/AnFormula.ts
|
|
1256
1314
|
var AnFormula = class _AnFormula {
|
|
1257
|
-
/**
|
|
1258
|
-
* Prefer the {@link formula} factory function over calling this directly.
|
|
1259
|
-
*/
|
|
1315
|
+
/** Prefer the {@link formula} factory function over calling this directly. */
|
|
1260
1316
|
constructor(name, steps = []) {
|
|
1261
1317
|
this._name = name;
|
|
1262
1318
|
this.steps = steps;
|
|
@@ -1267,18 +1323,10 @@ var AnFormula = class _AnFormula {
|
|
|
1267
1323
|
}
|
|
1268
1324
|
/**
|
|
1269
1325
|
* Returns a copy of this formula with a new name, leaving the original unchanged.
|
|
1270
|
-
*
|
|
1271
|
-
* @param name - The new name.
|
|
1272
|
-
* @example
|
|
1273
|
-
* const base = formula().mul(an(2));
|
|
1274
|
-
* const named = base.named("Double");
|
|
1275
|
-
* named.name // "Double"
|
|
1276
|
-
* base.name // undefined
|
|
1277
1326
|
*/
|
|
1278
1327
|
named(name) {
|
|
1279
1328
|
return new _AnFormula(name, this.steps);
|
|
1280
1329
|
}
|
|
1281
|
-
// ── Private helper ────────────────────────────────────────────────────────
|
|
1282
1330
|
step(fn) {
|
|
1283
1331
|
return new _AnFormula(this._name, [...this.steps, fn]);
|
|
1284
1332
|
}
|
|
@@ -1303,7 +1351,7 @@ var AnFormula = class _AnFormula {
|
|
|
1303
1351
|
pow(exp) {
|
|
1304
1352
|
return this.step((v) => v.pow(exp));
|
|
1305
1353
|
}
|
|
1306
|
-
// ── Fused
|
|
1354
|
+
// ── Fused ─────────────────────────────────────────────────────────────────
|
|
1307
1355
|
/** Appends `(value * mult) + add` to the pipeline. */
|
|
1308
1356
|
mulAdd(mult, add) {
|
|
1309
1357
|
return this.step((v) => v.mulAdd(mult, add));
|
|
@@ -1329,7 +1377,7 @@ var AnFormula = class _AnFormula {
|
|
|
1329
1377
|
abs() {
|
|
1330
1378
|
return this.step((v) => v.abs());
|
|
1331
1379
|
}
|
|
1332
|
-
/** Appends `
|
|
1380
|
+
/** Appends `negate()` to the pipeline. */
|
|
1333
1381
|
neg() {
|
|
1334
1382
|
return this.step((v) => v.negate());
|
|
1335
1383
|
}
|
|
@@ -1354,34 +1402,39 @@ var AnFormula = class _AnFormula {
|
|
|
1354
1402
|
* Returns a new formula that first applies `this`, then applies `next`.
|
|
1355
1403
|
*
|
|
1356
1404
|
* Neither operand is mutated.
|
|
1357
|
-
*
|
|
1358
|
-
* @param next - The formula to apply after `this`.
|
|
1359
|
-
* @example
|
|
1360
|
-
* const full = armorReduction.then(critBonus);
|
|
1361
|
-
* const result = full.apply(baseDamage);
|
|
1362
1405
|
*/
|
|
1363
1406
|
then(next) {
|
|
1364
1407
|
return new _AnFormula(this._name, [...this.steps, ...next.steps]);
|
|
1365
1408
|
}
|
|
1366
1409
|
// ── Terminal ──────────────────────────────────────────────────────────────
|
|
1367
1410
|
/**
|
|
1368
|
-
*
|
|
1411
|
+
* Clones `value` once, runs the pipeline against the clone, and returns it.
|
|
1369
1412
|
*
|
|
1370
|
-
* The
|
|
1413
|
+
* The original `value` is never mutated.
|
|
1371
1414
|
*
|
|
1372
|
-
* @param value - The starting value. Plain `number` is coerced via `ArbitraryNumber.from`.
|
|
1373
|
-
* @throws `"ArbitraryNumber.from: value must be finite"` when a plain `number` is non-finite.
|
|
1374
1415
|
* @example
|
|
1375
|
-
* const damage = damageFormula.apply(
|
|
1376
|
-
* const scaled = damageFormula.apply(boostedBase);
|
|
1416
|
+
* const damage = damageFormula.apply(playerAtk); // playerAtk unchanged
|
|
1377
1417
|
*/
|
|
1378
1418
|
apply(value) {
|
|
1379
|
-
|
|
1419
|
+
const result = value instanceof ArbitraryNumber ? value.clone() : ArbitraryNumber.from(value);
|
|
1380
1420
|
for (const step of this.steps) {
|
|
1381
|
-
|
|
1421
|
+
step(result);
|
|
1382
1422
|
}
|
|
1383
1423
|
return result;
|
|
1384
1424
|
}
|
|
1425
|
+
/**
|
|
1426
|
+
* Runs the pipeline directly against `value`, mutating it in-place.
|
|
1427
|
+
*
|
|
1428
|
+
* Use on hot paths where you don't need to preserve the original value.
|
|
1429
|
+
*
|
|
1430
|
+
* @example
|
|
1431
|
+
* damageFormula.applyInPlace(enemyAtk); // enemyAtk is mutated
|
|
1432
|
+
*/
|
|
1433
|
+
applyInPlace(value) {
|
|
1434
|
+
for (const step of this.steps) {
|
|
1435
|
+
step(value);
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1385
1438
|
};
|
|
1386
1439
|
function formula(name) {
|
|
1387
1440
|
return new AnFormula(name, []);
|
|
@@ -1636,19 +1689,13 @@ var ArbitraryNumberGuard = class _ArbitraryNumberGuard {
|
|
|
1636
1689
|
return obj instanceof ArbitraryNumber;
|
|
1637
1690
|
}
|
|
1638
1691
|
/**
|
|
1639
|
-
* Returns `true` if `obj` has the shape
|
|
1640
|
-
* (i.e. has numeric `coefficient` and `exponent` properties).
|
|
1692
|
+
* Returns `true` if `obj` has the shape `{ coefficient: number; exponent: number }`.
|
|
1641
1693
|
*
|
|
1642
|
-
*
|
|
1643
|
-
*
|
|
1644
|
-
* to distinguish between the two.
|
|
1645
|
-
*
|
|
1646
|
-
* @param obj - The value to test.
|
|
1647
|
-
* @returns `true` when `obj` has `typeof coefficient === "number"` and
|
|
1648
|
-
* `typeof exponent === "number"`.
|
|
1694
|
+
* Both `ArbitraryNumber` instances and plain objects with the right shape pass this
|
|
1695
|
+
* check. Use {@link isArbitraryNumber} to distinguish the two.
|
|
1649
1696
|
*/
|
|
1650
1697
|
static isNormalizedNumber(obj) {
|
|
1651
|
-
return obj != null && typeof obj
|
|
1698
|
+
return obj != null && typeof obj.coefficient === "number" && typeof obj.exponent === "number";
|
|
1652
1699
|
}
|
|
1653
1700
|
/**
|
|
1654
1701
|
* Returns `true` if `obj` is an {@link ArbitraryNumber} with a value of zero.
|
|
@@ -1661,124 +1708,14 @@ var ArbitraryNumberGuard = class _ArbitraryNumberGuard {
|
|
|
1661
1708
|
}
|
|
1662
1709
|
};
|
|
1663
1710
|
|
|
1664
|
-
// src/utility/ArbitraryNumberOps.ts
|
|
1665
|
-
var ArbitraryNumberOps = class _ArbitraryNumberOps {
|
|
1666
|
-
/**
|
|
1667
|
-
* Converts `value` to an `ArbitraryNumber`, returning it unchanged if it already is one.
|
|
1668
|
-
*
|
|
1669
|
-
* @param value - A plain `number` or an existing `ArbitraryNumber`.
|
|
1670
|
-
* @returns The corresponding `ArbitraryNumber`.
|
|
1671
|
-
* @throws `ArbitraryNumberInputError` when `value` is `NaN`, `Infinity`, or `-Infinity`.
|
|
1672
|
-
*/
|
|
1673
|
-
static from(value) {
|
|
1674
|
-
return value instanceof ArbitraryNumber ? value : ArbitraryNumber.from(value);
|
|
1675
|
-
}
|
|
1676
|
-
/**
|
|
1677
|
-
* Converts `value` to an `ArbitraryNumber`, returning `null` for non-finite inputs
|
|
1678
|
-
* instead of throwing.
|
|
1679
|
-
*
|
|
1680
|
-
* Use this at system boundaries (form inputs, external APIs) where you want to handle
|
|
1681
|
-
* bad input gracefully rather than catching an exception.
|
|
1682
|
-
*
|
|
1683
|
-
* @param value - A plain `number` or an existing `ArbitraryNumber`.
|
|
1684
|
-
* @returns The `ArbitraryNumber`, or `null` if the input is `NaN`, `Infinity`, or `-Infinity`.
|
|
1685
|
-
*
|
|
1686
|
-
* @example
|
|
1687
|
-
* ops.tryFrom(1500) // ArbitraryNumber { coefficient: 1.5, exponent: 3 }
|
|
1688
|
-
* ops.tryFrom(Infinity) // null
|
|
1689
|
-
* ops.tryFrom(NaN) // null
|
|
1690
|
-
*/
|
|
1691
|
-
static tryFrom(value) {
|
|
1692
|
-
if (value instanceof ArbitraryNumber) return value;
|
|
1693
|
-
if (!isFinite(value)) return null;
|
|
1694
|
-
return ArbitraryNumber.from(value);
|
|
1695
|
-
}
|
|
1696
|
-
/**
|
|
1697
|
-
* Returns `left + right`, coercing both operands as needed.
|
|
1698
|
-
*
|
|
1699
|
-
* @param left - The augend.
|
|
1700
|
-
* @param right - The addend.
|
|
1701
|
-
* @example
|
|
1702
|
-
* ops.add(1500, 2500) // ArbitraryNumber (4000)
|
|
1703
|
-
*/
|
|
1704
|
-
static add(left, right) {
|
|
1705
|
-
return _ArbitraryNumberOps.from(left).add(_ArbitraryNumberOps.from(right));
|
|
1706
|
-
}
|
|
1707
|
-
/**
|
|
1708
|
-
* Returns `left - right`, coercing both operands as needed.
|
|
1709
|
-
*
|
|
1710
|
-
* @param left - The minuend.
|
|
1711
|
-
* @param right - The subtrahend.
|
|
1712
|
-
* @example
|
|
1713
|
-
* ops.sub(5000, 1500) // ArbitraryNumber (3500)
|
|
1714
|
-
*/
|
|
1715
|
-
static sub(left, right) {
|
|
1716
|
-
return _ArbitraryNumberOps.from(left).sub(_ArbitraryNumberOps.from(right));
|
|
1717
|
-
}
|
|
1718
|
-
/**
|
|
1719
|
-
* Returns `left * right`, coercing both operands as needed.
|
|
1720
|
-
*
|
|
1721
|
-
* @param left - The multiplicand.
|
|
1722
|
-
* @param right - The multiplier.
|
|
1723
|
-
* @example
|
|
1724
|
-
* ops.mul(an(1, 3), 5) // ArbitraryNumber (5000)
|
|
1725
|
-
*/
|
|
1726
|
-
static mul(left, right) {
|
|
1727
|
-
return _ArbitraryNumberOps.from(left).mul(_ArbitraryNumberOps.from(right));
|
|
1728
|
-
}
|
|
1729
|
-
/**
|
|
1730
|
-
* Returns `left / right`, coercing both operands as needed.
|
|
1731
|
-
*
|
|
1732
|
-
* @param left - The dividend.
|
|
1733
|
-
* @param right - The divisor.
|
|
1734
|
-
* @throws `"Division by zero"` when `right` is zero.
|
|
1735
|
-
* @example
|
|
1736
|
-
* ops.div(an(1, 6), 1000) // ArbitraryNumber (1000)
|
|
1737
|
-
*/
|
|
1738
|
-
static div(left, right) {
|
|
1739
|
-
return _ArbitraryNumberOps.from(left).div(_ArbitraryNumberOps.from(right));
|
|
1740
|
-
}
|
|
1741
|
-
/**
|
|
1742
|
-
* Compares `left` to `right`.
|
|
1743
|
-
*
|
|
1744
|
-
* @param left - The left operand.
|
|
1745
|
-
* @param right - The right operand.
|
|
1746
|
-
* @returns `1` if `left > right`, `-1` if `left < right`, `0` if equal.
|
|
1747
|
-
* @example
|
|
1748
|
-
* ops.compare(5000, 1500) // 1
|
|
1749
|
-
*/
|
|
1750
|
-
static compare(left, right) {
|
|
1751
|
-
return _ArbitraryNumberOps.from(left).compareTo(_ArbitraryNumberOps.from(right));
|
|
1752
|
-
}
|
|
1753
|
-
/**
|
|
1754
|
-
* Clamps `value` to the inclusive range `[min, max]`, coercing all inputs as needed.
|
|
1755
|
-
*
|
|
1756
|
-
* @param value - The value to clamp.
|
|
1757
|
-
* @param min - The lower bound (inclusive).
|
|
1758
|
-
* @param max - The upper bound (inclusive).
|
|
1759
|
-
* @example
|
|
1760
|
-
* ops.clamp(500, 1000, 2000) // ArbitraryNumber (1000) - below min, returns min
|
|
1761
|
-
*/
|
|
1762
|
-
static clamp(value, min, max) {
|
|
1763
|
-
return ArbitraryNumber.clamp(
|
|
1764
|
-
_ArbitraryNumberOps.from(value),
|
|
1765
|
-
_ArbitraryNumberOps.from(min),
|
|
1766
|
-
_ArbitraryNumberOps.from(max)
|
|
1767
|
-
);
|
|
1768
|
-
}
|
|
1769
|
-
};
|
|
1770
|
-
|
|
1771
1711
|
// src/utility/ArbitraryNumberHelpers.ts
|
|
1772
1712
|
var ArbitraryNumberHelpers = class _ArbitraryNumberHelpers {
|
|
1773
1713
|
static coerce(value) {
|
|
1774
|
-
return
|
|
1714
|
+
return value instanceof ArbitraryNumber ? value : ArbitraryNumber.from(value);
|
|
1775
1715
|
}
|
|
1776
1716
|
/**
|
|
1777
1717
|
* Returns `true` when `value >= threshold`.
|
|
1778
1718
|
*
|
|
1779
|
-
* @param value - The value to test.
|
|
1780
|
-
* @param threshold - The minimum required value.
|
|
1781
|
-
* @returns `true` when `value >= threshold`.
|
|
1782
1719
|
* @example
|
|
1783
1720
|
* ArbitraryNumberHelpers.meetsOrExceeds(gold, upgradeCost)
|
|
1784
1721
|
*/
|
|
@@ -1790,9 +1727,6 @@ var ArbitraryNumberHelpers = class _ArbitraryNumberHelpers {
|
|
|
1790
1727
|
*
|
|
1791
1728
|
* Equivalent to `floor(total / step)`.
|
|
1792
1729
|
*
|
|
1793
|
-
* @param total - The total available amount.
|
|
1794
|
-
* @param step - The cost or size of one unit. Must be greater than zero.
|
|
1795
|
-
* @returns The number of whole units that fit, as an `ArbitraryNumber`.
|
|
1796
1730
|
* @throws `"step must be greater than zero"` when `step <= 0`.
|
|
1797
1731
|
* @example
|
|
1798
1732
|
* const canBuy = ArbitraryNumberHelpers.wholeMultipleCount(gold, upgradeCost);
|
|
@@ -1806,15 +1740,11 @@ var ArbitraryNumberHelpers = class _ArbitraryNumberHelpers {
|
|
|
1806
1740
|
if (numTotal.lessThanOrEqual(ArbitraryNumber.Zero)) {
|
|
1807
1741
|
return ArbitraryNumber.Zero;
|
|
1808
1742
|
}
|
|
1809
|
-
return numTotal.div(numStep).floor();
|
|
1743
|
+
return numTotal.clone().div(numStep).floor();
|
|
1810
1744
|
}
|
|
1811
1745
|
/**
|
|
1812
1746
|
* Returns `value - delta`, clamped to a minimum of `floor` (default `0`).
|
|
1813
1747
|
*
|
|
1814
|
-
* @param value - The starting value.
|
|
1815
|
-
* @param delta - The amount to subtract.
|
|
1816
|
-
* @param floor - The minimum result. Defaults to `ArbitraryNumber.Zero`.
|
|
1817
|
-
* @returns `max(value - delta, floor)`.
|
|
1818
1748
|
* @example
|
|
1819
1749
|
* health = ArbitraryNumberHelpers.subtractWithFloor(health, damage);
|
|
1820
1750
|
*/
|
|
@@ -1822,13 +1752,12 @@ var ArbitraryNumberHelpers = class _ArbitraryNumberHelpers {
|
|
|
1822
1752
|
const numValue = _ArbitraryNumberHelpers.coerce(value);
|
|
1823
1753
|
const numDelta = _ArbitraryNumberHelpers.coerce(delta);
|
|
1824
1754
|
const numFloor = _ArbitraryNumberHelpers.coerce(floor);
|
|
1825
|
-
const result = numValue.sub(numDelta);
|
|
1755
|
+
const result = numValue.clone().sub(numDelta);
|
|
1826
1756
|
return result.lessThan(numFloor) ? numFloor : result;
|
|
1827
1757
|
}
|
|
1828
1758
|
};
|
|
1829
1759
|
|
|
1830
1760
|
exports.AlphabetNotation = AlphabetNotation;
|
|
1831
|
-
exports.AnChain = AnChain;
|
|
1832
1761
|
exports.AnFormula = AnFormula;
|
|
1833
1762
|
exports.ArbitraryNumber = ArbitraryNumber;
|
|
1834
1763
|
exports.ArbitraryNumberDomainError = ArbitraryNumberDomainError;
|
|
@@ -1836,20 +1765,19 @@ exports.ArbitraryNumberError = ArbitraryNumberError;
|
|
|
1836
1765
|
exports.ArbitraryNumberGuard = ArbitraryNumberGuard;
|
|
1837
1766
|
exports.ArbitraryNumberHelpers = ArbitraryNumberHelpers;
|
|
1838
1767
|
exports.ArbitraryNumberInputError = ArbitraryNumberInputError;
|
|
1839
|
-
exports.
|
|
1768
|
+
exports.ArbitraryNumberMutationError = ArbitraryNumberMutationError;
|
|
1840
1769
|
exports.CLASSIC_UNITS = CLASSIC_UNITS;
|
|
1841
1770
|
exports.COMPACT_UNITS = COMPACT_UNITS;
|
|
1771
|
+
exports.FrozenArbitraryNumber = FrozenArbitraryNumber;
|
|
1842
1772
|
exports.ScientificNotation = ScientificNotation;
|
|
1843
1773
|
exports.SuffixNotationBase = SuffixNotationBase;
|
|
1844
1774
|
exports.UnitNotation = UnitNotation;
|
|
1845
1775
|
exports.alphabetSuffix = alphabetSuffix;
|
|
1846
1776
|
exports.an = an;
|
|
1847
|
-
exports.chain = chain;
|
|
1848
1777
|
exports.formula = formula;
|
|
1849
1778
|
exports.guard = ArbitraryNumberGuard;
|
|
1850
1779
|
exports.helpers = ArbitraryNumberHelpers;
|
|
1851
1780
|
exports.letterNotation = letterNotation;
|
|
1852
|
-
exports.ops = ArbitraryNumberOps;
|
|
1853
1781
|
exports.scientificNotation = scientificNotation;
|
|
1854
1782
|
exports.unitNotation = unitNotation;
|
|
1855
1783
|
//# sourceMappingURL=index.cjs.map
|