picocalc 0.0.1 → 0.0.2

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/dist/index.mjs CHANGED
@@ -1,10 +1,4 @@
1
- //#region src/lib/constants.ts
2
- const PI = Math.PI.toString();
3
- const constants = {
4
- e: Math.E.toString(),
5
- pi: PI
6
- };
7
- //#endregion
1
+ import "bigint.bitlength";
8
2
  //#region src/lib/errors.ts
9
3
  var GenericMathErrror = class extends Error {
10
4
  pos;
@@ -15,11 +9,6 @@ var GenericMathErrror = class extends Error {
15
9
  this.name = name;
16
10
  }
17
11
  };
18
- var LexerError = class extends GenericMathErrror {
19
- constructor(message, pos) {
20
- super("LexerError", message, pos);
21
- }
22
- };
23
12
  var InterpreterError = class extends GenericMathErrror {
24
13
  constructor(message, pos) {
25
14
  super("InterpreterError", message, pos);
@@ -30,6 +19,11 @@ var MaximumPrecisionError = class extends InterpreterError {
30
19
  super(`Exceeded maximum precision of ${maxPrecision} digits (${precision})`);
31
20
  }
32
21
  };
22
+ var NotImplementedError = class extends InterpreterError {
23
+ constructor(message, pos) {
24
+ super(message, pos);
25
+ }
26
+ };
33
27
  var OverflowError = class extends InterpreterError {
34
28
  constructor() {
35
29
  super("Overflow");
@@ -45,56 +39,152 @@ var EmptyExpressionError = class extends InterpreterError {
45
39
  super("Empty expression");
46
40
  }
47
41
  };
48
- var UnexpectedEndOfExpressionError = class extends InterpreterError {
49
- constructor() {
50
- super("Unexpected end of expression");
51
- }
52
- };
53
42
  var MismatchedParenthesisError = class extends InterpreterError {
54
43
  constructor(pos, message = "Mismatched parenthesis") {
55
44
  super(message, pos);
56
45
  }
57
46
  };
58
- var InsufficientOperandsError = class extends InterpreterError {
59
- constructor(pos) {
60
- super("Insufficient operands for operation", pos);
61
- }
47
+ //#endregion
48
+ //#region src/lib/types.ts
49
+ const OverflowValue = { n: "OVERFLOW" };
50
+ //#endregion
51
+ //#region src/lib/utils/constants.ts
52
+ const PI = Math.PI.toString();
53
+ const E = Math.E.toString();
54
+ const ZERO = {
55
+ n: 0n,
56
+ d: 1n
62
57
  };
63
- var ParserError = class extends GenericMathErrror {
64
- constructor(message, pos) {
65
- super("ParserError", message, pos);
66
- }
58
+ const ONE = {
59
+ n: 1n,
60
+ d: 1n
67
61
  };
68
- var IncompleteExpressionError = class extends ParserError {
69
- constructor(message, pos) {
70
- super(`Incomplete expression: ${message}`, pos);
71
- }
62
+ const constants = {
63
+ e: E,
64
+ pi: PI
72
65
  };
66
+ function getConst(id) {
67
+ const c = constants[id];
68
+ return {
69
+ n: BigInt(c.replace(".", "")),
70
+ d: 10n ** BigInt(c.length - c.indexOf(".") - 1)
71
+ };
72
+ }
73
73
  //#endregion
74
74
  //#region src/lib/utils/gcd.ts
75
75
  /**
76
- * Fast Binary GCD (Stein's Algorithm) for BigInt.
76
+ * The Euclidean GCD Algorithm for BigInt.
77
77
  */
78
78
  function gcd(a, b) {
79
79
  if (a === b) return a;
80
80
  if (a === 1n || b === 1n) return 1n;
81
81
  if (a === 0n) return b;
82
82
  if (b === 0n) return a;
83
- a = a < 0n ? -a : a;
84
- b = b < 0n ? -b : b;
85
- let shift = 0n;
86
- while (((a | b) & 1n) === 0n) {
87
- a >>= 1n;
88
- b >>= 1n;
89
- shift++;
83
+ a = a < 0 ? -a : a;
84
+ b = b < 0 ? -b : b;
85
+ while (b > 0) {
86
+ a %= b;
87
+ [a, b] = [b, a];
90
88
  }
91
- while ((a & 1n) === 0n) a >>= 1n;
92
- do {
93
- while ((b & 1n) === 0n) b >>= 1n;
94
- if (a > b) [a, b] = [b, a];
95
- b = b - a;
96
- } while (b !== 0n);
97
- return a << shift;
89
+ return a;
90
+ }
91
+ //#endregion
92
+ //#region src/lib/utils/multiply.ts
93
+ function multiply(a, b) {
94
+ const aN = a.n;
95
+ const bN = b.n;
96
+ if (aN === 0n || bN === 0n) return ZERO;
97
+ if (aN === "OVERFLOW") return a;
98
+ if (bN === "OVERFLOW") return b;
99
+ const { d: aD, c: aC, e: aE } = a;
100
+ const { d: bD, c: bC, e: bE } = b;
101
+ const g1 = gcd(aN, bD);
102
+ const g2 = gcd(bN, aD);
103
+ const n = aN / g1 * (bN / g2);
104
+ const d = aD / g2 * (bD / g1);
105
+ if (aC === void 0 && bC === void 0) return {
106
+ n,
107
+ d
108
+ };
109
+ if (aC === bC) return {
110
+ n,
111
+ d,
112
+ c: aC,
113
+ e: simplify(add({
114
+ n: aE?.n ?? 1n,
115
+ d: aE?.d ?? 1n
116
+ }, {
117
+ n: bE?.n ?? 1n,
118
+ d: bE?.d ?? 1n
119
+ }))
120
+ };
121
+ if (aC !== void 0 && bC !== void 0) return multiply(toSimpleFraction(a), toSimpleFraction(b));
122
+ return {
123
+ n,
124
+ d,
125
+ c: aC ?? bC,
126
+ e: aE ?? bE
127
+ };
128
+ }
129
+ //#endregion
130
+ //#region src/lib/utils/nthroot.ts
131
+ /**
132
+ * Calculates the integer nth root of a BigInt.
133
+ */
134
+ function iNthRoot(value, n) {
135
+ if (value < 0 && n % 2n === 0n) throw new NotImplementedError(`Even (${n}th) root of negative numbers is not implemented yet.`);
136
+ if (value < 0) return -iNthRoot(-value, n);
137
+ if (value < 2n) return value;
138
+ if (n === 1n) return value;
139
+ let x = 1n << BigInt.bitLength(value) / n + 1n;
140
+ const nMinus1 = n - 1n;
141
+ while (true) {
142
+ const nextX = (nMinus1 * x + value / x ** nMinus1) / n;
143
+ if (nextX >= x) break;
144
+ x = nextX;
145
+ }
146
+ return x;
147
+ }
148
+ /**
149
+ * Calculates the n-th root of a fraction.
150
+ */
151
+ function nthRoot(v, n, precision) {
152
+ if (n === 0n) throw new DivisionByZeroError();
153
+ if (n === 1n) return v;
154
+ if (v.n < 0) {
155
+ if (n % 2n === 0n) throw new NotImplementedError(`Even (${n}th) root of negative numbers is not implemented yet.`);
156
+ const positiveResult = nthRoot({
157
+ n: -v.n,
158
+ d: v.d
159
+ }, n, precision);
160
+ return {
161
+ n: -positiveResult.n,
162
+ d: positiveResult.d
163
+ };
164
+ }
165
+ if (v.n === 0n) return ZERO;
166
+ if (precision.format === "precise") {
167
+ const rootN = iNthRoot(v.n, n);
168
+ const rootD = iNthRoot(v.d, n);
169
+ if (rootN ** n === v.n && rootD ** n === v.d) {
170
+ const e = simplify({
171
+ n: v.e?.n ?? 1n,
172
+ d: (v.e?.d ?? 1n) * n
173
+ });
174
+ return simplify({
175
+ n: rootN,
176
+ d: rootD,
177
+ c: v.c,
178
+ e
179
+ });
180
+ }
181
+ }
182
+ const precisionDigits = Math.max(10, (precision.maxDecimals ?? 30) * 3);
183
+ const scaleFactor = 10n ** (BigInt(precisionDigits) * n);
184
+ return simplify({
185
+ n: iNthRoot(v.n * scaleFactor / v.d, n),
186
+ d: 10n ** BigInt(precisionDigits)
187
+ });
98
188
  }
99
189
  //#endregion
100
190
  //#region src/lib/utils/simplify.ts
@@ -103,34 +193,277 @@ function gcd(a, b) {
103
193
  */
104
194
  function simplify(v) {
105
195
  if (v.d === 0n) throw new DivisionByZeroError();
106
- if (v.n === 0n) return {
107
- n: 0n,
108
- d: 1n,
109
- c: v.c
110
- };
111
- if (v.d === 1n) return v;
196
+ if (v.n === 0n) return ZERO;
197
+ if (v.d === 1n) {
198
+ if (v.e?.n === 0n) return {
199
+ n: v.n,
200
+ d: v.d
201
+ };
202
+ return v;
203
+ }
112
204
  const common = gcd(v.n, v.d);
113
- const sign = v.d < 0n ? -1n : 1n;
205
+ const sign = v.d < 0 ? -1n : 1n;
206
+ const n = v.n / common * sign;
207
+ const d = v.d / common * sign;
208
+ if (v.e?.n === 0n) return {
209
+ n,
210
+ d
211
+ };
114
212
  return {
115
- n: v.n / common * sign,
116
- d: v.d / common * sign,
117
- c: v.c
213
+ n,
214
+ d,
215
+ c: v.c,
216
+ e: v.e
217
+ };
218
+ }
219
+ /**
220
+ * Resolves any constants and exponents to return a simple fraction.
221
+ */
222
+ function toSimpleFraction(val) {
223
+ if (!val.c) return val;
224
+ const c = getConst(val.c);
225
+ const expN = val.e?.n ?? 1n;
226
+ const expD = val.e?.d ?? 1n;
227
+ const absExpN = expN < 0 ? -expN : expN;
228
+ let poweredConstant = {
229
+ n: expN < 0 ? c.d ** absExpN : c.n ** absExpN,
230
+ d: expN < 0 ? c.n ** absExpN : c.d ** absExpN
231
+ };
232
+ if (expD !== 1n) poweredConstant = nthRoot(poweredConstant, expD, { format: "precise" });
233
+ return multiply(poweredConstant, {
234
+ n: val.n,
235
+ d: val.d
236
+ });
237
+ }
238
+ //#endregion
239
+ //#region src/lib/utils/add.ts
240
+ function add(left, right, subtract = false) {
241
+ const lN = left.n;
242
+ const rN = right.n;
243
+ if (lN === "OVERFLOW") return left;
244
+ if (rN === "OVERFLOW") return right;
245
+ const { d: lD, c, e } = left;
246
+ const { d: rD, c: rC, e: rE } = right;
247
+ if (lN === 0n) return {
248
+ n: subtract ? -rN : rN,
249
+ d: rD,
250
+ c: rC,
251
+ e: rE
252
+ };
253
+ if (rN === 0n) return {
254
+ n: lN,
255
+ d: lD,
256
+ c,
257
+ e
258
+ };
259
+ let n;
260
+ let d;
261
+ if (lD === rD) {
262
+ n = subtract ? lN - rN : lN + rN;
263
+ d = lD;
264
+ } else {
265
+ const common = gcd(lD, rD);
266
+ const mLeft = rD / common;
267
+ const mRight = lD / common;
268
+ n = subtract ? lN * mLeft - rN * mRight : lN * mLeft + rN * mRight;
269
+ d = lD * mLeft;
270
+ }
271
+ if (n === 0n) return ZERO;
272
+ if (c !== rC || e?.n !== rE?.n) return add(toSimpleFraction(left), toSimpleFraction(right), subtract);
273
+ return {
274
+ n,
275
+ d,
276
+ c,
277
+ e
118
278
  };
119
279
  }
120
280
  //#endregion
121
281
  //#region src/lib/utils/ceil.ts
122
282
  /**
123
- * Returns the smallest integer greater than or equal to a Value.
283
+ * Returns the smallest integer greater than or equal to the value.
124
284
  */
125
285
  function ceil(v) {
126
286
  const { n, d } = simplify(v);
127
287
  if (d === 1n) return n;
128
- const isPositive = n > 0n;
288
+ const isPositive = n > 0;
129
289
  const result = n / d;
130
290
  const hasRemainder = n % d !== 0n;
131
291
  return isPositive && hasRemainder ? result + 1n : result;
132
292
  }
133
293
  //#endregion
294
+ //#region src/lib/utils/divide.ts
295
+ function divide(left, right) {
296
+ const lN = left.n;
297
+ const rN = right.n;
298
+ if (rN === 0n) throw new DivisionByZeroError();
299
+ if (lN === 0n) return ZERO;
300
+ if (lN === "OVERFLOW" || rN === "OVERFLOW") return OverflowValue;
301
+ const { d: lD, c: lC, e: lE } = left;
302
+ const { d: rD, c: rC, e: rE } = right;
303
+ const g1 = gcd(lN, rN);
304
+ const g2 = gcd(rD, lD);
305
+ const n = lN / g1 * (rD / g2);
306
+ const d = lD / g2 * (rN / g1);
307
+ if (lC !== void 0 && lC === rC) return {
308
+ n,
309
+ d,
310
+ c: lC,
311
+ e: simplify(add({
312
+ n: lE?.n ?? 1n,
313
+ d: lE?.d ?? 1n
314
+ }, {
315
+ n: rE?.n ?? 1n,
316
+ d: rE?.d ?? 1n
317
+ }, true))
318
+ };
319
+ const c = lC ?? rC;
320
+ let e;
321
+ if (lC !== void 0) e = lE ? {
322
+ n: lE.n,
323
+ d: lE.d ?? 1n
324
+ } : {
325
+ n: 1n,
326
+ d: 1n
327
+ };
328
+ else if (rC !== void 0) e = {
329
+ n: -(rE?.n ?? 1n),
330
+ d: rE?.d ?? 1n
331
+ };
332
+ return {
333
+ n,
334
+ d,
335
+ c,
336
+ e
337
+ };
338
+ }
339
+ //#endregion
340
+ //#region src/lib/utils/sqrt.ts
341
+ /**
342
+ * Calculates the integer square root of a BigInt.
343
+ */
344
+ function isqrt(value) {
345
+ if (value < 0) throw new NotImplementedError("Square root of negative numbers is not implemented yet.");
346
+ if (value < 2n) return value;
347
+ let res = 0n;
348
+ let bit = 1n << (BigInt.bitLength(value) & -2n);
349
+ while (bit !== 0n) {
350
+ if (value >= res + bit) {
351
+ value -= res + bit;
352
+ res = (res >> 1n) + bit;
353
+ } else res >>= 1n;
354
+ bit >>= 2n;
355
+ }
356
+ return res;
357
+ }
358
+ function isPerfectSquare(val) {
359
+ if (val < 0) return [false, 0n];
360
+ const root = isqrt(val);
361
+ return [root * root === val, root];
362
+ }
363
+ function sqrt(v, precision) {
364
+ if (v.n === "OVERFLOW") return v;
365
+ if (v.n < 0) throw new NotImplementedError("Square root of negative numbers is not implemented yet.");
366
+ if (v.n === 0n) return ZERO;
367
+ if (precision.format === "precise") {
368
+ const [nIsSquare, n] = isPerfectSquare(v.n);
369
+ const [dIsSquare, d] = isPerfectSquare(v.d);
370
+ if (nIsSquare && dIsSquare) {
371
+ const e = simplify({
372
+ n: v.e?.n ?? 1n,
373
+ d: (v.e?.d ?? 1n) * 2n
374
+ });
375
+ return simplify({
376
+ n,
377
+ d,
378
+ c: v.c,
379
+ e
380
+ });
381
+ }
382
+ }
383
+ const precisionDigits = Math.max(10, (precision.maxDecimals ?? 30) * 3);
384
+ const scaleFactor = 10n ** BigInt(precisionDigits * 2);
385
+ return simplify({
386
+ n: isqrt(v.n * scaleFactor / v.d),
387
+ d: 10n ** BigInt(precisionDigits)
388
+ });
389
+ }
390
+ //#endregion
391
+ //#region src/lib/utils/exponentiate.ts
392
+ /**
393
+ * Maximum digits allowed for the result of exponentiation
394
+ */
395
+ const MAX_EXPONENT_RESULT_DIGITS = 10000000n;
396
+ function exponentiate(left, right, precision) {
397
+ const rN = right.n;
398
+ if (rN === 0n) return ONE;
399
+ const lN = left.n;
400
+ if (lN === "OVERFLOW") return left;
401
+ const lD = left.d;
402
+ const c = left.c;
403
+ if (lN === lD && c === void 0) return ONE;
404
+ if (rN === "OVERFLOW") return right;
405
+ if (lN === -lD && c === void 0) {
406
+ if (rN % 2n) return {
407
+ n: -1n,
408
+ d: 1n
409
+ };
410
+ return ONE;
411
+ }
412
+ const normalizedExp = toSimpleFraction(simplify(right));
413
+ let exponent = normalizedExp.n;
414
+ if (lN === 0n) {
415
+ if (exponent < 0) throw new DivisionByZeroError();
416
+ return ZERO;
417
+ }
418
+ const dExp = normalizedExp.d;
419
+ if (dExp >= 100) {
420
+ let baseFloat = Number(lN) / Number(lD);
421
+ if (c) baseFloat *= Number(constants[c]);
422
+ const expFloat = Number(exponent) / Number(dExp);
423
+ const resultFloat = baseFloat ** expFloat;
424
+ if (!Number.isFinite(resultFloat)) return OverflowValue;
425
+ const [integerPart, fractionalPart] = resultFloat.toString().split(".");
426
+ if (!fractionalPart) return simplify({
427
+ n: BigInt(integerPart),
428
+ d: 1n
429
+ });
430
+ const denominator = 10n ** BigInt(fractionalPart.length);
431
+ return simplify({
432
+ n: BigInt(integerPart + fractionalPart),
433
+ d: denominator
434
+ });
435
+ }
436
+ const eN = (left.e?.n ?? 1n) * exponent;
437
+ let baseN = lN;
438
+ let baseD = lD;
439
+ if (exponent < 0) {
440
+ exponent = -exponent;
441
+ [baseN, baseD] = [baseD, baseN];
442
+ }
443
+ const baseNDigits = BigInt.bitLength(baseN);
444
+ const baseDDigits = BigInt.bitLength(baseD);
445
+ if (baseNDigits * exponent > MAX_EXPONENT_RESULT_DIGITS || baseDDigits * exponent > MAX_EXPONENT_RESULT_DIGITS || (baseNDigits > 5e3 || baseDDigits > 5e3) && exponent > 10) return OverflowValue;
446
+ const e = c ? simplify({
447
+ n: eN,
448
+ d: left.e?.d ?? 1n
449
+ }) : void 0;
450
+ if (dExp === exponent) return {
451
+ n: baseN,
452
+ d: baseD,
453
+ c,
454
+ e
455
+ };
456
+ const v = {
457
+ n: baseN ** exponent,
458
+ d: baseD ** exponent,
459
+ c,
460
+ e
461
+ };
462
+ if (dExp === 1n) return v;
463
+ if (dExp === 2n) return sqrt(v, precision);
464
+ return nthRoot(v, dExp, precision);
465
+ }
466
+ //#endregion
134
467
  //#region src/lib/utils/factorial.ts
135
468
  function binaryProduct(arr, low, high) {
136
469
  if (low > high) return 1n;
@@ -150,7 +483,7 @@ function getPrimes(limit) {
150
483
  * Calculates factorial.
151
484
  */
152
485
  function factorial(n) {
153
- if (n < 0n) return null;
486
+ if (n < 0) return null;
154
487
  if (n === 0n || n === 1n) return 1n;
155
488
  if (n < 1000n) {
156
489
  let result = 1n;
@@ -174,12 +507,12 @@ function factorial(n) {
174
507
  //#endregion
175
508
  //#region src/lib/utils/floor.ts
176
509
  /**
177
- * Returns the largest integer less than or equal to a Value.
510
+ * Returns the largest integer less than or equal to the value.
178
511
  */
179
512
  function floor(v) {
180
513
  const { n, d } = simplify(v);
181
514
  if (d === 1n) return n;
182
- const isNegative = n < 0n;
515
+ const isNegative = n < 0;
183
516
  const result = n / d;
184
517
  const hasRemainder = n % d !== 0n;
185
518
  return isNegative && hasRemainder ? result - 1n : result;
@@ -190,6 +523,7 @@ function floor(v) {
190
523
  * Calculates remainder of division.
191
524
  */
192
525
  function mod(a, b) {
526
+ if (b.n === 0n) throw new DivisionByZeroError();
193
527
  const d = a.d * b.d / gcd(a.d, b.d);
194
528
  const n1 = a.n * (d / a.d);
195
529
  const n2 = b.n * (d / b.d);
@@ -199,131 +533,6 @@ function mod(a, b) {
199
533
  };
200
534
  }
201
535
  //#endregion
202
- //#region src/lib/utils/multiply.ts
203
- function multiply(a, b) {
204
- if (a.n === 0n || b.n === 0n) return {
205
- n: 0n,
206
- d: 1n
207
- };
208
- let n;
209
- let d;
210
- let c;
211
- if (a.d === 1n && b.d === 1n) {
212
- n = a.n * b.n;
213
- d = 1n;
214
- } else {
215
- const g1 = gcd(a.n, b.d);
216
- const g2 = gcd(b.n, a.d);
217
- n = a.n / g1 * (b.n / g2);
218
- d = a.d / g2 * (b.d / g1);
219
- }
220
- if (a.c === void 0 && b.c !== void 0) c = b.c;
221
- else if (a.c !== void 0 && b.c === void 0) c = a.c;
222
- return {
223
- n,
224
- d,
225
- c
226
- };
227
- }
228
- //#endregion
229
- //#region src/lib/utils/nthroot.ts
230
- /**
231
- * Calculates the integer nth root of a BigInt.
232
- */
233
- function iNthRoot(value, n) {
234
- if (value < 0n && n % 2n === 0n) throw new InterpreterError("Even root of negative number is not supported yet.");
235
- if (value < 0n) return -iNthRoot(-value, n);
236
- if (value < 2n) return value;
237
- if (n === 1n) return value;
238
- let x = 1n << BigInt(value.toString(2).length) / n + 1n;
239
- const nMinus1 = n - 1n;
240
- while (true) {
241
- const nextX = (nMinus1 * x + value / x ** nMinus1) / n;
242
- if (nextX >= x) break;
243
- x = nextX;
244
- }
245
- return x;
246
- }
247
- /**
248
- * Calculates the n-th root of a Value (fraction).
249
- */
250
- function nthRoot(v, root, precise = false, precisionDigits = 100) {
251
- if (root === 0n) throw new DivisionByZeroError();
252
- if (root === 1n) return v;
253
- if (v.n < 0n) {
254
- if (root % 2n === 0n) throw new InterpreterError(`${root}-th root of negative not supported.`);
255
- const positiveResult = nthRoot({
256
- n: -v.n,
257
- d: v.d
258
- }, root, precise, precisionDigits);
259
- return {
260
- n: -positiveResult.n,
261
- d: positiveResult.d
262
- };
263
- }
264
- if (v.n === 0n) return {
265
- n: 0n,
266
- d: 1n
267
- };
268
- if (precise) {
269
- const rootN = iNthRoot(v.n, root);
270
- const rootD = iNthRoot(v.d, root);
271
- if (rootN ** root === v.n && rootD ** root === v.d) return simplify({
272
- n: rootN,
273
- d: rootD
274
- });
275
- }
276
- const scaleFactor = 10n ** (BigInt(precisionDigits) * root);
277
- return simplify({
278
- n: iNthRoot(v.n * scaleFactor / v.d, root),
279
- d: 10n ** BigInt(precisionDigits)
280
- });
281
- }
282
- //#endregion
283
- //#region src/lib/utils/sqrt.ts
284
- /**
285
- * Calculates the integer square root of a BigInt.
286
- */
287
- function isqrt(value) {
288
- if (value < 0n) throw new InterpreterError("Square root of negative not supported yet.");
289
- if (value < 2n) return value;
290
- let res = 0n;
291
- let bit = 1n << BigInt(value.toString(2).length - 1 & -2);
292
- while (bit !== 0n) {
293
- if (value >= res + bit) {
294
- value -= res + bit;
295
- res = (res >> 1n) + bit;
296
- } else res >>= 1n;
297
- bit >>= 2n;
298
- }
299
- return res;
300
- }
301
- function isPerfectSquare(val) {
302
- if (val < 0n) return [false, 0n];
303
- const root = isqrt(val);
304
- return [root * root === val, root];
305
- }
306
- function sqrt(v, precise = false, precisionDigits = 100) {
307
- if (v.n < 0n) throw new InterpreterError("Square root of negative not supported yet.");
308
- if (v.n === 0n) return {
309
- n: 0n,
310
- d: 1n
311
- };
312
- if (precise) {
313
- const [nIsSquare, nRoot] = isPerfectSquare(v.n);
314
- const [dIsSquare, dRoot] = isPerfectSquare(v.d);
315
- if (nIsSquare && dIsSquare) return simplify({
316
- n: nRoot,
317
- d: dRoot
318
- });
319
- }
320
- const scaleFactor = 10n ** BigInt(precisionDigits * 2);
321
- return simplify({
322
- n: isqrt(v.n * scaleFactor / v.d),
323
- d: 10n ** BigInt(precisionDigits)
324
- });
325
- }
326
- //#endregion
327
536
  //#region src/lib/interpreter.ts
328
537
  const precedence = {
329
538
  LPAREN: 0,
@@ -346,77 +555,90 @@ const precedence = {
346
555
  function isUnaryOperation(op) {
347
556
  return op === "UNARY_PLUS" || op === "UNARY_MINUS" || op === "ABS_FN" || op === "CEIL_FN" || op === "FLOOR_FN" || op === "SQRT_FN";
348
557
  }
349
- function getConst(id) {
350
- const c = constants[id];
351
- return {
352
- n: BigInt(c.replace(".", "")),
353
- d: 10n ** BigInt(c.length - c.indexOf(".") - 1)
354
- };
355
- }
356
558
  /**
357
559
  * Maximum allowed precision
358
560
  */
359
561
  const MAX_PRECISION = 5e4;
360
562
  /**
361
- * Threshold to prevent denominators from growing infinitely.
563
+ * Maximum allowed factorial
362
564
  */
363
- const SIMPLIFY_THRESHOLD = 10n ** 4000n;
565
+ const MAX_FACTORIAL = 2e4;
566
+ /**
567
+ * Maximum allowed exponent for scientific number notation
568
+ */
569
+ const MAX_SCIENTIFIC_NUMBER_EXPONENT = 3e5;
364
570
  function evaluate(tokens, options = {}) {
365
571
  if (tokens.length === 0) throw new EmptyExpressionError();
366
- const { format } = options;
367
572
  const values = [];
368
573
  const ops = [];
369
574
  const applyOp = (pos) => {
370
575
  const op = ops.pop();
371
576
  if (!op || op === "LPAREN" || op === "ABS_OPEN") return;
372
577
  const right = values.pop();
373
- if (right === void 0) throw new UnexpectedEndOfExpressionError();
374
578
  if (isUnaryOperation(op)) switch (op) {
375
579
  case "UNARY_PLUS":
376
580
  values.push(right);
377
581
  return;
378
- case "UNARY_MINUS":
582
+ case "UNARY_MINUS": {
583
+ const rN = right.n;
584
+ if (rN === "OVERFLOW") {
585
+ values.push(OverflowValue);
586
+ return;
587
+ }
379
588
  values.push({
380
- n: -right.n,
381
- d: right.d,
382
- c: right.c
589
+ ...right,
590
+ n: -rN
383
591
  });
384
592
  return;
593
+ }
385
594
  case "ABS_FN": {
386
595
  const rN = right.n;
596
+ if (rN === "OVERFLOW") {
597
+ values.push(OverflowValue);
598
+ return;
599
+ }
600
+ const n = rN < 0 ? -rN : rN;
387
601
  values.push({
388
- n: rN < 0n ? -rN : rN,
389
- d: right.d,
390
- c: right.c
602
+ ...right,
603
+ n
391
604
  });
392
605
  return;
393
606
  }
394
607
  case "CEIL_FN":
608
+ if (right.n === "OVERFLOW") {
609
+ values.push(OverflowValue);
610
+ return;
611
+ }
395
612
  values.push({
396
- n: ceil(right),
613
+ n: ceil(toSimpleFraction(right)),
397
614
  d: 1n
398
615
  });
399
616
  return;
400
617
  case "FLOOR_FN":
618
+ if (right.n === "OVERFLOW") {
619
+ values.push(OverflowValue);
620
+ return;
621
+ }
401
622
  values.push({
402
- n: floor(right),
623
+ n: floor(toSimpleFraction(right)),
403
624
  d: 1n
404
625
  });
405
626
  return;
406
- case "SQRT_FN": {
407
- if (right.c === void 0) {
408
- values.push(sqrt(right, format === "precise"));
409
- return;
410
- }
411
- const v = multiply(right, getConst(right.c));
412
- values.push(sqrt(v, format === "precise"));
627
+ case "SQRT_FN":
628
+ values.push(sqrt(right, options));
413
629
  return;
414
- }
415
630
  }
416
631
  if (op === "FACTORIAL") {
632
+ if (right.n === "OVERFLOW") {
633
+ values.push(OverflowValue);
634
+ return;
635
+ }
417
636
  const reduced = simplify(right);
418
- if (reduced.d !== 1n || reduced.n < 0n) throw new InterpreterError("Factorial is only defined for non-negative integers", pos);
419
- if (reduced.n >= 4e5) throw new OverflowError();
637
+ if (reduced.d !== 1n || reduced.n < 0) throw new NotImplementedError("Factorial is only implemented for non-negative integers.", pos);
638
+ if (reduced.n >= MAX_FACTORIAL) {
639
+ values.push(OverflowValue);
640
+ return;
641
+ }
420
642
  values.push({
421
643
  n: factorial(reduced.n),
422
644
  d: 1n
@@ -424,153 +646,29 @@ function evaluate(tokens, options = {}) {
424
646
  return;
425
647
  }
426
648
  const left = values.pop();
427
- if (left === void 0) throw new InsufficientOperandsError(pos);
428
- let resN;
429
- let resD;
430
- let resC;
431
- const lN = left.n;
432
- const lD = left.d;
433
- const lC = left.c;
434
- const rN = right.n;
435
- const rD = right.d;
436
- const rC = right.c;
437
649
  switch (op) {
438
650
  case "ADD":
439
- case "SUBTRACT": {
440
- const isSub = op === "SUBTRACT";
441
- if (lN === 0n) {
442
- resN = isSub ? -rN : rN;
443
- resD = rD;
444
- resC = rC;
445
- break;
446
- }
447
- if (rN === 0n) {
448
- resN = lN;
449
- resD = lD;
450
- resC = lC;
451
- break;
452
- }
453
- if (lD === rD) {
454
- resN = isSub ? lN - rN : lN + rN;
455
- resD = lD;
456
- } else {
457
- const common = gcd(lD, rD);
458
- if (common === 1n) {
459
- resN = isSub ? lN * rD - rN * lD : lN * rD + rN * lD;
460
- resD = lD * rD;
461
- } else {
462
- const mLeft = rD / common;
463
- const mRight = lD / common;
464
- resN = isSub ? lN * mLeft - rN * mRight : lN * mLeft + rN * mRight;
465
- resD = lD * mLeft;
466
- }
467
- }
468
- if (lC === rC && resN !== 0n) resC = lC;
469
- break;
470
- }
651
+ case "SUBTRACT":
652
+ values.push(add(left, right, op === "SUBTRACT"));
653
+ return;
471
654
  case "MULTIPLY":
472
- case "IMPLICIT_MUL": {
473
- const result = multiply(left, right);
474
- resN = result.n;
475
- resD = result.d;
476
- resC = result.c;
477
- break;
478
- }
479
- case "DIVIDE": {
480
- const g1 = gcd(lN, rN);
481
- const g2 = gcd(rD, lD);
482
- resN = lN / g1 * (rD / g2);
483
- resD = lD / g2 * (rN / g1);
484
- if (lC !== void 0 && rC === void 0) resC = lC;
485
- break;
486
- }
487
- case "MOD": {
488
- if (rN === 0n) throw new DivisionByZeroError();
489
- const { n, d } = mod({
490
- n: lN,
491
- d: lD
492
- }, {
493
- n: rN,
494
- d: rD
495
- });
496
- resN = n;
497
- resD = d;
498
- break;
499
- }
500
- case "EXP": {
501
- const normalizedExponent = simplify(right);
502
- let exponent = normalizedExponent.n;
503
- if (exponent === 0n) {
504
- resN = 1n;
505
- resD = 1n;
506
- break;
507
- }
508
- if (lN === 0n) {
509
- if (rN < 0) throw new DivisionByZeroError();
510
- resN = 0n;
511
- resD = 1n;
512
- break;
513
- }
514
- if (lN === lD && lC === void 0) {
515
- resN = 1n;
516
- resD = 1n;
517
- break;
518
- }
519
- const exponentD = normalizedExponent.d;
520
- if (exponentD === 2n) {
521
- const rootResult = sqrt({
522
- n: lN ** exponent,
523
- d: lD ** exponent
524
- }, format === "precise");
525
- resN = rootResult.n;
526
- resD = rootResult.d;
527
- resC = rootResult.c;
528
- break;
529
- }
530
- if (exponentD !== 1n) {
531
- const rootResult = nthRoot({
532
- n: lN ** exponent,
533
- d: lD ** exponent
534
- }, exponentD, format === "precise");
535
- resN = rootResult.n;
536
- resD = rootResult.d;
537
- resC = rootResult.c;
538
- break;
539
- }
540
- let baseN = lN;
541
- let baseD = lD;
542
- if (exponent < 0n) {
543
- [baseN, baseD] = [baseD, baseN];
544
- exponent = -exponent;
545
- }
546
- if (exponent === 1n) {
547
- resN = baseN;
548
- resD = baseD;
549
- if (normalizedExponent.c === void 0) resC = lC;
550
- break;
551
- }
552
- if (exponent > 1e4 && (baseN * exponent > 6e6 || baseD * exponent > 6e6)) throw new OverflowError();
553
- if (!lC) {
554
- resN = baseN ** exponent;
555
- resD = baseD ** exponent;
556
- break;
655
+ case "IMPLICIT_MUL":
656
+ values.push(multiply(left, right));
657
+ return;
658
+ case "DIVIDE":
659
+ values.push(divide(left, right));
660
+ return;
661
+ case "MOD":
662
+ if (left.n === "OVERFLOW" || right.n === "OVERFLOW") {
663
+ values.push(OverflowValue);
664
+ return;
557
665
  }
558
- const c = getConst(lC);
559
- resN = (baseN * c.n) ** exponent;
560
- resD = (baseD * c.d) ** exponent;
561
- break;
562
- }
666
+ values.push(mod(left, right));
667
+ return;
668
+ case "EXP":
669
+ values.push(exponentiate(left, right, options));
670
+ return;
563
671
  }
564
- if (resD > SIMPLIFY_THRESHOLD) values.push(simplify({
565
- n: resN,
566
- d: resD,
567
- c: resC
568
- }));
569
- else values.push({
570
- n: resN,
571
- d: resD,
572
- c: resC
573
- });
574
672
  };
575
673
  const pushOpWithPrecedence = (currentOp, pos) => {
576
674
  const isUnary = isUnaryOperation(currentOp);
@@ -578,126 +676,124 @@ function evaluate(tokens, options = {}) {
578
676
  while (ops.length > 0 && !isUnary && (isRightAssociative ? precedence[ops[ops.length - 1]] > precedence[currentOp] : precedence[ops[ops.length - 1]] >= precedence[currentOp])) applyOp(pos);
579
677
  ops.push(currentOp);
580
678
  };
581
- for (let i = 0; i < tokens.length; i++) {
582
- const token = tokens[i];
583
- switch (token.type) {
584
- case "NUMBER": {
585
- const frac = token.fraction || "";
586
- const fl = frac.length;
587
- if (fl > MAX_PRECISION) throw new MaximumPrecisionError(fl, MAX_PRECISION);
588
- let n = BigInt(token.whole + frac);
589
- let d = fl === 0 ? 1n : 10n ** BigInt(fl);
590
- if (token.exponent) {
591
- if (token.exponent.length > MAX_PRECISION) throw new MaximumPrecisionError(token.exponent.length, MAX_PRECISION);
592
- const expValue = BigInt(token.exponent);
593
- if (expValue > 2e6) throw new OverflowError();
594
- if (expValue >= 0n) n *= 10n ** expValue;
595
- else d *= 10n ** -expValue;
679
+ for (const token of tokens) switch (token.type) {
680
+ case "NUMBER": {
681
+ const frac = token.fraction || "";
682
+ const fl = frac.length;
683
+ if (fl > MAX_PRECISION) throw new MaximumPrecisionError(fl, MAX_PRECISION);
684
+ let n = BigInt(token.whole + frac);
685
+ let d = fl === 0 ? 1n : 10n ** BigInt(fl);
686
+ if (token.exponent) {
687
+ if (token.exponent.length > MAX_PRECISION) throw new MaximumPrecisionError(token.exponent.length, MAX_PRECISION);
688
+ const expValue = BigInt(token.exponent);
689
+ if (expValue > MAX_SCIENTIFIC_NUMBER_EXPONENT) {
690
+ values.push(OverflowValue);
691
+ break;
596
692
  }
597
- if (d === 1n) values.push({
598
- n,
599
- d: 1n
600
- });
601
- else values.push(simplify({
602
- n,
603
- d
604
- }));
605
- break;
693
+ if (expValue >= 0) n *= 10n ** expValue;
694
+ else d *= 10n ** -expValue;
606
695
  }
607
- case "CONST":
608
- if (format === "precise") values.push({
609
- n: 1n,
610
- d: 1n,
611
- c: token.id
612
- });
613
- else values.push(getConst(token.id));
614
- break;
615
- case "LPAREN":
616
- ops.push("LPAREN");
617
- break;
618
- case "RPAREN": {
619
- let foundMatch = false;
620
- while (ops.length > 0) {
621
- if (ops[ops.length - 1] === "LPAREN") {
622
- foundMatch = true;
623
- break;
624
- }
625
- applyOp(token.pos);
696
+ if (d === 1n) values.push({
697
+ n,
698
+ d: 1n
699
+ });
700
+ else values.push({
701
+ n,
702
+ d
703
+ });
704
+ break;
705
+ }
706
+ case "CONST":
707
+ if (options.format === "precise") values.push({
708
+ n: 1n,
709
+ d: 1n,
710
+ c: token.id
711
+ });
712
+ else values.push(getConst(token.id));
713
+ break;
714
+ case "LPAREN":
715
+ ops.push("LPAREN");
716
+ break;
717
+ case "RPAREN": {
718
+ let foundMatch = false;
719
+ while (ops.length > 0) {
720
+ if (ops[ops.length - 1] === "LPAREN") {
721
+ foundMatch = true;
722
+ break;
626
723
  }
627
- if (!foundMatch) throw new MismatchedParenthesisError(token.pos);
628
- ops.pop();
629
- break;
724
+ applyOp(token.pos);
630
725
  }
631
- case "ABS_OPEN":
632
- ops.push("ABS_OPEN");
633
- break;
634
- case "ABS_CLOSE": {
635
- let foundMatch = false;
636
- while (ops.length > 0) {
637
- if (ops[ops.length - 1] === "ABS_OPEN") {
638
- foundMatch = true;
639
- break;
640
- }
641
- applyOp(token.pos);
642
- }
643
- if (!foundMatch) throw new InterpreterError("Mismatched absolute value pipe", token.pos);
644
- ops.pop();
645
- const val = values.pop();
646
- if (val === void 0) throw new UnexpectedEndOfExpressionError();
647
- values.push({
648
- n: val.n < 0n ? -val.n : val.n,
649
- d: val.d,
650
- c: val.c
651
- });
652
- break;
726
+ if (!foundMatch) throw new MismatchedParenthesisError(token.pos);
727
+ ops.pop();
728
+ break;
729
+ }
730
+ case "ABS_OPEN":
731
+ ops.push("ABS_OPEN");
732
+ break;
733
+ case "ABS_CLOSE": {
734
+ while (ops.length > 0) {
735
+ if (ops[ops.length - 1] === "ABS_OPEN") break;
736
+ applyOp(token.pos);
653
737
  }
654
- case "PLUS":
655
- pushOpWithPrecedence("ADD", token.pos);
656
- break;
657
- case "MINUS":
658
- pushOpWithPrecedence("SUBTRACT", token.pos);
659
- break;
660
- case "MUL":
661
- pushOpWithPrecedence("MULTIPLY", token.pos);
662
- break;
663
- case "DIV":
664
- pushOpWithPrecedence("DIVIDE", token.pos);
665
- break;
666
- case "POW":
667
- pushOpWithPrecedence("EXP", token.pos);
668
- break;
669
- case "UNARY_PLUS":
670
- pushOpWithPrecedence("UNARY_PLUS", token.pos);
671
- break;
672
- case "UNARY_MINUS":
673
- pushOpWithPrecedence("UNARY_MINUS", token.pos);
674
- break;
675
- case "IMPLICIT_MUL":
676
- pushOpWithPrecedence("IMPLICIT_MUL", token.pos);
677
- break;
678
- case "FACTORIAL":
679
- pushOpWithPrecedence("FACTORIAL", token.pos);
680
- break;
681
- case "MOD":
682
- pushOpWithPrecedence("MOD", token.pos);
683
- break;
684
- case "FUNC":
685
- switch (token.id) {
686
- case "abs":
687
- pushOpWithPrecedence("ABS_FN", token.pos);
688
- break;
689
- case "ceil":
690
- pushOpWithPrecedence("CEIL_FN", token.pos);
691
- break;
692
- case "floor":
693
- pushOpWithPrecedence("FLOOR_FN", token.pos);
694
- break;
695
- case "sqrt":
696
- pushOpWithPrecedence("SQRT_FN", token.pos);
697
- break;
698
- }
738
+ ops.pop();
739
+ const val = values.pop();
740
+ if (val.n === "OVERFLOW") {
741
+ values.push(OverflowValue);
699
742
  break;
743
+ }
744
+ values.push({
745
+ n: val.n < 0 ? -val.n : val.n,
746
+ d: val.d,
747
+ c: val.c
748
+ });
749
+ break;
700
750
  }
751
+ case "PLUS":
752
+ pushOpWithPrecedence("ADD", token.pos);
753
+ break;
754
+ case "MINUS":
755
+ pushOpWithPrecedence("SUBTRACT", token.pos);
756
+ break;
757
+ case "MUL":
758
+ pushOpWithPrecedence("MULTIPLY", token.pos);
759
+ break;
760
+ case "DIV":
761
+ pushOpWithPrecedence("DIVIDE", token.pos);
762
+ break;
763
+ case "POW":
764
+ pushOpWithPrecedence("EXP", token.pos);
765
+ break;
766
+ case "UNARY_PLUS":
767
+ pushOpWithPrecedence("UNARY_PLUS", token.pos);
768
+ break;
769
+ case "UNARY_MINUS":
770
+ pushOpWithPrecedence("UNARY_MINUS", token.pos);
771
+ break;
772
+ case "IMPLICIT_MUL":
773
+ pushOpWithPrecedence("IMPLICIT_MUL", token.pos);
774
+ break;
775
+ case "FACTORIAL":
776
+ pushOpWithPrecedence("FACTORIAL", token.pos);
777
+ break;
778
+ case "MOD":
779
+ pushOpWithPrecedence("MOD", token.pos);
780
+ break;
781
+ case "FUNC":
782
+ switch (token.id) {
783
+ case "abs":
784
+ pushOpWithPrecedence("ABS_FN", token.pos);
785
+ break;
786
+ case "ceil":
787
+ pushOpWithPrecedence("CEIL_FN", token.pos);
788
+ break;
789
+ case "floor":
790
+ pushOpWithPrecedence("FLOOR_FN", token.pos);
791
+ break;
792
+ case "sqrt":
793
+ pushOpWithPrecedence("SQRT_FN", token.pos);
794
+ break;
795
+ }
796
+ break;
701
797
  }
702
798
  while (ops.length > 0) {
703
799
  const top = ops[ops.length - 1];
@@ -709,11 +805,16 @@ function evaluate(tokens, options = {}) {
709
805
  applyOp(lastPos);
710
806
  }
711
807
  const finalValue = values.pop();
712
- if (finalValue === void 0) throw new UnexpectedEndOfExpressionError();
808
+ if (finalValue.n === "OVERFLOW") throw new OverflowError();
713
809
  return simplify(finalValue);
714
810
  }
715
811
  //#endregion
716
812
  //#region src/lib/lexer.ts
813
+ var LexerError = class extends GenericMathErrror {
814
+ constructor(message, pos) {
815
+ super("LexerError", message, pos);
816
+ }
817
+ };
717
818
  function isWhitespace(ch) {
718
819
  return ch === " ";
719
820
  }
@@ -726,6 +827,10 @@ function isAlpha(ch) {
726
827
  return ch >= "a" && ch <= "z";
727
828
  }
728
829
  /**
830
+ * Maximum allowed expression length
831
+ */
832
+ const MAX_EXPRESSION_LENGTH = 75e6;
833
+ /**
729
834
  * Simple lexer (tokenizer).
730
835
  */
731
836
  function tokenize(expression, options = {}) {
@@ -733,6 +838,7 @@ function tokenize(expression, options = {}) {
733
838
  const tokens = [];
734
839
  let index = 0;
735
840
  const length = expression.length;
841
+ if (length > MAX_EXPRESSION_LENGTH) throw new LexerError(`Expression length (${length}) exceeds the maximum allowed limit of ${MAX_EXPRESSION_LENGTH}`, 0);
736
842
  while (index < length) {
737
843
  const ch = expression[index];
738
844
  const startPos = index;
@@ -744,31 +850,22 @@ function tokenize(expression, options = {}) {
744
850
  let whole = "";
745
851
  let fraction = void 0;
746
852
  let exponent = void 0;
853
+ const startIdx = index;
747
854
  if (ch === decimalSeparator) {
748
855
  whole = "0";
749
856
  index++;
750
857
  const fracStart = index;
751
- let fracBuffer = "";
752
- while (index < length && isDigit(expression[index])) {
753
- fracBuffer += expression[index];
754
- index++;
755
- }
858
+ while (index < length && isDigit(expression[index])) index++;
756
859
  if (index === fracStart) throw new LexerError("Expected digit after decimal separator", index);
757
- fraction = fracBuffer;
860
+ fraction = expression.slice(fracStart, index);
758
861
  } else {
759
- while (index < length && isDigit(expression[index])) {
760
- whole += expression[index];
761
- index++;
762
- }
862
+ while (index < length && isDigit(expression[index])) index++;
863
+ whole = expression.slice(startIdx, index);
763
864
  if (index < length && expression[index] === decimalSeparator) {
764
865
  index++;
765
- let fracBuffer = "";
766
866
  const fracStart = index;
767
- while (index < length && isDigit(expression[index])) {
768
- fracBuffer += expression[index];
769
- index++;
770
- }
771
- fraction = index === fracStart ? "0" : fracBuffer;
867
+ while (index < length && isDigit(expression[index])) index++;
868
+ fraction = expression.slice(fracStart, index);
772
869
  }
773
870
  }
774
871
  if (index < length && (expression[index] === "e" || expression[index] === "E")) {
@@ -936,6 +1033,21 @@ function isBinaryOperator(token) {
936
1033
  function isUnaryContext(last) {
937
1034
  return !isOperand(last);
938
1035
  }
1036
+ var ParserError = class extends GenericMathErrror {
1037
+ constructor(message, pos) {
1038
+ super("ParserError", message, pos);
1039
+ }
1040
+ };
1041
+ var IncompleteExpressionError = class extends ParserError {
1042
+ constructor(message, pos) {
1043
+ super(`Incomplete expression: ${message}`, pos);
1044
+ }
1045
+ };
1046
+ var UnexpectedOperatorError = class extends ParserError {
1047
+ constructor(message, pos) {
1048
+ super(`Unexpected operator: ${message}`, pos);
1049
+ }
1050
+ };
939
1051
  function resolveIdentifier(token) {
940
1052
  if (token.id === "pi" || token.id === "e") return {
941
1053
  type: "CONST",
@@ -986,14 +1098,14 @@ function parse(tokens) {
986
1098
  }
987
1099
  prevParsed = result[result.length - 1];
988
1100
  if (isBinaryOperator(token)) {
989
- if (isUnaryContext(prevParsed)) throw new ParserError(`Unexpected operator '${getSym(token)}'`, token.pos);
1101
+ if (isUnaryContext(prevParsed)) throw new UnexpectedOperatorError(`Unexpected operator '${getSym(token)}'`, token.pos);
990
1102
  }
991
1103
  if (token.type === "FACTORIAL") {
992
- if (!isOperand(prevParsed) && prevParsed?.type !== "RPAREN") throw new ParserError("Unexpected factorial operator", token.pos);
1104
+ if (!isOperand(prevParsed) && prevParsed?.type !== "RPAREN") throw new UnexpectedOperatorError("Unexpected factorial operator", token.pos);
993
1105
  }
994
1106
  if (token.type === "RPAREN") {
995
- if (prevParsed && (prevParsed.type === "PLUS" || prevParsed.type === "MINUS" || isBinaryOperator(prevParsed))) throw new ParserError(`Unexpected ')' after operator '${getSym(prevParsed)}'`, token.pos);
996
- if (prevParsed?.type === "LPAREN") throw new ParserError("Unexpected ')' after '('", token.pos);
1107
+ if (prevParsed && (prevParsed.type === "PLUS" || prevParsed.type === "MINUS" || isBinaryOperator(prevParsed))) throw new UnexpectedOperatorError(`Unexpected ')' after operator '${getSym(prevParsed)}'`, token.pos);
1108
+ if (prevParsed?.type === "LPAREN") throw new UnexpectedOperatorError("Unexpected ')' after '('", token.pos);
997
1109
  }
998
1110
  if (prev && (prev.type === "NUMBER" || prev.type === "IDENTIFIER" && resolveIdentifier(prev).type === "CONST") && token.type === "NUMBER") throw new ParserError("Missing operator between numbers", token.pos);
999
1111
  if ((token.type === "PLUS" || token.type === "MINUS") && isUnaryContext(prevParsed)) result.push({
@@ -1010,33 +1122,55 @@ function parse(tokens) {
1010
1122
  "UNARY_PLUS",
1011
1123
  "UNARY_MINUS",
1012
1124
  "FUNC",
1013
- "ABS_OPEN"
1125
+ "ABS_OPEN",
1126
+ "LPAREN"
1014
1127
  ].includes(last.type))) throw new IncompleteExpressionError(`trailing operator '${last.type === "FUNC" ? last.id : getSym(last)}'`, last.pos);
1015
1128
  return result;
1016
1129
  }
1017
1130
  //#endregion
1018
1131
  //#region src/utils/format-result.ts
1019
- const getConstantStr = (coeff, c) => {
1020
- if (!c) return coeff;
1021
- if (coeff === "0") return "0";
1022
- if (coeff === "1") return c;
1023
- if (coeff === "-1") return `-${c}`;
1024
- return `${coeff}${c}`;
1025
- };
1132
+ function getConstantStr(coeff, c, e) {
1133
+ if (coeff === 0n) return "0";
1134
+ if (!c) return `${coeff}`;
1135
+ const isOne = e && e.n === 1n && (e.d === void 0 || e.d === 1n);
1136
+ if (!e || isOne) {
1137
+ if (coeff === 1n) return c;
1138
+ if (coeff === -1n) return `-${c}`;
1139
+ return `${coeff}${c}`;
1140
+ }
1141
+ const num = e.n;
1142
+ const den = e.d ?? 1n;
1143
+ const expStr = den === 1n ? `${num}` : `(${num}/${den})`;
1144
+ const constantStr = den === 2n * num ? `sqrt(${c})` : `${c}^${expStr}`;
1145
+ if (coeff === 1n) return constantStr;
1146
+ if (coeff === -1n) return `-${constantStr}`;
1147
+ return `${coeff}${constantStr}`;
1148
+ }
1149
+ function formatPrecise(v) {
1150
+ const { n, d, c, e } = v;
1151
+ if (n === 0n) return "0";
1152
+ const isNegative = n < 0;
1153
+ const absN = isNegative ? -n : n;
1154
+ const numSign = isNegative ? "-" : "";
1155
+ if (c && e && e.n < 0n) return `${numSign}${absN}/${getConstantStr(d, c, {
1156
+ n: -e.n,
1157
+ d: e.d
1158
+ })}`;
1159
+ const numeratorStr = getConstantStr(absN, c, e);
1160
+ if (d === 1n) return `${numSign}${numeratorStr}`;
1161
+ return `${numSign}${numeratorStr}/${d}`;
1162
+ }
1026
1163
  /**
1027
1164
  * Converts Result to a Decimal or Fraction String.
1028
1165
  */
1029
1166
  function formatResult(v, options = {}) {
1030
- const { n, d, c } = v;
1167
+ const { n, d } = v;
1031
1168
  if (d === 0n) return "NaN";
1032
- if (options.format === "precise") {
1033
- if (d === 1n) return getConstantStr(n.toString(), c);
1034
- return `${getConstantStr(n.toString(), c)}/${d}`;
1035
- }
1036
- const maxDecimals = options.maxDecimals ?? 30;
1037
- const isNegative = n < 0n;
1038
- const absN = n < 0n ? -n : n;
1039
- const integerPart = (absN / d).toString();
1169
+ if (options.format === "precise") return formatPrecise(v);
1170
+ const { maxDecimals = 30, roundingMode = "round" } = options;
1171
+ const isNegative = n < 0;
1172
+ const absN = isNegative ? -n : n;
1173
+ let integerPart = (absN / d).toString();
1040
1174
  let remainder = absN % d;
1041
1175
  if (remainder === 0n) {
1042
1176
  const res = (isNegative ? "-" : "") + integerPart;
@@ -1044,12 +1178,25 @@ function formatResult(v, options = {}) {
1044
1178
  }
1045
1179
  let fractionalPart = "";
1046
1180
  let count = 0;
1047
- while (remainder !== 0n && count < maxDecimals) {
1181
+ const targetLength = maxDecimals + 1;
1182
+ while (remainder !== 0n && count < targetLength) {
1048
1183
  remainder *= 10n;
1049
1184
  fractionalPart += (remainder / d).toString();
1050
1185
  remainder %= d;
1051
1186
  count++;
1052
1187
  }
1188
+ const nexDigitStr = fractionalPart[maxDecimals];
1189
+ if (nexDigitStr) {
1190
+ const nextDigit = parseInt(nexDigitStr, 10);
1191
+ fractionalPart = fractionalPart.slice(0, maxDecimals);
1192
+ if (roundingMode === "round" && nextDigit >= 5) {
1193
+ const incremented = (BigInt(fractionalPart) + 1n).toString();
1194
+ if (incremented.length > fractionalPart.length) {
1195
+ integerPart = (BigInt(integerPart) + 1n).toString();
1196
+ fractionalPart = incremented.slice(1);
1197
+ } else fractionalPart = incremented.padStart(fractionalPart.length, "0");
1198
+ }
1199
+ }
1053
1200
  let lastNonZero = -1;
1054
1201
  for (let i = fractionalPart.length - 1; i >= 0; i--) if (fractionalPart[i] !== "0") {
1055
1202
  lastNonZero = i;