picocalc 0.0.1 → 0.0.3

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,26 +1,15 @@
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
- var GenericMathErrror = class extends Error {
3
+ var PicocalcErrror = class extends Error {
10
4
  pos;
11
5
  constructor(name, message, pos) {
12
- if (pos === void 0) super(`${name}: ${message}`);
13
- else super(`${name}: ${message} at position ${pos}`);
6
+ if (pos === void 0) super(message);
7
+ else super(`${message} at position ${pos}`);
14
8
  this.pos = pos;
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
- var InterpreterError = class extends GenericMathErrror {
12
+ var InterpreterError = class extends PicocalcErrror {
24
13
  constructor(message, pos) {
25
14
  super("InterpreterError", message, pos);
26
15
  }
@@ -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];
88
+ }
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
+ };
90
164
  }
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;
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
+ };
212
+ return {
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);
114
273
  return {
115
- n: v.n / common * sign,
116
- d: v.d / common * sign,
117
- c: v.c
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 (simplify(right).n % 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,
@@ -343,80 +552,96 @@ const precedence = {
343
552
  SQRT_FN: 8,
344
553
  FACTORIAL: 10
345
554
  };
346
- function isUnaryOperation(op) {
347
- return op === "UNARY_PLUS" || op === "UNARY_MINUS" || op === "ABS_FN" || op === "CEIL_FN" || op === "FLOOR_FN" || op === "SQRT_FN";
555
+ function isFunctionOp(op) {
556
+ return 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
- };
558
+ function isUnaryOperation(op) {
559
+ return op === "UNARY_PLUS" || op === "UNARY_MINUS" || isFunctionOp(op);
355
560
  }
356
561
  /**
357
562
  * Maximum allowed precision
358
563
  */
359
564
  const MAX_PRECISION = 5e4;
360
565
  /**
361
- * Threshold to prevent denominators from growing infinitely.
566
+ * Maximum allowed factorial
567
+ */
568
+ const MAX_FACTORIAL = 2e4;
569
+ /**
570
+ * Maximum allowed exponent for scientific number notation
362
571
  */
363
- const SIMPLIFY_THRESHOLD = 10n ** 4000n;
572
+ const MAX_SCIENTIFIC_NUMBER_EXPONENT = 3e5;
364
573
  function evaluate(tokens, options = {}) {
365
574
  if (tokens.length === 0) throw new EmptyExpressionError();
366
- const { format } = options;
367
575
  const values = [];
368
576
  const ops = [];
369
577
  const applyOp = (pos) => {
370
578
  const op = ops.pop();
371
579
  if (!op || op === "LPAREN" || op === "ABS_OPEN") return;
372
580
  const right = values.pop();
373
- if (right === void 0) throw new UnexpectedEndOfExpressionError();
374
581
  if (isUnaryOperation(op)) switch (op) {
375
582
  case "UNARY_PLUS":
376
583
  values.push(right);
377
584
  return;
378
- case "UNARY_MINUS":
585
+ case "UNARY_MINUS": {
586
+ const rN = right.n;
587
+ if (rN === "OVERFLOW") {
588
+ values.push(OverflowValue);
589
+ return;
590
+ }
379
591
  values.push({
380
- n: -right.n,
381
- d: right.d,
382
- c: right.c
592
+ ...right,
593
+ n: -rN
383
594
  });
384
595
  return;
596
+ }
385
597
  case "ABS_FN": {
386
598
  const rN = right.n;
599
+ if (rN === "OVERFLOW") {
600
+ values.push(OverflowValue);
601
+ return;
602
+ }
603
+ const n = rN < 0 ? -rN : rN;
387
604
  values.push({
388
- n: rN < 0n ? -rN : rN,
389
- d: right.d,
390
- c: right.c
605
+ ...right,
606
+ n
391
607
  });
392
608
  return;
393
609
  }
394
610
  case "CEIL_FN":
611
+ if (right.n === "OVERFLOW") {
612
+ values.push(OverflowValue);
613
+ return;
614
+ }
395
615
  values.push({
396
- n: ceil(right),
616
+ n: ceil(toSimpleFraction(right)),
397
617
  d: 1n
398
618
  });
399
619
  return;
400
620
  case "FLOOR_FN":
621
+ if (right.n === "OVERFLOW") {
622
+ values.push(OverflowValue);
623
+ return;
624
+ }
401
625
  values.push({
402
- n: floor(right),
626
+ n: floor(toSimpleFraction(right)),
403
627
  d: 1n
404
628
  });
405
629
  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"));
630
+ case "SQRT_FN":
631
+ values.push(sqrt(right, options));
413
632
  return;
414
- }
415
633
  }
416
634
  if (op === "FACTORIAL") {
635
+ if (right.n === "OVERFLOW") {
636
+ values.push(OverflowValue);
637
+ return;
638
+ }
417
639
  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();
640
+ if (reduced.d !== 1n || reduced.n < 0) throw new NotImplementedError("Factorial is only implemented for non-negative integers.", pos);
641
+ if (reduced.n >= MAX_FACTORIAL) {
642
+ values.push(OverflowValue);
643
+ return;
644
+ }
420
645
  values.push({
421
646
  n: factorial(reduced.n),
422
647
  d: 1n
@@ -424,284 +649,151 @@ function evaluate(tokens, options = {}) {
424
649
  return;
425
650
  }
426
651
  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
652
  switch (op) {
438
653
  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
- }
654
+ case "SUBTRACT":
655
+ values.push(add(left, right, op === "SUBTRACT"));
656
+ return;
471
657
  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;
658
+ case "IMPLICIT_MUL":
659
+ values.push(multiply(left, right));
660
+ return;
661
+ case "DIVIDE":
662
+ values.push(divide(left, right));
663
+ return;
664
+ case "MOD":
665
+ if (left.n === "OVERFLOW" || right.n === "OVERFLOW") {
666
+ values.push(OverflowValue);
667
+ return;
557
668
  }
558
- const c = getConst(lC);
559
- resN = (baseN * c.n) ** exponent;
560
- resD = (baseD * c.d) ** exponent;
561
- break;
562
- }
669
+ values.push(mod(left, right));
670
+ return;
671
+ case "EXP":
672
+ values.push(exponentiate(left, right, options));
673
+ return;
563
674
  }
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
675
  };
575
676
  const pushOpWithPrecedence = (currentOp, pos) => {
576
677
  const isUnary = isUnaryOperation(currentOp);
577
678
  const isRightAssociative = currentOp === "EXP" || currentOp === "IMPLICIT_MUL";
578
- while (ops.length > 0 && !isUnary && (isRightAssociative ? precedence[ops[ops.length - 1]] > precedence[currentOp] : precedence[ops[ops.length - 1]] >= precedence[currentOp])) applyOp(pos);
679
+ while (ops.length > 0 && !isUnary && (isRightAssociative ? precedence[ops.at(-1)] > precedence[currentOp] : precedence[ops.at(-1)] >= precedence[currentOp])) applyOp(pos);
579
680
  ops.push(currentOp);
580
681
  };
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;
682
+ for (const token of tokens) switch (token.type) {
683
+ case "NUMBER": {
684
+ const frac = token.fraction || "";
685
+ const fl = frac.length;
686
+ if (fl > MAX_PRECISION) throw new MaximumPrecisionError(fl, MAX_PRECISION);
687
+ let n = BigInt(token.whole + frac);
688
+ let d = fl === 0 ? 1n : 10n ** BigInt(fl);
689
+ if (token.exponent) {
690
+ if (token.exponent.length > MAX_PRECISION) throw new MaximumPrecisionError(token.exponent.length, MAX_PRECISION);
691
+ const expValue = BigInt(token.exponent);
692
+ if (expValue > MAX_SCIENTIFIC_NUMBER_EXPONENT) {
693
+ values.push(OverflowValue);
694
+ break;
596
695
  }
597
- if (d === 1n) values.push({
598
- n,
599
- d: 1n
600
- });
601
- else values.push(simplify({
602
- n,
603
- d
604
- }));
605
- break;
696
+ if (expValue >= 0) n *= 10n ** expValue;
697
+ else d *= 10n ** -expValue;
606
698
  }
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);
699
+ if (d === 1n) values.push({
700
+ n,
701
+ d: 1n
702
+ });
703
+ else values.push({
704
+ n,
705
+ d
706
+ });
707
+ break;
708
+ }
709
+ case "CONST":
710
+ if (options.format === "precise") values.push({
711
+ n: 1n,
712
+ d: 1n,
713
+ c: token.id
714
+ });
715
+ else values.push(getConst(token.id));
716
+ break;
717
+ case "LPAREN":
718
+ case "ABS_OPEN":
719
+ ops.push(token.type);
720
+ break;
721
+ case "RPAREN": {
722
+ let foundMatch = false;
723
+ while (ops.length > 0) {
724
+ if (ops.at(-1) === "LPAREN") {
725
+ foundMatch = true;
726
+ break;
626
727
  }
627
- if (!foundMatch) throw new MismatchedParenthesisError(token.pos);
628
- ops.pop();
629
- break;
728
+ applyOp(token.pos);
630
729
  }
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;
730
+ if (!foundMatch) throw new MismatchedParenthesisError(token.pos);
731
+ ops.pop();
732
+ const nextOp = ops.at(-1);
733
+ if (!nextOp) break;
734
+ if (isFunctionOp(nextOp)) applyOp(token.pos);
735
+ break;
736
+ }
737
+ case "ABS_CLOSE": {
738
+ while (ops.length > 0) {
739
+ if (ops.at(-1) === "ABS_OPEN") break;
740
+ applyOp(token.pos);
653
741
  }
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
- }
742
+ ops.pop();
743
+ const val = values.pop();
744
+ if (val.n === "OVERFLOW") {
745
+ values.push(OverflowValue);
699
746
  break;
747
+ }
748
+ values.push({
749
+ n: val.n < 0 ? -val.n : val.n,
750
+ d: val.d,
751
+ c: val.c
752
+ });
753
+ break;
700
754
  }
755
+ case "PLUS":
756
+ pushOpWithPrecedence("ADD", token.pos);
757
+ break;
758
+ case "MINUS":
759
+ pushOpWithPrecedence("SUBTRACT", token.pos);
760
+ break;
761
+ case "MUL":
762
+ pushOpWithPrecedence("MULTIPLY", token.pos);
763
+ break;
764
+ case "DIV":
765
+ pushOpWithPrecedence("DIVIDE", token.pos);
766
+ break;
767
+ case "POW":
768
+ pushOpWithPrecedence("EXP", token.pos);
769
+ break;
770
+ case "UNARY_PLUS":
771
+ case "UNARY_MINUS":
772
+ case "IMPLICIT_MUL":
773
+ case "FACTORIAL":
774
+ case "MOD":
775
+ pushOpWithPrecedence(token.type, token.pos);
776
+ break;
777
+ case "FUNC":
778
+ switch (token.id) {
779
+ case "abs":
780
+ pushOpWithPrecedence("ABS_FN", token.pos);
781
+ break;
782
+ case "ceil":
783
+ pushOpWithPrecedence("CEIL_FN", token.pos);
784
+ break;
785
+ case "floor":
786
+ pushOpWithPrecedence("FLOOR_FN", token.pos);
787
+ break;
788
+ case "sqrt":
789
+ pushOpWithPrecedence("SQRT_FN", token.pos);
790
+ break;
791
+ }
792
+ break;
701
793
  }
702
794
  while (ops.length > 0) {
703
- const top = ops[ops.length - 1];
704
- const lastPos = tokens[tokens.length - 1]?.pos ?? 0;
795
+ const top = ops.at(-1);
796
+ const lastPos = tokens.at(-1)?.pos ?? 0;
705
797
  if (top === "LPAREN") {
706
798
  ops.pop();
707
799
  continue;
@@ -709,14 +801,16 @@ function evaluate(tokens, options = {}) {
709
801
  applyOp(lastPos);
710
802
  }
711
803
  const finalValue = values.pop();
712
- if (finalValue === void 0) throw new UnexpectedEndOfExpressionError();
804
+ if (finalValue.n === "OVERFLOW") throw new OverflowError();
713
805
  return simplify(finalValue);
714
806
  }
715
807
  //#endregion
716
808
  //#region src/lib/lexer.ts
717
- function isWhitespace(ch) {
718
- return ch === " ";
719
- }
809
+ var LexerError = class extends PicocalcErrror {
810
+ constructor(message, pos) {
811
+ super("LexerError", message, pos);
812
+ }
813
+ };
720
814
  function isDigit(ch) {
721
815
  if (ch === void 0) return false;
722
816
  return ch >= "0" && ch <= "9";
@@ -726,49 +820,52 @@ function isAlpha(ch) {
726
820
  return ch >= "a" && ch <= "z";
727
821
  }
728
822
  /**
823
+ * Maximum allowed expression length
824
+ */
825
+ const MAX_EXPRESSION_LENGTH = 75e6;
826
+ const CHAR_MAP = {
827
+ "(": "LPAREN",
828
+ ")": "RPAREN",
829
+ "+": "PLUS",
830
+ "-": "MINUS",
831
+ "*": "MUL",
832
+ "/": "DIV",
833
+ "^": "POW",
834
+ "!": "FACTORIAL",
835
+ "|": "PIPE",
836
+ "%": "MOD"
837
+ };
838
+ /**
729
839
  * Simple lexer (tokenizer).
730
840
  */
731
841
  function tokenize(expression, options = {}) {
732
842
  const { decimalSeparator = "." } = options;
733
843
  const tokens = [];
734
- let index = 0;
735
844
  const length = expression.length;
736
- while (index < length) {
845
+ if (length > MAX_EXPRESSION_LENGTH) throw new LexerError(`Expression length (${length}) exceeds the maximum allowed limit of ${MAX_EXPRESSION_LENGTH}`, 0);
846
+ for (let index = 0; index < length; index++) {
737
847
  const ch = expression[index];
738
- const startPos = index;
739
- if (isWhitespace(ch)) {
740
- index++;
741
- continue;
742
- }
848
+ const pos = index;
849
+ if (ch === " ") continue;
743
850
  if (isDigit(ch) || ch === decimalSeparator) {
744
851
  let whole = "";
745
852
  let fraction = void 0;
746
853
  let exponent = void 0;
854
+ const consumeDigits = () => {
855
+ const start = index;
856
+ while (index < length && isDigit(expression[index])) index++;
857
+ return expression.slice(start, index);
858
+ };
747
859
  if (ch === decimalSeparator) {
748
860
  whole = "0";
749
861
  index++;
750
- const fracStart = index;
751
- let fracBuffer = "";
752
- while (index < length && isDigit(expression[index])) {
753
- fracBuffer += expression[index];
754
- index++;
755
- }
756
- if (index === fracStart) throw new LexerError("Expected digit after decimal separator", index);
757
- fraction = fracBuffer;
862
+ fraction = consumeDigits();
863
+ if (fraction.length === 0) throw new LexerError("Expected digit after decimal separator", index);
758
864
  } else {
759
- while (index < length && isDigit(expression[index])) {
760
- whole += expression[index];
761
- index++;
762
- }
865
+ whole = consumeDigits();
763
866
  if (index < length && expression[index] === decimalSeparator) {
764
867
  index++;
765
- let fracBuffer = "";
766
- const fracStart = index;
767
- while (index < length && isDigit(expression[index])) {
768
- fracBuffer += expression[index];
769
- index++;
770
- }
771
- fraction = index === fracStart ? "0" : fracBuffer;
868
+ fraction = consumeDigits();
772
869
  }
773
870
  }
774
871
  if (index < length && (expression[index] === "e" || expression[index] === "E")) {
@@ -781,115 +878,37 @@ function tokenize(expression, options = {}) {
781
878
  }
782
879
  if (lookahead < length && isDigit(expression[lookahead])) {
783
880
  index = lookahead;
784
- let expBuffer = sign;
785
- while (index < length && isDigit(expression[index])) {
786
- expBuffer += expression[index];
787
- index++;
788
- }
789
- exponent = expBuffer;
881
+ exponent = sign + consumeDigits();
790
882
  }
791
883
  }
792
884
  if (index < length && expression[index] === decimalSeparator) throw new LexerError("Invalid decimal number format", index);
885
+ index--;
793
886
  tokens.push({
794
887
  type: "NUMBER",
795
888
  whole,
796
889
  fraction,
797
890
  exponent,
798
- pos: startPos
891
+ pos
799
892
  });
800
893
  continue;
801
894
  }
802
895
  if (isAlpha(ch)) {
803
896
  let id = "";
804
897
  while (index < length && isAlpha(expression[index])) id += expression[index++];
898
+ index--;
805
899
  tokens.push({
806
900
  type: "IDENTIFIER",
807
901
  id,
808
- pos: startPos
809
- });
810
- continue;
811
- }
812
- if (ch === "(") {
813
- tokens.push({
814
- type: "LPAREN",
815
- pos: startPos
816
- });
817
- index++;
818
- continue;
819
- }
820
- if (ch === ")") {
821
- tokens.push({
822
- type: "RPAREN",
823
- pos: startPos
824
- });
825
- index++;
826
- continue;
827
- }
828
- if (ch === "+") {
829
- tokens.push({
830
- type: "PLUS",
831
- pos: startPos
832
- });
833
- index++;
834
- continue;
835
- }
836
- if (ch === "-") {
837
- tokens.push({
838
- type: "MINUS",
839
- pos: startPos
840
- });
841
- index++;
842
- continue;
843
- }
844
- if (ch === "*") {
845
- tokens.push({
846
- type: "MUL",
847
- pos: startPos
902
+ pos
848
903
  });
849
- index++;
850
904
  continue;
851
905
  }
852
- if (ch === "/") {
853
- tokens.push({
854
- type: "DIV",
855
- pos: startPos
856
- });
857
- index++;
858
- continue;
859
- }
860
- if (ch === "^") {
861
- tokens.push({
862
- type: "POW",
863
- pos: startPos
864
- });
865
- index++;
866
- continue;
867
- }
868
- if (ch === "!") {
869
- tokens.push({
870
- type: "FACTORIAL",
871
- pos: startPos
872
- });
873
- index++;
874
- continue;
875
- }
876
- if (ch === "|") {
877
- tokens.push({
878
- type: "PIPE",
879
- pos: startPos
880
- });
881
- index++;
882
- continue;
883
- }
884
- if (ch === "%") {
885
- tokens.push({
886
- type: "MOD",
887
- pos: startPos
888
- });
889
- index++;
890
- continue;
891
- }
892
- throw new LexerError(`Unexpected character '${ch}'`, index);
906
+ const type = CHAR_MAP[ch];
907
+ if (!type) throw new LexerError(`Unexpected character '${ch}'`, index);
908
+ tokens.push({
909
+ type,
910
+ pos
911
+ });
893
912
  }
894
913
  return tokens;
895
914
  }
@@ -915,7 +934,7 @@ const symbolMap = {
915
934
  const prettifyNumber = (t) => t.fraction ? `${t.whole}.${t.fraction}` : t.whole;
916
935
  function getSym(t) {
917
936
  if (t.type === "NUMBER") return prettifyNumber(t);
918
- if (t.type === "IDENTIFIER") return t.id;
937
+ if (t.type === "IDENTIFIER" || t.type === "FUNC" || t.type === "CONST") return t.id;
919
938
  return symbolMap[t.type];
920
939
  }
921
940
  //#endregion
@@ -930,22 +949,35 @@ function isOperand(last) {
930
949
  function isBinaryOperator(token) {
931
950
  return token.type === "MUL" || token.type === "DIV" || token.type === "POW" || token.type === "MOD";
932
951
  }
933
- /**
934
- * Helper: Does the current context allow a +/- to be unary?
935
- */
936
952
  function isUnaryContext(last) {
937
953
  return !isOperand(last);
938
954
  }
955
+ var ParserError = class extends PicocalcErrror {
956
+ constructor(message, pos) {
957
+ super("ParserError", message, pos);
958
+ }
959
+ };
960
+ var IncompleteExpressionError = class extends ParserError {
961
+ constructor(message, pos) {
962
+ super(`Incomplete expression: ${message}`, pos);
963
+ }
964
+ };
965
+ var UnexpectedOperatorError = class extends ParserError {
966
+ constructor(message, pos) {
967
+ super(`Unexpected operator: ${message}`, pos);
968
+ }
969
+ };
939
970
  function resolveIdentifier(token) {
971
+ const pos = token.pos;
940
972
  if (token.id === "pi" || token.id === "e") return {
941
973
  type: "CONST",
942
974
  id: token.id,
943
- pos: token.pos
975
+ pos
944
976
  };
945
977
  if (token.id === "abs" || token.id === "ceil" || token.id === "floor" || token.id === "sqrt") return {
946
978
  type: "FUNC",
947
979
  id: token.id,
948
- pos: token.pos
980
+ pos
949
981
  };
950
982
  throw new ParserError(`Unknown identifier '${token.id}'`, token.pos);
951
983
  }
@@ -957,107 +989,106 @@ function parse(tokens) {
957
989
  let token = tokens[i];
958
990
  const prev = tokens[i - 1];
959
991
  if (token.type === "IDENTIFIER") token = resolveIdentifier(token);
960
- let prevParsed = result[result.length - 1];
992
+ const pos = token.pos;
993
+ const isPrevOperand = isOperand(result.at(-1));
961
994
  if (token.type === "PIPE") {
962
- if (absStack > 0 && isOperand(prevParsed)) {
995
+ if (absStack > 0 && isPrevOperand) {
963
996
  result.push({
964
997
  type: "ABS_CLOSE",
965
- pos: token.pos
998
+ pos
966
999
  });
967
1000
  absStack--;
968
- } else {
969
- if (isOperand(prevParsed)) result.push({
970
- type: "IMPLICIT_MUL",
971
- pos: token.pos
972
- });
973
- result.push({
974
- type: "ABS_OPEN",
975
- pos: token.pos
976
- });
977
- absStack++;
1001
+ continue;
978
1002
  }
979
- continue;
980
- }
981
- if (isOperand(prevParsed)) {
982
- if (token.type === "LPAREN" || token.type === "FUNC" || token.type === "NUMBER" || token.type === "CONST") result.push({
1003
+ if (isPrevOperand) result.push({
983
1004
  type: "IMPLICIT_MUL",
984
- pos: token.pos
1005
+ pos
985
1006
  });
1007
+ result.push({
1008
+ type: "ABS_OPEN",
1009
+ pos
1010
+ });
1011
+ absStack++;
1012
+ continue;
986
1013
  }
987
- prevParsed = result[result.length - 1];
988
- if (isBinaryOperator(token)) {
989
- if (isUnaryContext(prevParsed)) throw new ParserError(`Unexpected operator '${getSym(token)}'`, token.pos);
990
- }
991
- if (token.type === "FACTORIAL") {
992
- if (!isOperand(prevParsed) && prevParsed?.type !== "RPAREN") throw new ParserError("Unexpected factorial operator", token.pos);
993
- }
994
- 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);
1014
+ if ((token.type === "LPAREN" || token.type === "FUNC" || token.type === "NUMBER" || token.type === "CONST") && isPrevOperand) result.push({
1015
+ type: "IMPLICIT_MUL",
1016
+ pos
1017
+ });
1018
+ const prevParsed = result.at(-1);
1019
+ if (isBinaryOperator(token) && isUnaryContext(prevParsed)) throw new UnexpectedOperatorError(`Unexpected operator '${getSym(token)}'`, token.pos);
1020
+ if ((token.type === "FACTORIAL" || token.type === "RPAREN") && isUnaryContext(prevParsed)) {
1021
+ if (!prevParsed) throw new UnexpectedOperatorError(`Unexpected '${getSym(token)}' at the beginning of expression`, token.pos);
1022
+ throw new UnexpectedOperatorError(`Unexpected '${getSym(token)}' after '${getSym(prevParsed)}'`, token.pos);
997
1023
  }
998
1024
  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
1025
  if ((token.type === "PLUS" || token.type === "MINUS") && isUnaryContext(prevParsed)) result.push({
1000
1026
  type: token.type === "PLUS" ? "UNARY_PLUS" : "UNARY_MINUS",
1001
- pos: token.pos
1027
+ pos
1002
1028
  });
1003
1029
  else result.push(token);
1004
1030
  }
1005
- if (absStack > 0) throw new ParserError("Unclosed absolute value '|'", result[result.length - 1]?.pos ?? 0);
1006
- const last = result[result.length - 1];
1031
+ const last = result.at(-1);
1032
+ if (absStack > 0) throw new ParserError("Unclosed absolute value '|'", last?.pos ?? 0);
1007
1033
  if (last && last.type !== "CONST" && (isBinaryOperator(last) || [
1008
1034
  "PLUS",
1009
1035
  "MINUS",
1010
1036
  "UNARY_PLUS",
1011
1037
  "UNARY_MINUS",
1012
1038
  "FUNC",
1013
- "ABS_OPEN"
1039
+ "ABS_OPEN",
1040
+ "LPAREN"
1014
1041
  ].includes(last.type))) throw new IncompleteExpressionError(`trailing operator '${last.type === "FUNC" ? last.id : getSym(last)}'`, last.pos);
1015
1042
  return result;
1016
1043
  }
1017
1044
  //#endregion
1018
1045
  //#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
- };
1046
+ function getConstantStr(coeff, c, e) {
1047
+ if (coeff === 0n) return "0";
1048
+ if (!c) return `${coeff}`;
1049
+ let constantStr = c;
1050
+ if (e) {
1051
+ const num = e.n;
1052
+ const den = e.d ?? 1n;
1053
+ if (num !== den) {
1054
+ const expStr = den === 1n ? `${num}` : `(${num}/${den})`;
1055
+ constantStr = den === 2n * num ? `sqrt(${c})` : `${c}^${expStr}`;
1056
+ }
1057
+ }
1058
+ if (coeff === 1n) return constantStr;
1059
+ if (coeff === -1n) return `-${constantStr}`;
1060
+ return `${coeff}${constantStr}`;
1061
+ }
1062
+ function formatPrecise(v) {
1063
+ const { n, d, c, e } = v;
1064
+ if (n === 0n) return "0";
1065
+ const isNegative = n < 0;
1066
+ const absN = isNegative ? -n : n;
1067
+ const numSign = isNegative ? "-" : "";
1068
+ if (c && e && e.n < 0n) return `${numSign}${absN}/${getConstantStr(d, c, {
1069
+ n: -e.n,
1070
+ d: e.d
1071
+ })}`;
1072
+ const numeratorStr = getConstantStr(absN, c, e);
1073
+ if (d === 1n) return `${numSign}${numeratorStr}`;
1074
+ return `${numSign}${numeratorStr}/${d}`;
1075
+ }
1026
1076
  /**
1027
1077
  * Converts Result to a Decimal or Fraction String.
1028
1078
  */
1029
1079
  function formatResult(v, options = {}) {
1030
- const { n, d, c } = v;
1031
- 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();
1040
- let remainder = absN % d;
1041
- if (remainder === 0n) {
1042
- const res = (isNegative ? "-" : "") + integerPart;
1043
- return res === "-0" ? "0" : res;
1044
- }
1045
- let fractionalPart = "";
1046
- let count = 0;
1047
- while (remainder !== 0n && count < maxDecimals) {
1048
- remainder *= 10n;
1049
- fractionalPart += (remainder / d).toString();
1050
- remainder %= d;
1051
- count++;
1052
- }
1053
- let lastNonZero = -1;
1054
- for (let i = fractionalPart.length - 1; i >= 0; i--) if (fractionalPart[i] !== "0") {
1055
- lastNonZero = i;
1056
- break;
1057
- }
1058
- const finalFraction = lastNonZero === -1 ? "" : fractionalPart.slice(0, lastNonZero + 1);
1059
- const result = finalFraction === "" ? integerPart : `${integerPart}.${finalFraction}`;
1060
- return (isNegative && result !== "0" ? "-" : "") + result;
1080
+ const { n, d } = v;
1081
+ if (options.format === "precise") return formatPrecise(v);
1082
+ const { maxDecimals = 30, roundingMode = "round" } = options;
1083
+ const isNegative = n < 0;
1084
+ const factor = 10n ** BigInt(maxDecimals);
1085
+ let resultStr = (((isNegative ? -n : n) * factor + (roundingMode === "round" ? d / 2n : 0n)) / d).toString();
1086
+ if (resultStr.length <= maxDecimals) resultStr = resultStr.padStart(maxDecimals + 1, "0");
1087
+ const splitIdx = resultStr.length - maxDecimals;
1088
+ const integerPart = resultStr.slice(0, splitIdx);
1089
+ const fractionalPart = resultStr.slice(splitIdx).replace(/0+$/, "");
1090
+ const finalResult = fractionalPart ? `${integerPart}.${fractionalPart}` : integerPart;
1091
+ return (isNegative && finalResult !== "0" ? "-" : "") + finalResult;
1061
1092
  }
1062
1093
  //#endregion
1063
1094
  //#region src/index.ts
@@ -1068,4 +1099,4 @@ function calculate(expression, options) {
1068
1099
  return formatResult(evaluate(parse(tokenize(expression, options)), options), options);
1069
1100
  }
1070
1101
  //#endregion
1071
- export { calculate };
1102
+ export { PicocalcErrror, calculate };