picocalc 0.0.4 → 0.0.5

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.d.mts CHANGED
@@ -1,18 +1,14 @@
1
1
  import "bigint.bitlength";
2
- //#region src/lib/types.d.ts
2
+ type RoundingMode = "round" | "truncate";
3
3
  interface PrecisionOptions {
4
4
  readonly format?: "decimal" | "precise";
5
5
  readonly maxDecimals?: number;
6
- readonly roundingMode?: "round" | "truncate";
6
+ readonly roundingMode?: RoundingMode;
7
7
  }
8
- //#endregion
9
- //#region src/lib/errors.d.ts
10
- declare class PicocalcError extends Error {
8
+ export declare class PicocalcError extends Error {
11
9
  pos?: number;
12
10
  constructor(name: string, message: string, pos?: number);
13
11
  }
14
- //#endregion
15
- //#region src/index.d.ts
16
12
  interface CalculateOptions extends PrecisionOptions {
17
13
  decimalSeparator?: "." | ",";
18
14
  }
@@ -20,5 +16,4 @@ interface CalculateOptions extends PrecisionOptions {
20
16
  * The main entry point for the library.
21
17
  */
22
18
  declare function calculate(expression: string, options?: CalculateOptions): string;
23
- //#endregion
24
- export { PicocalcError, calculate };
19
+ export { calculate };
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import "bigint.bitlength";
2
- //#region src/lib/errors.ts
2
+ // src/lib/errors.ts
3
3
  var PicocalcError = class extends Error {
4
4
  pos;
5
5
  constructor(name, message, pos) {
@@ -39,39 +39,56 @@ var EmptyExpressionError = class extends InterpreterError {
39
39
  super("Empty expression");
40
40
  }
41
41
  };
42
+ var InterpreterRangeError = class extends InterpreterError {
43
+ constructor(message) {
44
+ super(message);
45
+ }
46
+ };
42
47
  var MismatchedParenthesisError = class extends InterpreterError {
43
48
  constructor(pos, message = "Mismatched parenthesis") {
44
49
  super(message, pos);
45
50
  }
46
51
  };
47
- //#endregion
48
- //#region src/lib/types.ts
52
+ // src/lib/options.ts
53
+ const ROUNDING_MODE = "round";
54
+ function getPrecisionDigits(maxDecimals) {
55
+ return Math.max(10, (maxDecimals ?? 30) + 10);
56
+ }
57
+ /**
58
+ * Maximum allowed precision
59
+ */
60
+ const MAX_PRECISION = 5e4;
61
+ // src/lib/types.ts
49
62
  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
57
- };
58
- const ONE = {
59
- n: 1n,
60
- d: 1n
61
- };
62
- const constants = {
63
- e: E,
64
- pi: PI
65
- };
66
- function getConst(id) {
67
- const c = constants[id];
63
+ // src/lib/utils/e.ts
64
+ function splitSeries(a, b) {
65
+ if (b - a === 1n) return {
66
+ n: 1n,
67
+ d: b
68
+ };
69
+ const mid = (a + b) / 2n;
70
+ const left = splitSeries(a, mid);
71
+ const right = splitSeries(mid, b);
72
+ return {
73
+ n: left.n * right.d + right.n,
74
+ d: left.d * right.d
75
+ };
76
+ }
77
+ function computeE(maxDecimals) {
78
+ let b = 1n;
79
+ let m = 1n;
80
+ const t = 10n ** BigInt(getPrecisionDigits(maxDecimals));
81
+ while (m < t) {
82
+ b++;
83
+ m *= b;
84
+ }
85
+ const { n, d } = splitSeries(0n, b);
68
86
  return {
69
- n: BigInt(c.replace(".", "")),
70
- d: 10n ** BigInt(c.length - c.indexOf(".") - 1)
87
+ n: d + n,
88
+ d
71
89
  };
72
90
  }
73
- //#endregion
74
- //#region src/lib/utils/gcd.ts
91
+ // src/lib/utils/gcd.ts
75
92
  /**
76
93
  * The Euclidean GCD Algorithm for BigInt.
77
94
  */
@@ -88,9 +105,8 @@ function gcd(a, b) {
88
105
  }
89
106
  return a;
90
107
  }
91
- //#endregion
92
- //#region src/lib/utils/multiply.ts
93
- function multiply(a, b) {
108
+ // src/lib/utils/multiply.ts
109
+ function multiply(a, b, maxDecimals = 30) {
94
110
  const aN = a.n;
95
111
  const bN = b.n;
96
112
  if (aN === 0n || bN === 0n) return ZERO;
@@ -116,9 +132,9 @@ function multiply(a, b) {
116
132
  }, {
117
133
  n: bE?.n ?? 1n,
118
134
  d: bE?.d ?? 1n
119
- }))
135
+ }, maxDecimals))
120
136
  };
121
- if (aC !== void 0 && bC !== void 0) return multiply(toSimpleFraction(a), toSimpleFraction(b));
137
+ if (aC !== void 0 && bC !== void 0) return multiply(toSimpleFraction(a, maxDecimals), toSimpleFraction(b, maxDecimals), maxDecimals);
122
138
  return {
123
139
  n,
124
140
  d,
@@ -126,8 +142,7 @@ function multiply(a, b) {
126
142
  e: aE ?? bE
127
143
  };
128
144
  }
129
- //#endregion
130
- //#region src/lib/utils/nthroot.ts
145
+ // src/lib/utils/nthroot.ts
131
146
  /**
132
147
  * Calculates the integer nth root of a BigInt.
133
148
  */
@@ -179,15 +194,15 @@ function nthRoot(v, n, precision) {
179
194
  });
180
195
  }
181
196
  }
182
- const precisionDigits = Math.max(10, (precision.maxDecimals ?? 30) * 3);
197
+ const precisionDigits = getPrecisionDigits(precision.maxDecimals);
183
198
  const scaleFactor = 10n ** (BigInt(precisionDigits) * n);
199
+ const s = toSimpleFraction(v);
184
200
  return simplify({
185
- n: iNthRoot(v.n * scaleFactor / v.d, n),
201
+ n: iNthRoot(s.n * scaleFactor / s.d, n),
186
202
  d: 10n ** BigInt(precisionDigits)
187
203
  });
188
204
  }
189
- //#endregion
190
- //#region src/lib/utils/simplify.ts
205
+ // src/lib/utils/simplify.ts
191
206
  /**
192
207
  * Reduces a fraction to its simplest form.
193
208
  */
@@ -195,7 +210,7 @@ function simplify(v) {
195
210
  if (v.d === 0n) throw new DivisionByZeroError();
196
211
  if (v.n === 0n) return ZERO;
197
212
  if (v.d === 1n) {
198
- if (v.e?.n === 0n) return {
213
+ if (v.e?.n === 0n || !v.c) return {
199
214
  n: v.n,
200
215
  d: v.d
201
216
  };
@@ -205,7 +220,7 @@ function simplify(v) {
205
220
  const sign = v.d < 0 ? -1n : 1n;
206
221
  const n = v.n / common * sign;
207
222
  const d = v.d / common * sign;
208
- if (v.e?.n === 0n) return {
223
+ if (v.e?.n === 0n || !v.c) return {
209
224
  n,
210
225
  d
211
226
  };
@@ -219,9 +234,12 @@ function simplify(v) {
219
234
  /**
220
235
  * Resolves any constants and exponents to return a simple fraction.
221
236
  */
222
- function toSimpleFraction(val) {
223
- if (!val.c) return val;
224
- const c = getConst(val.c);
237
+ function toSimpleFraction(val, maxDecimals = 30) {
238
+ if (!val.c) return {
239
+ d: val.d,
240
+ n: val.n
241
+ };
242
+ const c = getConst(val.c, { maxDecimals });
225
243
  const expN = val.e?.n ?? 1n;
226
244
  const expD = val.e?.d ?? 1n;
227
245
  const absExpN = expN < 0 ? -expN : expN;
@@ -233,11 +251,120 @@ function toSimpleFraction(val) {
233
251
  return multiply(poweredConstant, {
234
252
  n: val.n,
235
253
  d: val.d
254
+ }, maxDecimals);
255
+ }
256
+ // src/lib/utils/arctan.ts
257
+ function rawArctanSeries(absN, absD, precision) {
258
+ const precisionDigits = getPrecisionDigits(precision.maxDecimals);
259
+ const SCALE = 10n ** BigInt(precisionDigits);
260
+ const xScaled = absN * SCALE / absD;
261
+ if (xScaled === 0n) return {
262
+ n: 0n,
263
+ d: SCALE
264
+ };
265
+ const xSqScaled = xScaled * xScaled / SCALE;
266
+ let currentTerm = xScaled;
267
+ let totalSum = xScaled;
268
+ let k = 1n;
269
+ while (true) {
270
+ currentTerm = currentTerm * xSqScaled / SCALE;
271
+ const denominatorFactor = 2n * k + 1n;
272
+ const termValue = currentTerm / denominatorFactor;
273
+ if (termValue === 0n) break;
274
+ if (k % 2n === 1n) totalSum -= termValue;
275
+ else totalSum += termValue;
276
+ k++;
277
+ }
278
+ return {
279
+ n: totalSum,
280
+ d: SCALE
281
+ };
282
+ }
283
+ function arctan(v, precision) {
284
+ const { n, d } = v;
285
+ if (n === 0n) return ZERO;
286
+ const isNegative = n < 0n !== d < 0n;
287
+ const absN = n < 0n ? -n : n;
288
+ const absD = d < 0n ? -d : d;
289
+ if (absN === absD) {
290
+ const pi = getConst("pi", precision);
291
+ const n = isNegative ? -pi.n : pi.n;
292
+ return simplify({
293
+ c: pi.c,
294
+ n,
295
+ d: pi.d * 4n
296
+ });
297
+ }
298
+ if (absN > absD) {
299
+ const pi = computePi(precision);
300
+ const subArctan = arctan({
301
+ n: absD,
302
+ d: absN
303
+ }, precision);
304
+ const resultNum = pi.n * subArctan.d - 2n * subArctan.n * pi.d;
305
+ const resultDen = 2n * pi.d * subArctan.d;
306
+ return simplify({
307
+ n: isNegative ? -resultNum : resultNum,
308
+ d: resultDen
309
+ });
310
+ }
311
+ if (absN * 2n > absD) {
312
+ const halfArctan = arctan({
313
+ n: 1n,
314
+ d: 2n
315
+ }, precision);
316
+ const remainderArctan = arctan({
317
+ n: 2n * absN - absD,
318
+ d: 2n * absD + absN
319
+ }, precision);
320
+ const resultNum = halfArctan.n * remainderArctan.d + remainderArctan.n * halfArctan.d;
321
+ const resultDen = halfArctan.d * remainderArctan.d;
322
+ return simplify({
323
+ n: isNegative ? -resultNum : resultNum,
324
+ d: resultDen
325
+ });
326
+ }
327
+ const r = rawArctanSeries(absN, absD, precision);
328
+ return simplify({
329
+ n: isNegative ? -r.n : r.n,
330
+ d: r.d
331
+ });
332
+ }
333
+ // src/lib/utils/pi.ts
334
+ function computePi(precision) {
335
+ const p5 = arctan({
336
+ n: 1n,
337
+ d: 5n
338
+ }, precision);
339
+ const p239 = arctan({
340
+ n: 1n,
341
+ d: 239n
342
+ }, precision);
343
+ return simplify({
344
+ n: 16n * p5.n * p239.d - 4n * p239.n * p5.d,
345
+ d: p5.d * p239.d
236
346
  });
237
347
  }
238
- //#endregion
239
- //#region src/lib/utils/add.ts
240
- function add(left, right, subtract = false) {
348
+ // src/lib/utils/constants.ts
349
+ const ZERO = {
350
+ n: 0n,
351
+ d: 1n
352
+ };
353
+ const ONE = {
354
+ n: 1n,
355
+ d: 1n
356
+ };
357
+ function getConst(id, options) {
358
+ if (options.format === "precise") return {
359
+ n: 1n,
360
+ d: 1n,
361
+ c: id
362
+ };
363
+ if (id === "pi") return computePi(options);
364
+ return computeE(options.maxDecimals);
365
+ }
366
+ // src/lib/utils/add.ts
367
+ function add(left, right, maxDecimals = 30, subtract = false) {
241
368
  const lN = left.n;
242
369
  const rN = right.n;
243
370
  if (lN === "OVERFLOW") return left;
@@ -269,7 +396,7 @@ function add(left, right, subtract = false) {
269
396
  d = lD * mLeft;
270
397
  }
271
398
  if (n === 0n) return ZERO;
272
- if (c !== rC || e?.n !== rE?.n) return add(toSimpleFraction(left), toSimpleFraction(right), subtract);
399
+ if (c !== rC || e?.n !== rE?.n) return add(toSimpleFraction(left, maxDecimals), toSimpleFraction(right, maxDecimals), maxDecimals, subtract);
273
400
  return {
274
401
  n,
275
402
  d,
@@ -277,8 +404,104 @@ function add(left, right, subtract = false) {
277
404
  e
278
405
  };
279
406
  }
280
- //#endregion
281
- //#region src/lib/utils/ceil.ts
407
+ // src/lib/utils/sqrt.ts
408
+ /**
409
+ * Calculates the integer square root of a BigInt.
410
+ */
411
+ function isqrt(value) {
412
+ if (value < 0) throw new NotImplementedError("Square root of negative numbers is not implemented yet.");
413
+ if (value < 2n) return value;
414
+ let res = 0n;
415
+ let bit = 1n << (BigInt.bitLength(value) & -2n);
416
+ while (bit !== 0n) {
417
+ if (value >= res + bit) {
418
+ value -= res + bit;
419
+ res = (res >> 1n) + bit;
420
+ } else res >>= 1n;
421
+ bit >>= 2n;
422
+ }
423
+ return res;
424
+ }
425
+ function isPerfectSquare(val) {
426
+ if (val < 0) return [false, 0n];
427
+ const root = isqrt(val);
428
+ return [root * root === val, root];
429
+ }
430
+ function sqrt(v, precision) {
431
+ if (v.n === "OVERFLOW") return v;
432
+ if (v.n < 0) throw new NotImplementedError("Square root of negative numbers is not implemented yet.");
433
+ if (v.n === 0n) return ZERO;
434
+ if (precision.format === "precise") {
435
+ const [nIsSquare, n] = isPerfectSquare(v.n);
436
+ const [dIsSquare, d] = isPerfectSquare(v.d);
437
+ if (nIsSquare && dIsSquare) {
438
+ const e = simplify({
439
+ n: v.e?.n ?? 1n,
440
+ d: (v.e?.d ?? 1n) * 2n
441
+ });
442
+ return simplify({
443
+ n,
444
+ d,
445
+ c: v.c,
446
+ e
447
+ });
448
+ }
449
+ }
450
+ const precisionDigits = getPrecisionDigits(precision.maxDecimals);
451
+ const scaleFactor = 10n ** BigInt(precisionDigits * 2);
452
+ const s = toSimpleFraction(v);
453
+ return simplify({
454
+ n: isqrt(s.n * scaleFactor / s.d),
455
+ d: 10n ** BigInt(precisionDigits)
456
+ });
457
+ }
458
+ // src/lib/utils/arctanh.ts
459
+ function rawArctanhSeries(xScaled, SCALE) {
460
+ let currentTerm = xScaled;
461
+ let totalSum = xScaled;
462
+ const xSqScaled = xScaled * xScaled / SCALE;
463
+ let k = 1n;
464
+ while (true) {
465
+ currentTerm = currentTerm * xSqScaled / SCALE;
466
+ const denominatorFactor = 2n * k + 1n;
467
+ const termValue = currentTerm / denominatorFactor;
468
+ if (termValue === 0n) break;
469
+ totalSum += termValue;
470
+ k++;
471
+ }
472
+ return totalSum;
473
+ }
474
+ function arctanh(v, precision) {
475
+ const { n, d } = v;
476
+ if (n === 0n) return ZERO;
477
+ const precisionDigits = getPrecisionDigits(precision.maxDecimals);
478
+ const SCALE = 10n ** BigInt(precisionDigits);
479
+ let currentN = n;
480
+ let currentD = d;
481
+ const isNegative = currentN < 0n !== currentD < 0n;
482
+ currentN = currentN < 0n ? -currentN : currentN;
483
+ currentD = currentD < 0n ? -currentD : currentD;
484
+ if (currentN >= currentD) throw new InterpreterRangeError("arctanh argument must be strictly between -1 and 1");
485
+ let reductions = 0n;
486
+ const threshold = SCALE / 2n;
487
+ let xScaled = currentN * SCALE / currentD;
488
+ while (xScaled > threshold) {
489
+ const sqrtVal = sqrt({
490
+ n: SCALE - xScaled * xScaled / SCALE,
491
+ d: SCALE
492
+ }, precision);
493
+ const denom = SCALE + sqrtVal.n * SCALE / sqrtVal.d;
494
+ xScaled = xScaled * SCALE / denom;
495
+ reductions++;
496
+ }
497
+ let sumScaled = rawArctanhSeries(xScaled, SCALE);
498
+ sumScaled <<= reductions;
499
+ return simplify({
500
+ n: isNegative ? -sumScaled : sumScaled,
501
+ d: SCALE
502
+ });
503
+ }
504
+ // src/lib/utils/ceil.ts
282
505
  /**
283
506
  * Returns the smallest integer greater than or equal to the value.
284
507
  */
@@ -290,9 +513,8 @@ function ceil(v) {
290
513
  const hasRemainder = n % d !== 0n;
291
514
  return isPositive && hasRemainder ? result + 1n : result;
292
515
  }
293
- //#endregion
294
- //#region src/lib/utils/divide.ts
295
- function divide(left, right) {
516
+ // src/lib/utils/divide.ts
517
+ function divide(left, right, maxDecimals = 30) {
296
518
  const lN = left.n;
297
519
  const rN = right.n;
298
520
  if (rN === 0n) throw new DivisionByZeroError();
@@ -314,7 +536,7 @@ function divide(left, right) {
314
536
  }, {
315
537
  n: rE?.n ?? 1n,
316
538
  d: rE?.d ?? 1n
317
- }, true))
539
+ }, maxDecimals, true))
318
540
  };
319
541
  const c = lC ?? rC;
320
542
  let e;
@@ -336,59 +558,101 @@ function divide(left, right) {
336
558
  e
337
559
  };
338
560
  }
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;
561
+ // src/lib/utils/ln.ts
562
+ function ln(v, precision) {
563
+ const { n, d, c, e } = v;
564
+ if (n <= 0n || d <= 0n) throw new InterpreterRangeError("Logarithm of non-positive number");
565
+ if (c === "e") return e ? {
566
+ n: e.n,
567
+ d: e.d ?? 1n
568
+ } : ONE;
569
+ const precisionDigits = getPrecisionDigits(precision.maxDecimals);
570
+ const SCALE = 10n ** BigInt(precisionDigits);
571
+ let currentN = n;
572
+ let currentD = d;
573
+ let m = 0;
574
+ const isReciprocal = currentN * SCALE < currentD * SCALE;
575
+ if (currentN * SCALE < currentD * SCALE) {
576
+ currentN = d;
577
+ currentD = n;
355
578
  }
356
- return res;
579
+ while (currentN * SCALE >= 2n * currentD * SCALE) {
580
+ const val = sqrt({
581
+ n: currentN,
582
+ d: currentD
583
+ }, precision);
584
+ currentN = val.n;
585
+ currentD = val.d;
586
+ m++;
587
+ }
588
+ const at = arctanh({
589
+ n: currentN - currentD,
590
+ d: currentN + currentD
591
+ }, precision);
592
+ let rN = at.n * 2n;
593
+ if (m > 0) rN <<= BigInt(m);
594
+ if (isReciprocal) rN = -rN;
595
+ return simplify({
596
+ n: rN,
597
+ d: at.d
598
+ });
357
599
  }
358
- function isPerfectSquare(val) {
359
- if (val < 0) return [false, 0n];
360
- const root = isqrt(val);
361
- return [root * root === val, root];
600
+ // src/lib/utils/exp.ts
601
+ function rawExp(v, precision) {
602
+ const { n, d } = v;
603
+ if (n === 0n) return ONE;
604
+ const precisionDigits = getPrecisionDigits(precision.maxDecimals);
605
+ const SCALE = 10n ** BigInt(precisionDigits);
606
+ const xScaled = n * SCALE / d;
607
+ const ln2Val = ln({
608
+ n: 2n,
609
+ d: 1n
610
+ }, precision);
611
+ const ln2Scaled = ln2Val.n * SCALE / ln2Val.d;
612
+ let k;
613
+ if (xScaled >= 0n) k = (xScaled + ln2Scaled / 2n) / ln2Scaled;
614
+ else k = (xScaled - ln2Scaled / 2n) / ln2Scaled;
615
+ const rScaled = xScaled - k * ln2Scaled;
616
+ const isNegativeR = rScaled < 0n;
617
+ const absRScaled = isNegativeR ? -rScaled : rScaled;
618
+ let sum = SCALE;
619
+ let currentTerm = SCALE;
620
+ let factorial = 1n;
621
+ let i = 1n;
622
+ while (true) {
623
+ currentTerm = currentTerm * absRScaled / SCALE;
624
+ if (currentTerm === 0n) break;
625
+ factorial *= i;
626
+ const termVal = currentTerm / factorial;
627
+ if (termVal === 0n) break;
628
+ if (isNegativeR && i % 2n === 1n) sum -= termVal;
629
+ else sum += termVal;
630
+ i += 1n;
631
+ }
632
+ let finalNum = sum;
633
+ let finalDen = SCALE;
634
+ if (k > 0n) finalNum <<= k;
635
+ else if (k < 0n) finalDen <<= -k;
636
+ return {
637
+ n: finalNum,
638
+ d: finalDen
639
+ };
362
640
  }
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
- });
641
+ function exp(v, precision) {
642
+ const { n, d, c } = v;
643
+ if (n === 0n) return ONE;
644
+ if (!c) return simplify({
645
+ n: 1n,
646
+ d: 1n,
647
+ c: "e",
648
+ e: {
649
+ n,
650
+ d
381
651
  }
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
652
  });
653
+ return rawExp(toSimpleFraction(v, getPrecisionDigits(precision.maxDecimals)), precision);
389
654
  }
390
- //#endregion
391
- //#region src/lib/utils/exponentiate.ts
655
+ // src/lib/utils/exponentiate.ts
392
656
  /**
393
657
  * Maximum digits allowed for the result of exponentiation
394
658
  */
@@ -398,40 +662,38 @@ function exponentiate(left, right, precision) {
398
662
  if (rN === 0n) return ONE;
399
663
  const lN = left.n;
400
664
  if (lN === "OVERFLOW") return left;
401
- const lD = left.d;
402
- const c = left.c;
665
+ const { c, d: lD } = left;
403
666
  if (lN === lD && c === void 0) return ONE;
404
667
  if (rN === "OVERFLOW") return right;
405
- if (lN === -lD && c === void 0) {
406
- if (simplify(right).n % 2n) return {
668
+ const rS = simplify(right);
669
+ if (lN === -lD && c === void 0 && rS.d % 2n) {
670
+ if (rS.n % 2n) return {
407
671
  n: -1n,
408
672
  d: 1n
409
673
  };
410
674
  return ONE;
411
675
  }
412
- const normalizedExp = toSimpleFraction(simplify(right));
676
+ const normalizedExp = toSimpleFraction(rS, precision.maxDecimals);
677
+ const dExp = normalizedExp.d;
413
678
  let exponent = normalizedExp.n;
414
679
  if (lN === 0n) {
415
680
  if (exponent < 0) throw new DivisionByZeroError();
416
681
  return ZERO;
417
682
  }
418
- const dExp = normalizedExp.d;
419
683
  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
- });
684
+ const maxDecimals = getPrecisionDigits(precision.maxDecimals);
685
+ const lS = toSimpleFraction(left, maxDecimals);
686
+ const rS = toSimpleFraction(right, maxDecimals);
687
+ try {
688
+ const lnBase = ln(lS, { maxDecimals });
689
+ const SCALE = 10n ** BigInt(maxDecimals);
690
+ return rawExp({
691
+ n: lnBase.n * SCALE / lnBase.d * (rS.n * SCALE / rS.d) / SCALE,
692
+ d: SCALE
693
+ }, precision);
694
+ } catch {
695
+ return OverflowValue;
696
+ }
435
697
  }
436
698
  const eN = (left.e?.n ?? 1n) * exponent;
437
699
  let baseN = lN;
@@ -463,8 +725,7 @@ function exponentiate(left, right, precision) {
463
725
  if (dExp === 2n) return sqrt(v, precision);
464
726
  return nthRoot(v, dExp, precision);
465
727
  }
466
- //#endregion
467
- //#region src/lib/utils/factorial.ts
728
+ // src/lib/utils/factorial.ts
468
729
  function binaryProduct(arr, low, high) {
469
730
  if (low > high) return 1n;
470
731
  if (low === high) return arr[low];
@@ -504,8 +765,7 @@ function factorial(n) {
504
765
  }
505
766
  return binaryProduct(primePowers, 0, primePowers.length - 1);
506
767
  }
507
- //#endregion
508
- //#region src/lib/utils/floor.ts
768
+ // src/lib/utils/floor.ts
509
769
  /**
510
770
  * Returns the largest integer less than or equal to the value.
511
771
  */
@@ -517,8 +777,7 @@ function floor(v) {
517
777
  const hasRemainder = n % d !== 0n;
518
778
  return isNegative && hasRemainder ? result - 1n : result;
519
779
  }
520
- //#endregion
521
- //#region src/lib/utils/mod.ts
780
+ // src/lib/utils/mod.ts
522
781
  /**
523
782
  * Calculates remainder of division.
524
783
  */
@@ -532,8 +791,7 @@ function mod(a, b) {
532
791
  d
533
792
  };
534
793
  }
535
- //#endregion
536
- //#region src/lib/interpreter.ts
794
+ // src/lib/interpreter.ts
537
795
  const precedence = {
538
796
  LPAREN: 0,
539
797
  ABS_OPEN: 0,
@@ -546,99 +804,94 @@ const precedence = {
546
804
  UNARY_MINUS: 4,
547
805
  EXP: 6,
548
806
  IMPLICIT_MUL: 6,
549
- ABS_FN: 8,
550
- CEIL_FN: 8,
551
- FLOOR_FN: 8,
552
- SQRT_FN: 8,
807
+ FN_CALL: 8,
553
808
  FACTORIAL: 10
554
809
  };
555
810
  function isFunctionOp(op) {
556
- return op === "ABS_FN" || op === "CEIL_FN" || op === "FLOOR_FN" || op === "SQRT_FN";
811
+ return typeof op === "object";
557
812
  }
558
813
  function isUnaryOperation(op) {
559
814
  return op === "UNARY_PLUS" || op === "UNARY_MINUS" || isFunctionOp(op);
560
815
  }
561
- /**
562
- * Maximum allowed precision
563
- */
564
- const MAX_PRECISION = 5e4;
565
- /**
566
- * Maximum allowed factorial
567
- */
568
- const MAX_FACTORIAL = 2e4;
569
- /**
570
- * Maximum allowed exponent for scientific number notation
571
- */
572
- const MAX_SCIENTIFIC_NUMBER_EXPONENT = 3e5;
816
+ function getOpPrecedence(op) {
817
+ if (typeof op === "object") return precedence.FN_CALL;
818
+ return precedence[op];
819
+ }
573
820
  function evaluate(tokens, options = {}) {
574
821
  if (tokens.length === 0) throw new EmptyExpressionError();
822
+ const { maxDecimals } = options;
575
823
  const values = [];
576
824
  const ops = [];
577
825
  const applyOp = (pos) => {
578
826
  const op = ops.pop();
579
- if (!op || op === "LPAREN" || op === "ABS_OPEN") return;
827
+ if (!op || op === "LPAREN" || op === "ABS_OPEN" || op === "UNARY_PLUS") return;
580
828
  const right = values.pop();
581
- if (isUnaryOperation(op)) switch (op) {
582
- case "UNARY_PLUS":
583
- values.push(right);
584
- return;
585
- case "UNARY_MINUS": {
586
- const rN = right.n;
587
- if (rN === "OVERFLOW") {
588
- values.push(OverflowValue);
829
+ if (isUnaryOperation(op)) {
830
+ const rN = right.n;
831
+ switch (op) {
832
+ case "UNARY_MINUS":
833
+ if (rN === "OVERFLOW") {
834
+ values.push(OverflowValue);
835
+ return;
836
+ }
837
+ values.push({
838
+ ...right,
839
+ n: -rN
840
+ });
589
841
  return;
590
- }
591
- values.push({
592
- ...right,
593
- n: -rN
594
- });
842
+ }
843
+ const { id } = op;
844
+ if (id === "sqrt") {
845
+ values.push(sqrt(right, options));
595
846
  return;
596
847
  }
597
- case "ABS_FN": {
598
- const rN = right.n;
599
- if (rN === "OVERFLOW") {
600
- values.push(OverflowValue);
601
- return;
602
- }
603
- const n = rN < 0 ? -rN : rN;
604
- values.push({
605
- ...right,
606
- n
607
- });
848
+ if (rN === "OVERFLOW") {
849
+ values.push(OverflowValue);
608
850
  return;
609
851
  }
610
- case "CEIL_FN":
611
- if (right.n === "OVERFLOW") {
612
- values.push(OverflowValue);
852
+ switch (id) {
853
+ case "abs": {
854
+ const n = rN < 0 ? -rN : rN;
855
+ values.push({
856
+ ...right,
857
+ n
858
+ });
613
859
  return;
614
860
  }
615
- values.push({
616
- n: ceil(toSimpleFraction(right)),
617
- d: 1n
618
- });
619
- return;
620
- case "FLOOR_FN":
621
- if (right.n === "OVERFLOW") {
622
- values.push(OverflowValue);
861
+ case "ceil":
862
+ values.push({
863
+ n: ceil(toSimpleFraction(right, maxDecimals)),
864
+ d: 1n
865
+ });
623
866
  return;
624
- }
625
- values.push({
626
- n: floor(toSimpleFraction(right)),
627
- d: 1n
628
- });
629
- return;
630
- case "SQRT_FN":
631
- values.push(sqrt(right, options));
632
- return;
867
+ case "floor":
868
+ values.push({
869
+ n: floor(toSimpleFraction(right, maxDecimals)),
870
+ d: 1n
871
+ });
872
+ return;
873
+ case "arctan":
874
+ values.push(arctan(toSimpleFraction(right, maxDecimals), options));
875
+ return;
876
+ case "arctanh":
877
+ values.push(arctanh(toSimpleFraction(right, maxDecimals), options));
878
+ return;
879
+ case "ln":
880
+ values.push(ln(right, options));
881
+ return;
882
+ case "exp":
883
+ values.push(exp(right, options));
884
+ return;
885
+ }
633
886
  }
634
887
  if (op === "FACTORIAL") {
635
888
  if (right.n === "OVERFLOW") {
636
889
  values.push(OverflowValue);
637
890
  return;
638
891
  }
639
- const reduced = simplify(right);
892
+ const reduced = toSimpleFraction(simplify(right));
640
893
  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) {
894
+ if (reduced.n >= 2e4) {
642
895
  values.push(OverflowValue);
643
896
  return;
644
897
  }
@@ -652,21 +905,21 @@ function evaluate(tokens, options = {}) {
652
905
  switch (op) {
653
906
  case "ADD":
654
907
  case "SUBTRACT":
655
- values.push(add(left, right, op === "SUBTRACT"));
908
+ values.push(add(left, right, maxDecimals, op === "SUBTRACT"));
656
909
  return;
657
910
  case "MULTIPLY":
658
911
  case "IMPLICIT_MUL":
659
- values.push(multiply(left, right));
912
+ values.push(multiply(left, right, maxDecimals));
660
913
  return;
661
914
  case "DIVIDE":
662
- values.push(divide(left, right));
915
+ values.push(divide(left, right, maxDecimals));
663
916
  return;
664
917
  case "MOD":
665
918
  if (left.n === "OVERFLOW" || right.n === "OVERFLOW") {
666
919
  values.push(OverflowValue);
667
920
  return;
668
921
  }
669
- values.push(mod(left, right));
922
+ values.push(mod(toSimpleFraction(left), toSimpleFraction(right)));
670
923
  return;
671
924
  case "EXP":
672
925
  values.push(exponentiate(left, right, options));
@@ -676,20 +929,20 @@ function evaluate(tokens, options = {}) {
676
929
  const pushOpWithPrecedence = (currentOp, pos) => {
677
930
  const isUnary = isUnaryOperation(currentOp);
678
931
  const isRightAssociative = currentOp === "EXP" || currentOp === "IMPLICIT_MUL";
679
- while (ops.length > 0 && !isUnary && (isRightAssociative ? precedence[ops.at(-1)] > precedence[currentOp] : precedence[ops.at(-1)] >= precedence[currentOp])) applyOp(pos);
932
+ while (ops.length > 0 && !isUnary && (isRightAssociative ? getOpPrecedence(ops.at(-1)) > getOpPrecedence(currentOp) : getOpPrecedence(ops.at(-1)) >= getOpPrecedence(currentOp))) applyOp(pos);
680
933
  ops.push(currentOp);
681
934
  };
682
935
  for (const token of tokens) switch (token.type) {
683
936
  case "NUMBER": {
684
937
  const frac = token.fraction || "";
685
938
  const fl = frac.length;
686
- if (fl > MAX_PRECISION) throw new MaximumPrecisionError(fl, MAX_PRECISION);
939
+ if (fl > 5e4) throw new MaximumPrecisionError(fl, MAX_PRECISION);
687
940
  let n = BigInt(token.whole + frac);
688
941
  let d = fl === 0 ? 1n : 10n ** BigInt(fl);
689
942
  if (token.exponent) {
690
- if (token.exponent.length > MAX_PRECISION) throw new MaximumPrecisionError(token.exponent.length, MAX_PRECISION);
943
+ if (token.exponent.length > 5e4) throw new MaximumPrecisionError(token.exponent.length, MAX_PRECISION);
691
944
  const expValue = BigInt(token.exponent);
692
- if (expValue > MAX_SCIENTIFIC_NUMBER_EXPONENT) {
945
+ if (expValue > 3e5) {
693
946
  values.push(OverflowValue);
694
947
  break;
695
948
  }
@@ -707,12 +960,11 @@ function evaluate(tokens, options = {}) {
707
960
  break;
708
961
  }
709
962
  case "CONST":
710
- if (options.format === "precise") values.push({
963
+ values.push({
711
964
  n: 1n,
712
965
  d: 1n,
713
966
  c: token.id
714
967
  });
715
- else values.push(getConst(token.id));
716
968
  break;
717
969
  case "LPAREN":
718
970
  case "ABS_OPEN":
@@ -774,22 +1026,7 @@ function evaluate(tokens, options = {}) {
774
1026
  case "MOD":
775
1027
  pushOpWithPrecedence(token.type, token.pos);
776
1028
  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;
1029
+ case "FUNC": pushOpWithPrecedence(token, token.pos);
793
1030
  }
794
1031
  while (ops.length > 0) {
795
1032
  const top = ops.at(-1);
@@ -804,8 +1041,7 @@ function evaluate(tokens, options = {}) {
804
1041
  if (finalValue.n === "OVERFLOW") throw new OverflowError();
805
1042
  return simplify(finalValue);
806
1043
  }
807
- //#endregion
808
- //#region src/lib/lexer.ts
1044
+ // src/lib/lexer.ts
809
1045
  var LexerError = class extends PicocalcError {
810
1046
  constructor(message, pos) {
811
1047
  super("LexerError", message, pos);
@@ -912,8 +1148,7 @@ function tokenize(expression, options = {}) {
912
1148
  }
913
1149
  return tokens;
914
1150
  }
915
- //#endregion
916
- //#region src/lib/symbol.ts
1151
+ // src/lib/symbol.ts
917
1152
  const symbolMap = {
918
1153
  PLUS: "+",
919
1154
  UNARY_PLUS: "+",
@@ -937,8 +1172,20 @@ function getSym(t) {
937
1172
  if (t.type === "IDENTIFIER" || t.type === "FUNC" || t.type === "CONST") return t.id;
938
1173
  return symbolMap[t.type];
939
1174
  }
940
- //#endregion
941
- //#region src/lib/parser.ts
1175
+ // src/lib/parser.ts
1176
+ const KNOWN_FUNCTIONS = /* @__PURE__ */ new Set([
1177
+ "abs",
1178
+ "arctan",
1179
+ "arctanh",
1180
+ "ceil",
1181
+ "exp",
1182
+ "floor",
1183
+ "ln",
1184
+ "sqrt"
1185
+ ]);
1186
+ function isKnownFunctionName(name) {
1187
+ return KNOWN_FUNCTIONS.has(name);
1188
+ }
942
1189
  /**
943
1190
  * Helper: Is the last token an "operand" (something that can be followed by a closing pipe or implicit mul)?
944
1191
  */
@@ -974,7 +1221,7 @@ function resolveIdentifier(token) {
974
1221
  id: token.id,
975
1222
  pos
976
1223
  };
977
- if (token.id === "abs" || token.id === "ceil" || token.id === "floor" || token.id === "sqrt") return {
1224
+ if (isKnownFunctionName(token.id)) return {
978
1225
  type: "FUNC",
979
1226
  id: token.id,
980
1227
  pos
@@ -1041,8 +1288,20 @@ function parse(tokens) {
1041
1288
  ].includes(last.type))) throw new IncompleteExpressionError(`trailing operator '${last.type === "FUNC" ? last.id : getSym(last)}'`, last.pos);
1042
1289
  return result;
1043
1290
  }
1044
- //#endregion
1045
- //#region src/utils/format-result.ts
1291
+ // src/lib/decimals.ts
1292
+ function toDecimal({ n, d }, { maxDecimals = 30, roundingMode = ROUNDING_MODE }) {
1293
+ if (d === 0n) throw new Error("Division by zero");
1294
+ if (n === 0n) return "0";
1295
+ const isNegative = n < 0n;
1296
+ let resultStr = (((isNegative ? -n : n) * 10n ** BigInt(maxDecimals) + (roundingMode === "round" ? d / 2n : 0n)) / d).toString();
1297
+ if (resultStr.length <= maxDecimals) resultStr = resultStr.padStart(maxDecimals + 1, "0");
1298
+ const splitIdx = resultStr.length - maxDecimals;
1299
+ const integerPart = resultStr.slice(0, splitIdx);
1300
+ const fractionalPart = resultStr.slice(splitIdx).replace(/0+$/, "");
1301
+ const finalResult = fractionalPart ? `${integerPart}.${fractionalPart}` : integerPart;
1302
+ return (isNegative && finalResult !== "0" ? "-" : "") + finalResult;
1303
+ }
1304
+ // src/utils/format-result.ts
1046
1305
  function getConstantStr(coeff, c, e) {
1047
1306
  if (coeff === 0n) return "0";
1048
1307
  if (!c) return `${coeff}`;
@@ -1077,26 +1336,14 @@ function formatPrecise(v) {
1077
1336
  * Converts Result to a Decimal or Fraction String.
1078
1337
  */
1079
1338
  function formatResult(v, options = {}) {
1080
- const { n, d } = v;
1081
1339
  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;
1340
+ return toDecimal(toSimpleFraction(v, options.maxDecimals), options);
1092
1341
  }
1093
- //#endregion
1094
- //#region src/index.ts
1342
+ // src/index.ts
1095
1343
  /**
1096
1344
  * The main entry point for the library.
1097
1345
  */
1098
1346
  function calculate(expression, options) {
1099
1347
  return formatResult(evaluate(parse(tokenize(expression, options)), options), options);
1100
1348
  }
1101
- //#endregion
1102
1349
  export { PicocalcError, calculate };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "picocalc",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -30,12 +30,12 @@
30
30
  "bigint.bitlength": "0.2.0"
31
31
  },
32
32
  "devDependencies": {
33
- "@gameroman/config": "0.2.5",
34
- "@types/bun": "1.3.14",
35
- "oxfmt": "0.58.0",
36
- "oxlint": "1.73.0",
37
- "oxlint-tsgolint": "0.24.0",
38
- "tsdown": "0.22.7",
39
- "typescript": "6.0.3"
33
+ "@gameroman/config": "0.3.0",
34
+ "@types/bun": "1.4.4",
35
+ "oxfmt": "0.75.0",
36
+ "oxlint": "1.90.0",
37
+ "oxlint-tsgolint": "7.0.2005",
38
+ "tsdown": "0.23.4",
39
+ "typescript": "7.0.2"
40
40
  }
41
41
  }