atomic-fns 1.2.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.
Files changed (216) hide show
  1. package/README.md +397 -91
  2. package/dist/array/index.d.ts +9 -0
  3. package/dist/array/index.js +9 -0
  4. package/dist/array/list.d.ts +99 -0
  5. package/dist/array/list.js +133 -0
  6. package/dist/array/sets.d.ts +130 -0
  7. package/dist/array/sets.js +213 -0
  8. package/dist/async/index.d.ts +6 -0
  9. package/dist/async/index.js +6 -0
  10. package/dist/async/retry.d.ts +38 -0
  11. package/dist/async/retry.js +56 -0
  12. package/dist/async/sleep.d.ts +42 -0
  13. package/dist/async/sleep.js +76 -0
  14. package/dist/collections/bisect.d.ts +103 -0
  15. package/dist/collections/bisect.js +157 -0
  16. package/dist/collections/deque.d.ts +67 -97
  17. package/dist/collections/deque.js +197 -256
  18. package/dist/collections/entries.d.ts +89 -0
  19. package/dist/collections/entries.js +131 -0
  20. package/dist/collections/frozen-set.d.ts +42 -0
  21. package/dist/collections/frozen-set.js +64 -0
  22. package/dist/collections/group.d.ts +141 -0
  23. package/dist/collections/group.js +241 -0
  24. package/dist/collections/heap.d.ts +64 -0
  25. package/dist/collections/heap.js +205 -0
  26. package/dist/collections/index.d.ts +15 -623
  27. package/dist/collections/index.js +14 -844
  28. package/dist/collections/internal/arrayKeys.d.ts +19 -0
  29. package/dist/collections/internal/arrayKeys.js +83 -0
  30. package/dist/collections/internal/invariants.d.ts +4 -0
  31. package/dist/collections/internal/invariants.js +12 -0
  32. package/dist/collections/internal/iterable.d.ts +25 -0
  33. package/dist/collections/internal/iterable.js +62 -0
  34. package/dist/collections/iterate.d.ts +162 -0
  35. package/dist/collections/iterate.js +188 -0
  36. package/dist/collections/lru-cache.d.ts +58 -0
  37. package/dist/collections/lru-cache.js +164 -0
  38. package/dist/collections/order.d.ts +19 -0
  39. package/dist/collections/order.js +38 -0
  40. package/dist/collections/shape.d.ts +32 -0
  41. package/dist/collections/shape.js +86 -0
  42. package/dist/collections/sorted-map.d.ts +171 -0
  43. package/dist/collections/sorted-map.js +845 -0
  44. package/dist/collections/sorted-set.d.ts +103 -0
  45. package/dist/collections/sorted-set.js +147 -0
  46. package/dist/collections/splay-tree.d.ts +82 -0
  47. package/dist/collections/splay-tree.js +354 -0
  48. package/dist/collections/trie.d.ts +58 -0
  49. package/dist/collections/trie.js +196 -0
  50. package/dist/decimal/currency.d.ts +211 -0
  51. package/dist/decimal/currency.js +321 -0
  52. package/dist/decimal/decimal.d.ts +299 -0
  53. package/dist/decimal/decimal.js +660 -0
  54. package/dist/decimal/index.d.ts +11 -148
  55. package/dist/decimal/index.js +10 -410
  56. package/dist/decimal/internal/bits.d.ts +2 -0
  57. package/dist/decimal/internal/bits.js +7 -0
  58. package/dist/decimal/internal/echo.d.ts +4 -0
  59. package/dist/decimal/internal/echo.js +6 -0
  60. package/dist/decimal/internal/isqrt.d.ts +9 -0
  61. package/dist/decimal/internal/isqrt.js +34 -0
  62. package/dist/decimal/internal/locale.d.ts +7 -0
  63. package/dist/decimal/internal/locale.js +24 -0
  64. package/dist/decimal/internal/minor-units.d.ts +2 -0
  65. package/dist/decimal/internal/minor-units.js +33 -0
  66. package/dist/decimal/internal/pow10.d.ts +24 -0
  67. package/dist/decimal/internal/pow10.js +96 -0
  68. package/dist/encoding/base64.d.ts +39 -0
  69. package/dist/encoding/base64.js +53 -0
  70. package/dist/encoding/hex.d.ts +23 -0
  71. package/dist/encoding/hex.js +34 -0
  72. package/dist/encoding/index.d.ts +6 -0
  73. package/dist/encoding/index.js +6 -0
  74. package/dist/function/debounce.d.ts +68 -0
  75. package/dist/function/debounce.js +203 -0
  76. package/dist/function/index.d.ts +6 -0
  77. package/dist/function/index.js +6 -0
  78. package/dist/function/shared.d.ts +2 -0
  79. package/dist/function/shared.js +5 -0
  80. package/dist/function/wrap.d.ts +130 -0
  81. package/dist/function/wrap.js +113 -0
  82. package/dist/hash/digest.d.ts +18 -0
  83. package/dist/hash/digest.js +26 -0
  84. package/dist/hash/hash.d.ts +22 -0
  85. package/dist/hash/hash.js +252 -0
  86. package/dist/hash/hashString.d.ts +13 -0
  87. package/dist/hash/hashString.js +53 -0
  88. package/dist/hash/index.d.ts +8 -0
  89. package/dist/hash/index.js +8 -0
  90. package/dist/hash/stableStringify.d.ts +19 -0
  91. package/dist/hash/stableStringify.js +102 -0
  92. package/dist/id/index.d.ts +7 -0
  93. package/dist/id/index.js +7 -0
  94. package/dist/id/nanoid.d.ts +16 -0
  95. package/dist/id/nanoid.js +65 -0
  96. package/dist/id/uniqueId.d.ts +14 -0
  97. package/dist/id/uniqueId.js +20 -0
  98. package/dist/id/uuid.d.ts +25 -0
  99. package/dist/id/uuid.js +64 -0
  100. package/dist/index.d.ts +15 -8
  101. package/dist/index.js +15 -8
  102. package/dist/internal/bytes.d.ts +11 -0
  103. package/dist/internal/bytes.js +33 -0
  104. package/dist/internal/codec.d.ts +11 -0
  105. package/dist/internal/codec.js +84 -0
  106. package/dist/internal/compare.d.ts +10 -0
  107. package/dist/internal/compare.js +26 -0
  108. package/dist/internal/crypto.d.ts +5 -0
  109. package/dist/internal/crypto.js +11 -0
  110. package/dist/internal/keys.d.ts +12 -0
  111. package/dist/internal/keys.js +23 -0
  112. package/dist/internal/kind.d.ts +44 -0
  113. package/dist/internal/kind.js +139 -0
  114. package/dist/internal/sparse.d.ts +51 -0
  115. package/dist/internal/sparse.js +104 -0
  116. package/dist/internal/text.d.ts +7 -0
  117. package/dist/internal/text.js +15 -0
  118. package/dist/internal/timer.d.ts +10 -0
  119. package/dist/internal/timer.js +17 -0
  120. package/dist/internal/truncate.d.ts +10 -0
  121. package/dist/internal/truncate.js +22 -0
  122. package/dist/internal/walk.d.ts +47 -0
  123. package/dist/internal/walk.js +100 -0
  124. package/dist/is/index.d.ts +8 -0
  125. package/dist/is/index.js +8 -0
  126. package/dist/is/isArrayLike.d.ts +18 -0
  127. package/dist/is/isArrayLike.js +25 -0
  128. package/dist/is/isError.d.ts +13 -0
  129. package/dist/is/isError.js +18 -0
  130. package/dist/is/isNil.d.ts +12 -0
  131. package/dist/is/isNil.js +14 -0
  132. package/dist/is/isPlainObject.d.ts +15 -0
  133. package/dist/is/isPlainObject.js +34 -0
  134. package/dist/number/index.d.ts +7 -0
  135. package/dist/number/index.js +7 -0
  136. package/dist/number/round.d.ts +86 -0
  137. package/dist/number/round.js +130 -0
  138. package/dist/number/stats.d.ts +52 -0
  139. package/dist/number/stats.js +115 -0
  140. package/dist/object/cloneDeep.d.ts +28 -0
  141. package/dist/object/cloneDeep.js +200 -0
  142. package/dist/object/index.d.ts +14 -0
  143. package/dist/object/index.js +13 -0
  144. package/dist/object/isEqual.d.ts +64 -0
  145. package/dist/object/isEqual.js +289 -0
  146. package/dist/object/matches.d.ts +40 -0
  147. package/dist/object/matches.js +284 -0
  148. package/dist/object/merge.d.ts +66 -0
  149. package/dist/object/merge.js +272 -0
  150. package/dist/object/parsePath.d.ts +22 -0
  151. package/dist/object/parsePath.js +111 -0
  152. package/dist/object/path.d.ts +85 -0
  153. package/dist/object/path.js +208 -0
  154. package/dist/object/pick.d.ts +91 -0
  155. package/dist/object/pick.js +180 -0
  156. package/dist/object/reshape.d.ts +73 -0
  157. package/dist/object/reshape.js +144 -0
  158. package/dist/random/index.d.ts +96 -32
  159. package/dist/random/index.js +215 -53
  160. package/dist/string/case.d.ts +71 -0
  161. package/dist/string/case.js +152 -0
  162. package/dist/string/fold.d.ts +35 -0
  163. package/dist/string/fold.js +100 -0
  164. package/dist/string/index.d.ts +6 -187
  165. package/dist/string/index.js +6 -224
  166. package/dist/string/truncate.d.ts +25 -0
  167. package/dist/string/truncate.js +44 -0
  168. package/dist/string/words.d.ts +27 -0
  169. package/dist/string/words.js +241 -0
  170. package/package.json +99 -51
  171. package/dist/collections/BTree.d.ts +0 -230
  172. package/dist/collections/BTree.js +0 -1143
  173. package/dist/collections/Heap.d.ts +0 -69
  174. package/dist/collections/Heap.js +0 -181
  175. package/dist/collections/LRUCache.d.ts +0 -83
  176. package/dist/collections/LRUCache.js +0 -139
  177. package/dist/collections/SortedMap.d.ts +0 -92
  178. package/dist/collections/SortedMap.js +0 -131
  179. package/dist/collections/SortedSet.d.ts +0 -69
  180. package/dist/collections/SortedSet.js +0 -99
  181. package/dist/collections/SortedTree.d.ts +0 -103
  182. package/dist/collections/SortedTree.js +0 -142
  183. package/dist/collections/SplayTree.d.ts +0 -107
  184. package/dist/collections/SplayTree.js +0 -351
  185. package/dist/collections/Trie.d.ts +0 -42
  186. package/dist/collections/Trie.js +0 -104
  187. package/dist/collections/abc.d.ts +0 -152
  188. package/dist/collections/abc.js +0 -39
  189. package/dist/collections/frozenset.d.ts +0 -19
  190. package/dist/collections/frozenset.js +0 -33
  191. package/dist/functools/index.d.ts +0 -106
  192. package/dist/functools/index.js +0 -203
  193. package/dist/globals/index.d.ts +0 -268
  194. package/dist/globals/index.js +0 -447
  195. package/dist/i18n/currency.d.ts +0 -27
  196. package/dist/i18n/currency.js +0 -100
  197. package/dist/i18n/date/index.d.ts +0 -210
  198. package/dist/i18n/date/index.js +0 -552
  199. package/dist/i18n/date/utils.d.ts +0 -64
  200. package/dist/i18n/date/utils.js +0 -192
  201. package/dist/i18n/duration.d.ts +0 -113
  202. package/dist/i18n/duration.js +0 -398
  203. package/dist/i18n/format.d.ts +0 -34
  204. package/dist/i18n/format.js +0 -378
  205. package/dist/i18n/index.d.ts +0 -9
  206. package/dist/i18n/index.js +0 -8
  207. package/dist/itertools/index.d.ts +0 -251
  208. package/dist/itertools/index.js +0 -434
  209. package/dist/math/index.d.ts +0 -79
  210. package/dist/math/index.js +0 -81
  211. package/dist/mod.d.ts +0 -1
  212. package/dist/mod.js +0 -1
  213. package/dist/operators/index.d.ts +0 -148
  214. package/dist/operators/index.js +0 -325
  215. package/dist/types.d.ts +0 -19
  216. package/dist/types.js +0 -1
@@ -0,0 +1,660 @@
1
+ import { bitLength } from './internal/bits.js';
2
+ import { quoteForError } from './internal/echo.js';
3
+ import { isqrtRoot } from './internal/isqrt.js';
4
+ import { canonicalLocales } from './internal/locale.js';
5
+ import { pow10, pow10Squares } from './internal/pow10.js';
6
+ /** Every {@link RoundingMode}, keyed so the type check fails when the two lists drift apart. */
7
+ const ROUNDING_MODES = {
8
+ ceil: true,
9
+ floor: true,
10
+ expand: true,
11
+ trunc: true,
12
+ halfCeil: true,
13
+ halfFloor: true,
14
+ halfExpand: true,
15
+ halfTrunc: true,
16
+ halfEven: true
17
+ };
18
+ /** Significant digits {@link Decimal.divide} and {@link Decimal.sqrt} keep by default. */
19
+ const DEFAULT_PRECISION = 34;
20
+ const MAX_EXPONENT = 10_000;
21
+ const MAX_PRECISION = 10_000;
22
+ const MAX_DIGITS = 10_000;
23
+ const MAX_INPUT_LENGTH = 2 * MAX_EXPONENT + MAX_DIGITS + 32;
24
+ /** No Decimal reaches `10^MAX_VALUE_DIGITS`. */
25
+ const MAX_VALUE_DIGITS = MAX_DIGITS + MAX_EXPONENT;
26
+ /** Zeros {@link Decimal.toString} pads a coefficient with before it turns scientific. */
27
+ const MAX_PLAIN_PADDING = 64;
28
+ /**
29
+ * Unambiguous decimal grammar: a mandatory digit run or dot separates the two whitespace runs and no
30
+ * two optional digit runs overlap, so matching is linear.
31
+ */
32
+ const PARSE_LEADING_DIGITS = /^\s*([+-]?)(\d+)(?:\.(\d*))?(?:e([+-]?\d+))?\s*$/i;
33
+ const PARSE_LEADING_DOT = /^\s*([+-]?)\.(\d+)(?:e([+-]?\d+))?\s*$/i;
34
+ function absBig(n) {
35
+ return n < 0n ? -n : n;
36
+ }
37
+ /** Strips trailing decimal zeros with bulk divides by cached powers of ten. */
38
+ function normalize(i, e) {
39
+ if (i === 0n)
40
+ return { i: 0n, e: 0 };
41
+ if (i % 10n !== 0n)
42
+ return { i, e };
43
+ let x = i;
44
+ let zeros = 0;
45
+ const powers = pow10Squares();
46
+ for (let k = powers.length - 1; k >= 0; k--) {
47
+ const pk = powers[k];
48
+ const step = 1 << k;
49
+ while (x % pk === 0n) {
50
+ x /= pk;
51
+ zeros += step;
52
+ }
53
+ }
54
+ return { i: x, e: e + zeros };
55
+ }
56
+ /**
57
+ * Digit count of a non-zero absolute bigint from its bit length and one compare against a power
58
+ * of ten, without a base-10 conversion. Saturates past {@link MAX_VALUE_DIGITS}, where the value
59
+ * is already longer than every Decimal.
60
+ */
61
+ function digitLengthAbs(a) {
62
+ if (a < 10n)
63
+ return 1;
64
+ const estimate = Math.floor(bitLength(a) * Math.LOG10E * Math.LN2) + 1;
65
+ const digits = Math.min(estimate, MAX_VALUE_DIGITS + 2);
66
+ const bound = pow10(digits - 1);
67
+ if (a >= bound * 10n)
68
+ return digits + 1;
69
+ if (a < bound)
70
+ return digits - 1;
71
+ return digits;
72
+ }
73
+ function digitLength(n) {
74
+ return n === 0n ? 0 : digitLengthAbs(absBig(n));
75
+ }
76
+ /** The order of two values with the same sign whose magnitudes differ. */
77
+ function orderBySign(smallerMagnitude, negative) {
78
+ return smallerMagnitude === negative ? 1 : -1;
79
+ }
80
+ /**
81
+ * Orders `a.i * 10^a.e` and `b.i * 10^b.e`: sign first, then order of magnitude, and only when
82
+ * those tie does it scale one coefficient, by a gap no wider than the digit-count difference.
83
+ */
84
+ function compareIE(a, b) {
85
+ if (a.i === 0n && b.i === 0n)
86
+ return 0;
87
+ if (a.i === 0n)
88
+ return b.i < 0n ? 1 : -1;
89
+ if (b.i === 0n)
90
+ return a.i < 0n ? -1 : 1;
91
+ const negative = a.i < 0n;
92
+ if (negative !== b.i < 0n)
93
+ return negative ? -1 : 1;
94
+ const aMag = a.e + a.digits - 1;
95
+ const bMag = b.e + b.digits - 1;
96
+ if (aMag !== bMag)
97
+ return orderBySign(aMag < bMag, negative);
98
+ const aAbs = absBig(a.i);
99
+ const bAbs = absBig(b.i);
100
+ const left = a.e > b.e ? aAbs * pow10(a.e - b.e) : aAbs;
101
+ const right = b.e > a.e ? bAbs * pow10(b.e - a.e) : bAbs;
102
+ if (left === right)
103
+ return 0;
104
+ return orderBySign(left < right, negative);
105
+ }
106
+ /** `n / d` rounded to an integer by `mode`. */
107
+ function roundQuotient(n, d, mode) {
108
+ const q = n / d;
109
+ const r = n % d;
110
+ if (r === 0n)
111
+ return q;
112
+ const sign = r < 0n !== d < 0n ? -1n : 1n;
113
+ const twice = 2n * absBig(r);
114
+ const ad = absBig(d);
115
+ const half = twice < ad ? -1 : twice > ad ? 1 : 0;
116
+ let away;
117
+ switch (mode) {
118
+ case 'ceil':
119
+ away = sign > 0n;
120
+ break;
121
+ case 'floor':
122
+ away = sign < 0n;
123
+ break;
124
+ case 'expand':
125
+ away = true;
126
+ break;
127
+ case 'trunc':
128
+ away = false;
129
+ break;
130
+ case 'halfCeil':
131
+ away = half > 0 || (half === 0 && sign > 0n);
132
+ break;
133
+ case 'halfFloor':
134
+ away = half > 0 || (half === 0 && sign < 0n);
135
+ break;
136
+ case 'halfExpand':
137
+ away = half >= 0;
138
+ break;
139
+ case 'halfTrunc':
140
+ away = half > 0;
141
+ break;
142
+ case 'halfEven':
143
+ away = half > 0 || (half === 0 && q % 2n !== 0n);
144
+ break;
145
+ }
146
+ return away ? q + sign : q;
147
+ }
148
+ function assertRoundingMode(mode) {
149
+ if (typeof mode !== 'string' || !Object.hasOwn(ROUNDING_MODES, mode)) {
150
+ throw new RangeError(`roundingMode must be one of ${Object.keys(ROUNDING_MODES).join(', ')}`);
151
+ }
152
+ }
153
+ function assertOptions(options) {
154
+ if (options !== undefined && Object(options) !== options) {
155
+ throw new TypeError('options must be an object');
156
+ }
157
+ }
158
+ function assertIntegerIn(name, value, least, most) {
159
+ if (!Number.isInteger(value) || value < least || value > most) {
160
+ throw new RangeError(`${name} must be an integer from ${String(least)} to ${String(most)}`);
161
+ }
162
+ }
163
+ function assertDigitsBound(digits) {
164
+ if (digits > MAX_DIGITS)
165
+ throw new RangeError(`result exceeds ${String(MAX_DIGITS)} digits`);
166
+ }
167
+ function assertExponent(e) {
168
+ if (Math.abs(e) > MAX_EXPONENT)
169
+ throw new RangeError('exponent out of range');
170
+ }
171
+ /**
172
+ * An exact decimal number, shaped after the TC39 Decimal proposal: immutable, with arithmetic
173
+ * methods instead of operators. Values carry up to {@link Decimal.MAX_DIGITS} significant digits
174
+ * and an exponent within {@link Decimal.MAX_EXPONENT}; a result past either throws `RangeError`.
175
+ * Each operation costs time that grows with the digits of its operands, so size-limit untrusted
176
+ * lists before summing them.
177
+ *
178
+ * @example
179
+ * ```ts
180
+ * const total = new Decimal('0.1').add('0.2')
181
+ * total.toString() // '0.3'
182
+ * total.equals('0.3') // true
183
+ * ```
184
+ */
185
+ export class Decimal {
186
+ /** Significant digits a Decimal holds; longer inputs and results throw `RangeError`. */
187
+ static get MAX_DIGITS() {
188
+ return MAX_DIGITS;
189
+ }
190
+ /** Largest absolute exponent a Decimal stores; larger values throw `RangeError`. */
191
+ static get MAX_EXPONENT() {
192
+ return MAX_EXPONENT;
193
+ }
194
+ /** Largest fractional `digits` {@link Decimal.round} and {@link Decimal.toFixed} take. */
195
+ static get MAX_PRECISION() {
196
+ return MAX_PRECISION;
197
+ }
198
+ /**
199
+ * Orders two values, for `toSorted` and sorted collections.
200
+ * @example
201
+ * ```ts
202
+ * ['10', '9', '9.5'].map((s) => new Decimal(s)).toSorted(Decimal.compare).map(String)
203
+ * // ['9', '9.5', '10']
204
+ * ```
205
+ */
206
+ static compare(a, b) {
207
+ return compareIE(Decimal.#operand(a), Decimal.#operand(b));
208
+ }
209
+ #i;
210
+ #e;
211
+ #digits;
212
+ /**
213
+ * Reads a Decimal, a decimal string (`'-1.5'`, `'.5'`, `'2e-3'`, surrounding spaces allowed),
214
+ * a bigint, or a finite number through its shortest round-trip string, so `new Decimal(0.1)`
215
+ * is exactly `0.1`.
216
+ * @throws {TypeError} for any other type.
217
+ * @throws {RangeError} for a malformed string, a non-finite number, or a value past the bounds.
218
+ * @example
219
+ * ```ts
220
+ * new Decimal('1.50').toString() // '1.5'
221
+ * new Decimal(2n ** 64n).toString() // '18446744073709551616'
222
+ * ```
223
+ */
224
+ constructor(value) {
225
+ const parsed = Decimal.#parse(value);
226
+ const digits = digitLength(parsed.i);
227
+ assertDigitsBound(digits);
228
+ assertExponent(parsed.e);
229
+ this.#i = parsed.i;
230
+ this.#e = parsed.e;
231
+ this.#digits = digits;
232
+ }
233
+ static #from(value) {
234
+ return value instanceof Decimal ? value : new Decimal(value);
235
+ }
236
+ static #operand(value) {
237
+ const d = Decimal.#from(value);
238
+ return { i: d.#i, e: d.#e, digits: d.#digits };
239
+ }
240
+ static #parse(value) {
241
+ if (value instanceof Decimal)
242
+ return { i: value.#i, e: value.#e };
243
+ if (typeof value === 'string')
244
+ return Decimal.#parseString(value);
245
+ if (typeof value === 'number') {
246
+ if (!Number.isFinite(value))
247
+ throw new RangeError(`${String(value)} is not a finite number`);
248
+ return Decimal.#parseString(String(value));
249
+ }
250
+ if (typeof value === 'bigint') {
251
+ Decimal.#assertBigintSpan(value);
252
+ return normalize(value, 0);
253
+ }
254
+ throw new TypeError('Decimal value must be a Decimal, string, bigint or number');
255
+ }
256
+ /** Keeps a bigint too long for any Decimal away from the quadratic {@link normalize}. */
257
+ static #assertBigintSpan(value) {
258
+ if (digitLengthAbs(absBig(value)) <= MAX_VALUE_DIGITS)
259
+ return;
260
+ if (value % pow10(MAX_EXPONENT + 1) === 0n)
261
+ throw new RangeError('exponent out of range');
262
+ throw new RangeError(`result exceeds ${String(MAX_DIGITS)} digits`);
263
+ }
264
+ static #parseString(value) {
265
+ if (value.length > MAX_INPUT_LENGTH) {
266
+ throw new RangeError(`decimal string longer than ${String(MAX_INPUT_LENGTH)} characters`);
267
+ }
268
+ const leading = PARSE_LEADING_DIGITS.exec(value);
269
+ if (leading)
270
+ return Decimal.#parseParts(leading[1], leading[2], leading[3] ?? '', leading[4]);
271
+ const dotted = PARSE_LEADING_DOT.exec(value);
272
+ if (dotted)
273
+ return Decimal.#parseParts(dotted[1], '0', dotted[2], dotted[3]);
274
+ throw new RangeError(`invalid decimal string: ${quoteForError(value)}`);
275
+ }
276
+ static #parseParts(sign, intDigits, frac, exponent) {
277
+ // Count significant digits before BigInt sees an oversized coefficient.
278
+ const digits = intDigits + frac;
279
+ let start = 0;
280
+ while (start < digits.length && digits.charCodeAt(start) === 48)
281
+ start++;
282
+ let end = digits.length;
283
+ while (end > start && digits.charCodeAt(end - 1) === 48)
284
+ end--;
285
+ assertDigitsBound(end - start);
286
+ const i = BigInt(sign + digits);
287
+ return normalize(i, Number(exponent ?? 0) - frac.length);
288
+ }
289
+ static #fromIE(i, e) {
290
+ const n = normalize(i, e);
291
+ const digits = digitLength(n.i);
292
+ assertDigitsBound(digits);
293
+ assertExponent(n.e);
294
+ return Decimal.#of(n.i, n.e, digits);
295
+ }
296
+ /** A Decimal from parts that are already normalized and within the bounds. */
297
+ static #of(i, e, digits) {
298
+ const d = new Decimal(0n);
299
+ d.#i = i;
300
+ d.#e = e;
301
+ d.#digits = digits;
302
+ return d;
303
+ }
304
+ /** Rounds `(n / d) * 10^e` to `fractionDigits` fractional digits. */
305
+ static #scaleRound(n, d, e, fractionDigits, mode) {
306
+ const k = e + fractionDigits;
307
+ const q = k >= 0 ? roundQuotient(n * pow10(k), d, mode) : roundQuotient(n, d * pow10(-k), mode);
308
+ return Decimal.#fromIE(q, -fractionDigits);
309
+ }
310
+ /**
311
+ * The exact sum.
312
+ * @example
313
+ * ```ts
314
+ * new Decimal('0.1').add('0.2').toString() // '0.3'
315
+ * ```
316
+ */
317
+ add(other) {
318
+ const b = Decimal.#from(other);
319
+ if (this.#i === 0n)
320
+ return b;
321
+ if (b.#i === 0n)
322
+ return this;
323
+ const e = Math.min(this.#e, b.#e);
324
+ return Decimal.#fromIE(this.#i * pow10(this.#e - e) + b.#i * pow10(b.#e - e), e);
325
+ }
326
+ /**
327
+ * The exact difference.
328
+ * @example
329
+ * ```ts
330
+ * new Decimal('1').subtract('0.9').toString() // '0.1'
331
+ * ```
332
+ */
333
+ subtract(other) {
334
+ return this.add(Decimal.#from(other).negate());
335
+ }
336
+ /**
337
+ * The exact product.
338
+ * @example
339
+ * ```ts
340
+ * new Decimal('1.5').multiply('-2').toString() // '-3'
341
+ * ```
342
+ */
343
+ multiply(other) {
344
+ const b = Decimal.#from(other);
345
+ return Decimal.#fromIE(this.#i * b.#i, this.#e + b.#e);
346
+ }
347
+ /**
348
+ * The quotient rounded to `precision` significant digits (default 34), as the TC39 draft
349
+ * rounds, so `new Decimal('1e-30').divide(3)` keeps its scale. An exact quotient with fewer
350
+ * digits is returned whole.
351
+ * @throws {TypeError} for `options` that is not an object.
352
+ * @throws {RangeError} for a zero divisor, a `precision` out of range, an unknown mode, or a
353
+ * rounded quotient past the exponent bound: one too large, or one whose last digit falls below
354
+ * `10^-Decimal.MAX_EXPONENT`, as `new Decimal('1e-9990').divide(3)` does at 34 digits.
355
+ * @example
356
+ * ```ts
357
+ * new Decimal(1).divide(3).toString() // '0.3333333333333333333333333333333333'
358
+ * new Decimal(2).divide(3, { precision: 2, roundingMode: 'trunc' }).toString() // '0.66'
359
+ * new Decimal(1).divide(8).toString() // '0.125'
360
+ * ```
361
+ */
362
+ divide(other, options) {
363
+ assertOptions(options);
364
+ const precision = options?.precision ?? DEFAULT_PRECISION;
365
+ const mode = options?.roundingMode ?? 'halfEven';
366
+ assertIntegerIn('precision', precision, 1, MAX_DIGITS);
367
+ assertRoundingMode(mode);
368
+ const b = Decimal.#from(other);
369
+ if (b.#i === 0n)
370
+ throw new RangeError('division by zero');
371
+ if (this.#i === 0n)
372
+ return this;
373
+ // The quotient's order of magnitude is the difference of the operands', or one less.
374
+ const lead = compareIE({ i: absBig(this.#i), e: 1 - this.#digits, digits: this.#digits }, { i: absBig(b.#i), e: 1 - b.#digits, digits: b.#digits });
375
+ const magnitude = this.#e + this.#digits - (b.#e + b.#digits) - (lead < 0 ? 1 : 0);
376
+ return Decimal.#scaleRound(this.#i, b.#i, this.#e - b.#e, precision - 1 - magnitude, mode);
377
+ }
378
+ /**
379
+ * The exact remainder of truncating division, with the sign of this value, as `%` gives.
380
+ * @throws {RangeError} for a zero divisor.
381
+ * @example
382
+ * ```ts
383
+ * new Decimal('7.5').remainder(2).toString() // '1.5'
384
+ * new Decimal('-7.5').remainder(2).toString() // '-1.5'
385
+ * ```
386
+ */
387
+ remainder(other) {
388
+ const b = Decimal.#from(other);
389
+ if (b.#i === 0n)
390
+ throw new RangeError('division by zero');
391
+ const e = Math.min(this.#e, b.#e);
392
+ return Decimal.#fromIE((this.#i * pow10(this.#e - e)) % (b.#i * pow10(b.#e - e)), e);
393
+ }
394
+ /**
395
+ * This value with the opposite sign.
396
+ * @example
397
+ * ```ts
398
+ * new Decimal('5').negate().toString() // '-5'
399
+ * ```
400
+ */
401
+ negate() {
402
+ return Decimal.#of(-this.#i, this.#e, this.#digits);
403
+ }
404
+ /**
405
+ * The absolute value.
406
+ * @example
407
+ * ```ts
408
+ * new Decimal('-1.5').abs().toString() // '1.5'
409
+ * ```
410
+ */
411
+ abs() {
412
+ return this.#i < 0n ? this.negate() : this;
413
+ }
414
+ /**
415
+ * Rounds to `digits` fractional digits; a negative count rounds to tens, hundreds, and so on.
416
+ * @param digits Fractional digits, from `-Decimal.MAX_EXPONENT` to `Decimal.MAX_PRECISION`
417
+ * (default 0).
418
+ * @param roundingMode How ties and remainders round (default `'halfEven'`).
419
+ * @throws {RangeError} for `digits` out of range, an unknown mode, or a result past the
420
+ * exponent bound.
421
+ * @example
422
+ * ```ts
423
+ * new Decimal('2.5').round().toString() // '2'
424
+ * new Decimal('2.5').round(0, 'halfExpand').toString() // '3'
425
+ * new Decimal('1234').round(-2).toString() // '1200'
426
+ * ```
427
+ */
428
+ round(digits = 0, roundingMode = 'halfEven') {
429
+ assertIntegerIn('digits', digits, -MAX_EXPONENT, MAX_PRECISION);
430
+ assertRoundingMode(roundingMode);
431
+ if (-this.#e <= digits)
432
+ return this;
433
+ return Decimal.#scaleRound(this.#i, 1n, this.#e, digits, roundingMode);
434
+ }
435
+ /**
436
+ * The square root rounded to `precision` significant digits (default 34). An exact root with
437
+ * fewer digits is returned whole.
438
+ * @throws {TypeError} for `options` that is not an object.
439
+ * @throws {RangeError} for a negative value, a `precision` out of range, an unknown mode, or a
440
+ * rounded root whose last digit falls below `10^-Decimal.MAX_EXPONENT`.
441
+ * @example
442
+ * ```ts
443
+ * new Decimal(2).sqrt().toString() // '1.414213562373095048801688724209698'
444
+ * new Decimal('6.25').sqrt().toString() // '2.5'
445
+ * new Decimal(2).sqrt({ precision: 5 }).toString() // '1.4142'
446
+ * ```
447
+ */
448
+ sqrt(options) {
449
+ assertOptions(options);
450
+ const precision = options?.precision ?? DEFAULT_PRECISION;
451
+ const mode = options?.roundingMode ?? 'halfEven';
452
+ assertIntegerIn('precision', precision, 1, MAX_DIGITS);
453
+ assertRoundingMode(mode);
454
+ if (this.#i < 0n)
455
+ throw new RangeError('square root of a negative number');
456
+ if (this.#i === 0n)
457
+ return this;
458
+ // The root's order of magnitude is half this value's, rounded down.
459
+ const fractionDigits = precision - 1 - Math.floor((this.#e + this.#digits - 1) / 2);
460
+ // i * 10^e = coeff * 10^(2 * half), with coeff = i or 10i.
461
+ const odd = this.#e % 2 !== 0;
462
+ const coeff = odd ? this.#i * 10n : this.#i;
463
+ const half = (odd ? this.#e - 1 : this.#e) / 2;
464
+ // Rounding boundaries fall on whole root units, so a sticky digit stands in for the rest.
465
+ const scale = Math.max(0, half + fractionDigits + 1);
466
+ const { root, exact } = isqrtRoot(coeff * pow10(2 * scale));
467
+ const sticky = root * 10n + (exact ? 0n : 1n);
468
+ return Decimal.#scaleRound(sticky, 1n, half - scale - 1, fractionDigits, mode);
469
+ }
470
+ /**
471
+ * Orders this value against `other`: -1, 0 or 1.
472
+ * @example
473
+ * ```ts
474
+ * new Decimal('1.10').compare('1.1') // 0
475
+ * new Decimal('-2').compare(1) // -1
476
+ * ```
477
+ */
478
+ compare(other) {
479
+ return compareIE({ i: this.#i, e: this.#e, digits: this.#digits }, Decimal.#operand(other));
480
+ }
481
+ /**
482
+ * True when both are the same number, whatever their written form.
483
+ * @example
484
+ * ```ts
485
+ * new Decimal('2.50').equals('2.5') // true
486
+ * ```
487
+ */
488
+ equals(other) {
489
+ return this.compare(other) === 0;
490
+ }
491
+ /**
492
+ * True when this value is less than `other`.
493
+ * @example
494
+ * ```ts
495
+ * new Decimal('0.1').lessThan('0.2') // true
496
+ * ```
497
+ */
498
+ lessThan(other) {
499
+ return this.compare(other) < 0;
500
+ }
501
+ /**
502
+ * True when this value is less than or equal to `other`.
503
+ * @example
504
+ * ```ts
505
+ * new Decimal('0.2').lessThanOrEqual('0.20') // true
506
+ * ```
507
+ */
508
+ lessThanOrEqual(other) {
509
+ return this.compare(other) <= 0;
510
+ }
511
+ /**
512
+ * True when this value is greater than `other`.
513
+ * @example
514
+ * ```ts
515
+ * new Decimal('1e3').greaterThan(999) // true
516
+ * ```
517
+ */
518
+ greaterThan(other) {
519
+ return this.compare(other) > 0;
520
+ }
521
+ /**
522
+ * True when this value is greater than or equal to `other`.
523
+ * @example
524
+ * ```ts
525
+ * new Decimal('-0').greaterThanOrEqual(0) // true
526
+ * ```
527
+ */
528
+ greaterThanOrEqual(other) {
529
+ return this.compare(other) >= 0;
530
+ }
531
+ /**
532
+ * The canonical string: plain notation while that pads at most 64 zeros, scientific past that
533
+ * (`'1e+65'`, `'1.5e-70'`), so its length follows the significant digits and not the exponent.
534
+ * The constructor reads both forms back.
535
+ * @example
536
+ * ```ts
537
+ * new Decimal('1.50').toString() // '1.5'
538
+ * new Decimal('1e100').toString() // '1e+100'
539
+ * ```
540
+ */
541
+ toString() {
542
+ const padding = this.#e > 0 ? this.#e : -this.#e - this.#digits;
543
+ if (padding <= MAX_PLAIN_PADDING)
544
+ return this.toPlainString();
545
+ const digits = absBig(this.#i).toString();
546
+ const fraction = digits.length > 1 ? `.${digits.slice(1)}` : '';
547
+ const exponent = this.#e + digits.length - 1;
548
+ const sign = this.#i < 0n ? '-' : '';
549
+ return `${sign}${digits.slice(0, 1)}${fraction}e${exponent < 0 ? '' : '+'}${String(exponent)}`;
550
+ }
551
+ /**
552
+ * Plain notation at any magnitude. A short input such as `'1e10000'` expands to 10,001
553
+ * characters, so prefer {@link Decimal.toString} for untrusted values.
554
+ * @example
555
+ * ```ts
556
+ * new Decimal('1e5').toPlainString() // '100000'
557
+ * new Decimal('-1.5e-3').toPlainString() // '-0.0015'
558
+ * ```
559
+ */
560
+ toPlainString() {
561
+ const digits = absBig(this.#i).toString();
562
+ const sign = this.#i < 0n ? '-' : '';
563
+ if (this.#e >= 0)
564
+ return sign + digits + '0'.repeat(this.#e);
565
+ if (-this.#e >= digits.length) {
566
+ return `${sign}0.${'0'.repeat(-this.#e - digits.length)}${digits}`;
567
+ }
568
+ return `${sign}${digits.slice(0, this.#e)}.${digits.slice(this.#e)}`;
569
+ }
570
+ /**
571
+ * Plain notation with exactly `digits` fractional digits. Ties round half away from zero
572
+ * (`'halfExpand'`), as `Number.prototype.toFixed` rounds, not half to even as
573
+ * {@link Decimal.round} does by default; pass `roundingMode` to choose another.
574
+ * @param digits Fractional digits, from 0 to `Decimal.MAX_PRECISION` (default 0).
575
+ * @param roundingMode How the last digit rounds (default `'halfExpand'`).
576
+ * @throws {RangeError} for `digits` out of range or an unknown mode.
577
+ * @example
578
+ * ```ts
579
+ * new Decimal('2.5').toFixed() // '3'
580
+ * new Decimal('1.005').toFixed(2) // '1.01'
581
+ * new Decimal('3').toFixed(2) // '3.00'
582
+ * ```
583
+ */
584
+ toFixed(digits = 0, roundingMode = 'halfExpand') {
585
+ assertIntegerIn('digits', digits, 0, MAX_PRECISION);
586
+ const [whole, fraction = ''] = this.round(digits, roundingMode).toPlainString().split('.');
587
+ return digits === 0 ? whole : `${whole}.${fraction.padEnd(digits, '0')}`;
588
+ }
589
+ /** The canonical string, as {@link Decimal.toString}, so `JSON.stringify` keeps every digit. */
590
+ toJSON() {
591
+ return this.toString();
592
+ }
593
+ /**
594
+ * Formats the exact value with `Intl.NumberFormat`, which reads decimal strings without going
595
+ * through a `Number`, so digits past 2^53 survive.
596
+ * @throws {RangeError} when the value is beyond `Number.MAX_VALUE`, which `Intl` shows as `∞`,
597
+ * or so close to zero that it rounds to a `Number` zero, which `Intl` may show as `0`.
598
+ * @throws {RangeError} for a malformed locale tag, or a locale list longer than 255 characters
599
+ * joined, which `Intl` canonicalizes in quadratic time.
600
+ * @throws {TypeError} for a locale that is neither a string nor an `Intl.Locale`.
601
+ * @example
602
+ * ```ts
603
+ * new Decimal('12345678901234567.25').toLocaleString('en-US', { maximumFractionDigits: 2 })
604
+ * // '12,345,678,901,234,567.25'
605
+ * ```
606
+ */
607
+ toLocaleString(locales, options) {
608
+ const nearest = this.toNumber();
609
+ if (!Number.isFinite(nearest) || (nearest === 0 && this.#i !== 0n)) {
610
+ throw new RangeError('value is outside the range Intl.NumberFormat can show');
611
+ }
612
+ const format = new Intl.NumberFormat(canonicalLocales(locales), options);
613
+ // toString always prints a StringNumericLiteral, which the type cannot express.
614
+ return format.format(this.toString());
615
+ }
616
+ /**
617
+ * The nearest `Number`, read from the coefficient and exponent, so extreme exponents stay cheap.
618
+ * @example
619
+ * ```ts
620
+ * new Decimal('0.1').toNumber() // 0.1
621
+ * new Decimal('1e400').toNumber() // Infinity
622
+ * ```
623
+ */
624
+ toNumber() {
625
+ return Number(`${String(this.#i)}e${String(this.#e)}`);
626
+ }
627
+ /**
628
+ * The value as a bigint.
629
+ * @throws {RangeError} when the value has a fractional part.
630
+ * @example
631
+ * ```ts
632
+ * new Decimal('1.2e3').toBigInt() // 1200n
633
+ * ```
634
+ */
635
+ toBigInt() {
636
+ if (this.#e < 0)
637
+ throw new RangeError(`${quoteForError(this.toString())} is not an integer`);
638
+ return this.#i * pow10(this.#e);
639
+ }
640
+ /**
641
+ * What `console.log` and `util.inspect` show, such as `Decimal { 1.5 }`. The symbol is the
642
+ * runtime's own inspection hook, read by Node and runtimes compatible with it, not a protocol of
643
+ * this package.
644
+ * @example
645
+ * ```ts
646
+ * console.log(new Decimal('1.50')) // Decimal { 1.5 }
647
+ * ```
648
+ */
649
+ [Symbol.for('nodejs.util.inspect.custom')]() {
650
+ return `Decimal { ${this.toString()} }`;
651
+ }
652
+ /**
653
+ * Throws, so `<`, `+` and `==` cannot silently turn a Decimal into a lossy `Number`. Use
654
+ * {@link Decimal.compare}, {@link Decimal.add} or {@link Decimal.toNumber}.
655
+ * @throws {TypeError} always.
656
+ */
657
+ valueOf() {
658
+ throw new TypeError('Decimal cannot be converted to a primitive implicitly; use compare(), toNumber() or toString()');
659
+ }
660
+ }