picocalc 0.0.4 → 0.0.6

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,50 @@ var EmptyExpressionError = class extends InterpreterError {
39
39
  super("Empty expression");
40
40
  }
41
41
  };
42
- var MismatchedParenthesisError = class extends InterpreterError {
43
- constructor(pos, message = "Mismatched parenthesis") {
44
- super(message, pos);
42
+ var InterpreterRangeError = class extends InterpreterError {
43
+ constructor(message) {
44
+ super(message);
45
45
  }
46
46
  };
47
- //#endregion
48
- //#region src/lib/types.ts
47
+ const ROUNDING_MODE = "round";
48
+ function getBitPrecision(maxDecimals = 30) {
49
+ return BigInt(maxDecimals * 4 + 32);
50
+ }
51
+ /**
52
+ * Maximum allowed precision
53
+ */
54
+ const MAX_PRECISION = 5e4;
55
+ // src/lib/types.ts
49
56
  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];
57
+ // src/lib/utils/e.ts
58
+ function splitSeries(a, b) {
59
+ if (b - a === 1n) return {
60
+ n: 1n,
61
+ d: b
62
+ };
63
+ const mid = (a + b) / 2n;
64
+ const left = splitSeries(a, mid);
65
+ const right = splitSeries(mid, b);
68
66
  return {
69
- n: BigInt(c.replace(".", "")),
70
- d: 10n ** BigInt(c.length - c.indexOf(".") - 1)
67
+ n: left.n * right.d + right.n,
68
+ d: left.d * right.d
71
69
  };
72
70
  }
73
- //#endregion
74
- //#region src/lib/utils/gcd.ts
71
+ function computeE(maxDecimals) {
72
+ let b = 1n;
73
+ let m = 1n;
74
+ const t = 1n << getBitPrecision(maxDecimals);
75
+ while (m < t) {
76
+ b++;
77
+ m *= b;
78
+ }
79
+ const { n, d } = splitSeries(0n, b);
80
+ return {
81
+ n: d + n,
82
+ d
83
+ };
84
+ }
85
+ // src/lib/utils/gcd.ts
75
86
  /**
76
87
  * The Euclidean GCD Algorithm for BigInt.
77
88
  */
@@ -88,9 +99,8 @@ function gcd(a, b) {
88
99
  }
89
100
  return a;
90
101
  }
91
- //#endregion
92
- //#region src/lib/utils/multiply.ts
93
- function multiply(a, b) {
102
+ // src/lib/utils/multiply.ts
103
+ function multiply(a, b, maxDecimals) {
94
104
  const aN = a.n;
95
105
  const bN = b.n;
96
106
  if (aN === 0n || bN === 0n) return ZERO;
@@ -116,9 +126,9 @@ function multiply(a, b) {
116
126
  }, {
117
127
  n: bE?.n ?? 1n,
118
128
  d: bE?.d ?? 1n
119
- }))
129
+ }, maxDecimals))
120
130
  };
121
- if (aC !== void 0 && bC !== void 0) return multiply(toSimpleFraction(a), toSimpleFraction(b));
131
+ if (aC !== void 0 && bC !== void 0) return multiply(toSimpleFraction(a, maxDecimals), toSimpleFraction(b, maxDecimals), maxDecimals);
122
132
  return {
123
133
  n,
124
134
  d,
@@ -126,8 +136,7 @@ function multiply(a, b) {
126
136
  e: aE ?? bE
127
137
  };
128
138
  }
129
- //#endregion
130
- //#region src/lib/utils/nthroot.ts
139
+ // src/lib/utils/nthroot.ts
131
140
  /**
132
141
  * Calculates the integer nth root of a BigInt.
133
142
  */
@@ -148,46 +157,44 @@ function iNthRoot(value, n) {
148
157
  /**
149
158
  * Calculates the n-th root of a fraction.
150
159
  */
151
- function nthRoot(v, n, precision) {
160
+ function nthRoot(v, n, maxDecimals) {
152
161
  if (n === 0n) throw new DivisionByZeroError();
153
162
  if (n === 1n) return v;
154
163
  if (v.n < 0) {
155
164
  if (n % 2n === 0n) throw new NotImplementedError(`Even (${n}th) root of negative numbers is not implemented yet.`);
156
- const positiveResult = nthRoot({
165
+ const r = nthRoot({
157
166
  n: -v.n,
158
167
  d: v.d
159
- }, n, precision);
168
+ }, n, maxDecimals);
160
169
  return {
161
- n: -positiveResult.n,
162
- d: positiveResult.d
170
+ n: -r.n,
171
+ d: r.d
163
172
  };
164
173
  }
165
174
  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
- }
175
+ const rootN = iNthRoot(v.n, n);
176
+ const rootD = iNthRoot(v.d, n);
177
+ if (rootN ** n === v.n && rootD ** n === v.d) {
178
+ const e = simplify({
179
+ n: v.e?.n ?? 1n,
180
+ d: (v.e?.d ?? 1n) * n
181
+ });
182
+ return simplify({
183
+ n: rootN,
184
+ d: rootD,
185
+ c: v.c,
186
+ e
187
+ });
181
188
  }
182
- const precisionDigits = Math.max(10, (precision.maxDecimals ?? 30) * 3);
183
- const scaleFactor = 10n ** (BigInt(precisionDigits) * n);
189
+ const precisionBits = getBitPrecision(maxDecimals);
190
+ const scaleFactor = 1n << precisionBits * n;
191
+ const s = toSimpleFraction(v);
184
192
  return simplify({
185
- n: iNthRoot(v.n * scaleFactor / v.d, n),
186
- d: 10n ** BigInt(precisionDigits)
193
+ n: iNthRoot(s.n * scaleFactor / s.d, n),
194
+ d: 1n << precisionBits
187
195
  });
188
196
  }
189
- //#endregion
190
- //#region src/lib/utils/simplify.ts
197
+ // src/lib/utils/simplify.ts
191
198
  /**
192
199
  * Reduces a fraction to its simplest form.
193
200
  */
@@ -195,7 +202,7 @@ function simplify(v) {
195
202
  if (v.d === 0n) throw new DivisionByZeroError();
196
203
  if (v.n === 0n) return ZERO;
197
204
  if (v.d === 1n) {
198
- if (v.e?.n === 0n) return {
205
+ if (v.e?.n === 0n || !v.c) return {
199
206
  n: v.n,
200
207
  d: v.d
201
208
  };
@@ -205,7 +212,7 @@ function simplify(v) {
205
212
  const sign = v.d < 0 ? -1n : 1n;
206
213
  const n = v.n / common * sign;
207
214
  const d = v.d / common * sign;
208
- if (v.e?.n === 0n) return {
215
+ if (v.e?.n === 0n || !v.c) return {
209
216
  n,
210
217
  d
211
218
  };
@@ -219,9 +226,12 @@ function simplify(v) {
219
226
  /**
220
227
  * Resolves any constants and exponents to return a simple fraction.
221
228
  */
222
- function toSimpleFraction(val) {
223
- if (!val.c) return val;
224
- const c = getConst(val.c);
229
+ function toSimpleFraction(val, maxDecimals) {
230
+ if (!val.c) return {
231
+ d: val.d,
232
+ n: val.n
233
+ };
234
+ const c = getConst(val.c, maxDecimals);
225
235
  const expN = val.e?.n ?? 1n;
226
236
  const expD = val.e?.d ?? 1n;
227
237
  const absExpN = expN < 0 ? -expN : expN;
@@ -229,35 +239,123 @@ function toSimpleFraction(val) {
229
239
  n: expN < 0 ? c.d ** absExpN : c.n ** absExpN,
230
240
  d: expN < 0 ? c.n ** absExpN : c.d ** absExpN
231
241
  };
232
- if (expD !== 1n) poweredConstant = nthRoot(poweredConstant, expD, { format: "precise" });
242
+ if (expD !== 1n) poweredConstant = nthRoot(poweredConstant, expD);
233
243
  return multiply(poweredConstant, {
234
244
  n: val.n,
235
245
  d: val.d
246
+ }, maxDecimals);
247
+ }
248
+ // src/lib/utils/arctan.ts
249
+ function rawArctanSeries(xScaled, bitPrecision) {
250
+ const xSqScaled = xScaled * xScaled >> bitPrecision;
251
+ let currentTerm = xScaled;
252
+ let totalSum = xScaled;
253
+ let k = 1n;
254
+ while (true) {
255
+ currentTerm = currentTerm * xSqScaled >> bitPrecision;
256
+ const denominatorFactor = 2n * k + 1n;
257
+ const termValue = currentTerm / denominatorFactor;
258
+ if (termValue === 0n) break;
259
+ if (k % 2n === 1n) totalSum -= termValue;
260
+ else totalSum += termValue;
261
+ k++;
262
+ }
263
+ return totalSum;
264
+ }
265
+ function arctan(v, maxDecimals) {
266
+ const { n, d } = v;
267
+ if (n === 0n) return ZERO;
268
+ const bitPrecision = getBitPrecision(maxDecimals);
269
+ const isNegative = n < 0n !== d < 0n;
270
+ const absN = n < 0n ? -n : n;
271
+ const absD = d < 0n ? -d : d;
272
+ if (absN === absD) return {
273
+ c: "pi",
274
+ n: isNegative ? -1n : 1n,
275
+ d: 4n
276
+ };
277
+ if (absN > absD) {
278
+ const pi = computePi(maxDecimals);
279
+ const subArctan = arctan({
280
+ n: absD,
281
+ d: absN
282
+ }, maxDecimals);
283
+ const resultNum = pi.n * subArctan.d - 2n * subArctan.n * pi.d;
284
+ const resultDen = 2n * pi.d * subArctan.d;
285
+ return simplify({
286
+ n: isNegative ? -resultNum : resultNum,
287
+ d: resultDen
288
+ });
289
+ }
290
+ if (absN * 2n > absD) {
291
+ const halfArctan = arctan({
292
+ n: 1n,
293
+ d: 2n
294
+ }, maxDecimals);
295
+ const remainderArctan = arctan({
296
+ n: 2n * absN - absD,
297
+ d: 2n * absD + absN
298
+ }, maxDecimals);
299
+ const resultNum = halfArctan.n * remainderArctan.d + remainderArctan.n * halfArctan.d;
300
+ const resultDen = halfArctan.d * remainderArctan.d;
301
+ return simplify({
302
+ n: isNegative ? -resultNum : resultNum,
303
+ d: resultDen
304
+ });
305
+ }
306
+ const r = rawArctanSeries((absN << bitPrecision) / absD, bitPrecision);
307
+ return simplify({
308
+ n: isNegative ? -r : r,
309
+ d: 1n << bitPrecision
310
+ });
311
+ }
312
+ // src/lib/utils/pi.ts
313
+ function computePi(maxDecimals) {
314
+ const p5 = arctan({
315
+ n: 1n,
316
+ d: 5n
317
+ }, maxDecimals);
318
+ const p239 = arctan({
319
+ n: 1n,
320
+ d: 239n
321
+ }, maxDecimals);
322
+ return simplify({
323
+ n: 16n * p5.n * p239.d - 4n * p239.n * p5.d,
324
+ d: p5.d * p239.d
236
325
  });
237
326
  }
238
- //#endregion
239
- //#region src/lib/utils/add.ts
240
- function add(left, right, subtract = false) {
327
+ // src/lib/utils/constants.ts
328
+ const ZERO = {
329
+ n: 0n,
330
+ d: 1n
331
+ };
332
+ const ONE = {
333
+ n: 1n,
334
+ d: 1n
335
+ };
336
+ function getConst(id, maxDecimals) {
337
+ if (id === "pi") return computePi(maxDecimals);
338
+ return computeE(maxDecimals);
339
+ }
340
+ // src/lib/utils/add.ts
341
+ function negate(v) {
342
+ if (v.n === "OVERFLOW") return v;
343
+ const { n, d } = v;
344
+ return {
345
+ ...v,
346
+ n: d < 0 ? n : -n,
347
+ d: d < 0 ? -d : d
348
+ };
349
+ }
350
+ function add(left, right, maxDecimals, subtract = false) {
241
351
  const lN = left.n;
242
352
  const rN = right.n;
243
- if (lN === "OVERFLOW") return left;
244
- if (rN === "OVERFLOW") return right;
353
+ if (lN === "OVERFLOW" || rN === 0n) return left;
354
+ if (rN === "OVERFLOW" || lN === 0n) return subtract ? negate(right) : right;
245
355
  const { d: lD, c, e } = left;
246
356
  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;
357
+ if (c !== rC || e?.n !== rE?.n) return add(toSimpleFraction(left, maxDecimals), toSimpleFraction(right, maxDecimals), maxDecimals, subtract);
358
+ let n, d;
261
359
  if (lD === rD) {
262
360
  n = subtract ? lN - rN : lN + rN;
263
361
  d = lD;
@@ -269,7 +367,10 @@ function add(left, right, subtract = false) {
269
367
  d = lD * mLeft;
270
368
  }
271
369
  if (n === 0n) return ZERO;
272
- if (c !== rC || e?.n !== rE?.n) return add(toSimpleFraction(left), toSimpleFraction(right), subtract);
370
+ if (n < 0n && d < 0n) {
371
+ n = -n;
372
+ d = -d;
373
+ }
273
374
  return {
274
375
  n,
275
376
  d,
@@ -277,8 +378,36 @@ function add(left, right, subtract = false) {
277
378
  e
278
379
  };
279
380
  }
280
- //#endregion
281
- //#region src/lib/utils/ceil.ts
381
+ // src/lib/utils/arctanh.ts
382
+ function rawArctanhSeries(xScaled, bitPrecision) {
383
+ let currentTerm = xScaled;
384
+ let totalSum = xScaled;
385
+ const xSqScaled = xScaled * xScaled >> bitPrecision;
386
+ let k = 1n;
387
+ while (true) {
388
+ currentTerm = currentTerm * xSqScaled >> bitPrecision;
389
+ const denominatorFactor = 2n * k + 1n;
390
+ if (currentTerm < denominatorFactor) break;
391
+ totalSum += currentTerm / denominatorFactor;
392
+ k++;
393
+ }
394
+ return totalSum;
395
+ }
396
+ function arctanh(v, maxDecimals) {
397
+ const { n, d } = v;
398
+ if (n === 0n) return ZERO;
399
+ const bitPrecision = getBitPrecision(maxDecimals);
400
+ const isNegative = n < 0n !== d < 0n;
401
+ const currentN = n < 0n ? -n : n;
402
+ const currentD = d < 0n ? -d : d;
403
+ if (currentN >= currentD) throw new InterpreterRangeError("arctanh argument must be strictly between -1 and 1");
404
+ const sumScaled = rawArctanhSeries((currentN << bitPrecision) / currentD, bitPrecision);
405
+ return {
406
+ n: isNegative ? -sumScaled : sumScaled,
407
+ d: 1n << bitPrecision
408
+ };
409
+ }
410
+ // src/lib/utils/ceil.ts
282
411
  /**
283
412
  * Returns the smallest integer greater than or equal to the value.
284
413
  */
@@ -290,9 +419,8 @@ function ceil(v) {
290
419
  const hasRemainder = n % d !== 0n;
291
420
  return isPositive && hasRemainder ? result + 1n : result;
292
421
  }
293
- //#endregion
294
- //#region src/lib/utils/divide.ts
295
- function divide(left, right) {
422
+ // src/lib/utils/divide.ts
423
+ function divide(left, right, maxDecimals) {
296
424
  const lN = left.n;
297
425
  const rN = right.n;
298
426
  if (rN === 0n) throw new DivisionByZeroError();
@@ -300,6 +428,7 @@ function divide(left, right) {
300
428
  if (lN === "OVERFLOW" || rN === "OVERFLOW") return OverflowValue;
301
429
  const { d: lD, c: lC, e: lE } = left;
302
430
  const { d: rD, c: rC, e: rE } = right;
431
+ if (lC !== void 0 && rC !== void 0 && lC !== rC) return divide(toSimpleFraction(left, maxDecimals), toSimpleFraction(right, maxDecimals), maxDecimals);
303
432
  const g1 = gcd(lN, rN);
304
433
  const g2 = gcd(rD, lD);
305
434
  const n = lN / g1 * (rD / g2);
@@ -314,17 +443,14 @@ function divide(left, right) {
314
443
  }, {
315
444
  n: rE?.n ?? 1n,
316
445
  d: rE?.d ?? 1n
317
- }, true))
446
+ }, maxDecimals, true))
318
447
  };
319
448
  const c = lC ?? rC;
320
449
  let e;
321
450
  if (lC !== void 0) e = lE ? {
322
451
  n: lE.n,
323
452
  d: lE.d ?? 1n
324
- } : {
325
- n: 1n,
326
- d: 1n
327
- };
453
+ } : ONE;
328
454
  else if (rC !== void 0) e = {
329
455
  n: -(rE?.n ?? 1n),
330
456
  d: rE?.d ?? 1n
@@ -336,8 +462,7 @@ function divide(left, right) {
336
462
  e
337
463
  };
338
464
  }
339
- //#endregion
340
- //#region src/lib/utils/sqrt.ts
465
+ // src/lib/utils/sqrt.ts
341
466
  /**
342
467
  * Calculates the integer square root of a BigInt.
343
468
  */
@@ -360,78 +485,158 @@ function isPerfectSquare(val) {
360
485
  const root = isqrt(val);
361
486
  return [root * root === val, root];
362
487
  }
363
- function sqrt(v, precision) {
488
+ function sqrt(v, maxDecimals) {
364
489
  if (v.n === "OVERFLOW") return v;
365
490
  if (v.n < 0) throw new NotImplementedError("Square root of negative numbers is not implemented yet.");
366
491
  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
- }
492
+ const [nIsSquare, n] = isPerfectSquare(v.n);
493
+ const [dIsSquare, d] = isPerfectSquare(v.d);
494
+ if (nIsSquare && dIsSquare) {
495
+ const e = simplify({
496
+ n: v.e?.n ?? 1n,
497
+ d: (v.e?.d ?? 1n) * 2n
498
+ });
499
+ return simplify({
500
+ n,
501
+ d,
502
+ c: v.c,
503
+ e
504
+ });
382
505
  }
383
- const precisionDigits = Math.max(10, (precision.maxDecimals ?? 30) * 3);
384
- const scaleFactor = 10n ** BigInt(precisionDigits * 2);
506
+ const precisionBits = getBitPrecision(maxDecimals);
507
+ const scaleFactor = 1n << precisionBits * 2n;
508
+ const s = toSimpleFraction(v);
509
+ return {
510
+ n: isqrt(s.n * scaleFactor / s.d),
511
+ d: 1n << precisionBits
512
+ };
513
+ }
514
+ // src/lib/utils/ln.ts
515
+ function ln(v, maxDecimals) {
516
+ const { n, d, c, e } = v;
517
+ if (n <= 0n || d <= 0n) throw new InterpreterRangeError("Logarithm of non-positive number");
518
+ if (c === "e" && n === d) return e ? {
519
+ n: e.n,
520
+ d: e.d ?? 1n
521
+ } : ONE;
522
+ let { n: sN, d: sD } = toSimpleFraction(v);
523
+ const isReciprocal = sN < sD;
524
+ if (isReciprocal) [sN, sD] = [sD, sN];
525
+ let m = 0;
526
+ while (sN >= 2n * sD) {
527
+ const val = sqrt({
528
+ n: sN,
529
+ d: sD
530
+ }, maxDecimals);
531
+ sN = val.n;
532
+ sD = val.d;
533
+ m++;
534
+ }
535
+ const at = arctanh({
536
+ n: sN - sD,
537
+ d: sN + sD
538
+ }, maxDecimals);
539
+ let rN = at.n * 2n;
540
+ if (m > 0) rN <<= BigInt(m);
541
+ if (isReciprocal) rN = -rN;
385
542
  return simplify({
386
- n: isqrt(v.n * scaleFactor / v.d),
387
- d: 10n ** BigInt(precisionDigits)
543
+ n: rN,
544
+ d: at.d
388
545
  });
389
546
  }
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) {
547
+ // src/lib/utils/exp.ts
548
+ function rawExp(v, maxDecimals) {
549
+ const { n, d } = v;
550
+ if (n === 0n) return ONE;
551
+ const scale = 1n << getBitPrecision(maxDecimals);
552
+ const xScaled = n * scale / d;
553
+ const ln2Val = ln({
554
+ n: 2n,
555
+ d: 1n
556
+ }, maxDecimals);
557
+ const ln2Scaled = ln2Val.n * scale / ln2Val.d;
558
+ let k;
559
+ if (xScaled >= 0n) k = (xScaled + ln2Scaled / 2n) / ln2Scaled;
560
+ else k = (xScaled - ln2Scaled / 2n) / ln2Scaled;
561
+ const rScaled = xScaled - k * ln2Scaled;
562
+ const isNegativeR = rScaled < 0n;
563
+ const absRScaled = isNegativeR ? -rScaled : rScaled;
564
+ let sum = scale;
565
+ let currentTerm = scale;
566
+ let factorial = 1n;
567
+ let i = 1n;
568
+ while (true) {
569
+ currentTerm = currentTerm * absRScaled / scale;
570
+ if (currentTerm === 0n) break;
571
+ factorial *= i;
572
+ const termVal = currentTerm / factorial;
573
+ if (termVal === 0n) break;
574
+ if (isNegativeR && i % 2n === 1n) sum -= termVal;
575
+ else sum += termVal;
576
+ i += 1n;
577
+ }
578
+ let finalNum = sum;
579
+ let finalDen = scale;
580
+ if (k > 0n) finalNum <<= k;
581
+ else if (k < 0n) finalDen <<= -k;
582
+ return {
583
+ n: finalNum,
584
+ d: finalDen
585
+ };
586
+ }
587
+ function exp(v, maxDecimals) {
588
+ const { n, d, c } = v;
589
+ if (n === 0n) return ONE;
590
+ if (!c) return simplify({
591
+ n: 1n,
592
+ d: 1n,
593
+ c: "e",
594
+ e: {
595
+ n,
596
+ d
597
+ }
598
+ });
599
+ return rawExp(toSimpleFraction(v, maxDecimals), maxDecimals);
600
+ }
601
+ function exponentiate(left, right, maxDecimals) {
397
602
  const rN = right.n;
398
603
  if (rN === 0n) return ONE;
399
604
  const lN = left.n;
400
605
  if (lN === "OVERFLOW") return left;
401
- const lD = left.d;
402
- const c = left.c;
606
+ const { c, d: lD } = left;
403
607
  if (lN === lD && c === void 0) return ONE;
404
608
  if (rN === "OVERFLOW") return right;
405
- if (lN === -lD && c === void 0) {
406
- if (simplify(right).n % 2n) return {
609
+ const rS = simplify(right);
610
+ if (lN === -lD && c === void 0 && rS.d % 2n) {
611
+ if (rS.n % 2n) return {
407
612
  n: -1n,
408
613
  d: 1n
409
614
  };
410
615
  return ONE;
411
616
  }
412
- const normalizedExp = toSimpleFraction(simplify(right));
617
+ const normalizedExp = toSimpleFraction(rS, maxDecimals);
618
+ const dExp = normalizedExp.d;
413
619
  let exponent = normalizedExp.n;
414
620
  if (lN === 0n) {
415
621
  if (exponent < 0) throw new DivisionByZeroError();
416
622
  return ZERO;
417
623
  }
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
- });
624
+ if ((dExp >= 100 || lD > 1e3) && (lN !== 1n || rS.c)) {
625
+ const isNegativeBase = lN < 0;
626
+ if (isNegativeBase && dExp % 2n === 0n) throw new NotImplementedError(`Even (${dExp}th) root of negative numbers is not implemented yet.`);
627
+ const l = toSimpleFraction(isNegativeBase ? negate(left) : left, maxDecimals);
628
+ const r = toSimpleFraction(rS, maxDecimals);
629
+ const lnBase = ln(l, maxDecimals);
630
+ const scale = 1n << getBitPrecision(maxDecimals);
631
+ const p = lnBase.n * scale / lnBase.d * (r.n * scale / r.d) / scale;
632
+ const pAbs = p < 0 ? -p : p;
633
+ if (scale > pAbs * 10000000n || pAbs > scale * 10000000n) return OverflowValue;
634
+ const result = rawExp({
635
+ n: p,
636
+ d: scale
637
+ }, maxDecimals);
638
+ if (isNegativeBase && normalizedExp.n % 2n !== 0n) return negate(result);
639
+ return result;
435
640
  }
436
641
  const eN = (left.e?.n ?? 1n) * exponent;
437
642
  let baseN = lN;
@@ -442,7 +647,7 @@ function exponentiate(left, right, precision) {
442
647
  }
443
648
  const baseNDigits = BigInt.bitLength(baseN);
444
649
  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;
650
+ if (baseNDigits * exponent > 10000000n || baseDDigits * exponent > 10000000n || (baseNDigits > 5e3 || baseDDigits > 5e3) && exponent > 10) return OverflowValue;
446
651
  const e = c ? simplify({
447
652
  n: eN,
448
653
  d: left.e?.d ?? 1n
@@ -460,11 +665,10 @@ function exponentiate(left, right, precision) {
460
665
  e
461
666
  };
462
667
  if (dExp === 1n) return v;
463
- if (dExp === 2n) return sqrt(v, precision);
464
- return nthRoot(v, dExp, precision);
668
+ if (dExp === 2n) return sqrt(v, maxDecimals);
669
+ return nthRoot(v, dExp, maxDecimals);
465
670
  }
466
- //#endregion
467
- //#region src/lib/utils/factorial.ts
671
+ // src/lib/utils/factorial.ts
468
672
  function binaryProduct(arr, low, high) {
469
673
  if (low > high) return 1n;
470
674
  if (low === high) return arr[low];
@@ -504,8 +708,7 @@ function factorial(n) {
504
708
  }
505
709
  return binaryProduct(primePowers, 0, primePowers.length - 1);
506
710
  }
507
- //#endregion
508
- //#region src/lib/utils/floor.ts
711
+ // src/lib/utils/floor.ts
509
712
  /**
510
713
  * Returns the largest integer less than or equal to the value.
511
714
  */
@@ -517,8 +720,7 @@ function floor(v) {
517
720
  const hasRemainder = n % d !== 0n;
518
721
  return isNegative && hasRemainder ? result - 1n : result;
519
722
  }
520
- //#endregion
521
- //#region src/lib/utils/mod.ts
723
+ // src/lib/utils/mod.ts
522
724
  /**
523
725
  * Calculates remainder of division.
524
726
  */
@@ -532,8 +734,144 @@ function mod(a, b) {
532
734
  d
533
735
  };
534
736
  }
535
- //#endregion
536
- //#region src/lib/interpreter.ts
737
+ // src/lib/utils/trig.ts
738
+ function roundDiv(numerator, denominator) {
739
+ const twice = numerator * 2n;
740
+ const absTwice = twice < 0n ? -twice : twice;
741
+ const absDenom = denominator < 0n ? -denominator : denominator;
742
+ const quotient = (absTwice + absDenom) / (2n * absDenom);
743
+ return twice < 0n !== denominator < 0n ? -quotient : quotient;
744
+ }
745
+ function rawSinSeries(rScaled, bitPrecision) {
746
+ const rSqScaled = rScaled * rScaled >> bitPrecision;
747
+ let termValue = rScaled;
748
+ let totalSum = rScaled;
749
+ let k = 1n;
750
+ while (true) {
751
+ termValue = (termValue * rSqScaled >> bitPrecision) / (2n * k * (2n * k + 1n));
752
+ if (termValue === 0n) break;
753
+ if (k % 2n === 1n) totalSum -= termValue;
754
+ else totalSum += termValue;
755
+ k++;
756
+ }
757
+ return totalSum;
758
+ }
759
+ function rawCosSeries(rScaled, bitPrecision) {
760
+ const rSqScaled = rScaled * rScaled >> bitPrecision;
761
+ const scale = 1n << bitPrecision;
762
+ let termValue = scale;
763
+ let totalSum = scale;
764
+ let k = 1n;
765
+ while (true) {
766
+ termValue = (termValue * rSqScaled >> bitPrecision) / ((2n * k - 1n) * (2n * k));
767
+ if (termValue === 0n) break;
768
+ if (k % 2n === 1n) totalSum -= termValue;
769
+ else totalSum += termValue;
770
+ k++;
771
+ }
772
+ return totalSum;
773
+ }
774
+ function getHalfPi(maxDecimals) {
775
+ const { d, n, c } = computePi(maxDecimals);
776
+ return {
777
+ c,
778
+ n,
779
+ d: d * 2n
780
+ };
781
+ }
782
+ function sin(v, maxDecimals) {
783
+ const { n, d, c, e } = v;
784
+ if (n === 0n) return ZERO;
785
+ const bitPrecision = getBitPrecision(maxDecimals);
786
+ const scale = 1n << bitPrecision;
787
+ const isPiLinear = c === "pi" && (e === void 0 || e.n === 1n);
788
+ let rScaled;
789
+ let k;
790
+ if (isPiLinear) {
791
+ k = roundDiv(2n * n, d);
792
+ const rN = 2n * n - k * d;
793
+ if (rN === 0n) {
794
+ const quadrant = (k % 4n + 4n) % 4n;
795
+ if (quadrant === 0n || quadrant === 2n) return ZERO;
796
+ if (quadrant === 1n) return ONE;
797
+ return {
798
+ n: -1n,
799
+ d: 1n
800
+ };
801
+ }
802
+ const piVal = computePi(maxDecimals);
803
+ rScaled = rN * piVal.n * scale / (2n * d * piVal.d);
804
+ } else {
805
+ const simple = toSimpleFraction(v);
806
+ const halfPi = getHalfPi(maxDecimals);
807
+ k = roundDiv(simple.n * halfPi.d, simple.d * halfPi.n);
808
+ const rN = simple.n * halfPi.d - k * halfPi.n * simple.d;
809
+ const rD = simple.d * halfPi.d;
810
+ rScaled = rN * scale / rD;
811
+ }
812
+ const quadrant = (k % 4n + 4n) % 4n;
813
+ let resultScaled;
814
+ let negate = false;
815
+ if (quadrant === 0n) resultScaled = rawSinSeries(rScaled, bitPrecision);
816
+ else if (quadrant === 1n) resultScaled = rawCosSeries(rScaled, bitPrecision);
817
+ else if (quadrant === 2n) {
818
+ resultScaled = rawSinSeries(rScaled, bitPrecision);
819
+ negate = true;
820
+ } else {
821
+ resultScaled = rawCosSeries(rScaled, bitPrecision);
822
+ negate = true;
823
+ }
824
+ return {
825
+ n: negate ? -resultScaled : resultScaled,
826
+ d: scale
827
+ };
828
+ }
829
+ function cos(v, maxDecimals) {
830
+ const { n, d, c, e } = v;
831
+ if (n === 0n) return ONE;
832
+ const bitPrecision = getBitPrecision(maxDecimals);
833
+ const scale = 1n << bitPrecision;
834
+ const isPiLinear = c === "pi" && (e === void 0 || e.n === 1n);
835
+ let rScaled;
836
+ let k;
837
+ if (isPiLinear) {
838
+ k = roundDiv(2n * n, d);
839
+ const rN = 2n * n - k * d;
840
+ if (rN === 0n) {
841
+ const quadrant = (k % 4n + 4n) % 4n;
842
+ if (quadrant === 0n || quadrant === 2n) return quadrant === 0n ? ONE : {
843
+ n: -1n,
844
+ d: 1n
845
+ };
846
+ return ZERO;
847
+ }
848
+ const piVal = computePi(maxDecimals);
849
+ rScaled = rN * piVal.n * scale / (2n * d * piVal.d);
850
+ } else {
851
+ const simple = toSimpleFraction(v);
852
+ const halfPi = getHalfPi(maxDecimals);
853
+ k = roundDiv(simple.n * halfPi.d, simple.d * halfPi.n);
854
+ const rN = simple.n * halfPi.d - k * halfPi.n * simple.d;
855
+ const rD = simple.d * halfPi.d;
856
+ rScaled = rN * scale / rD;
857
+ }
858
+ const quadrant = (k % 4n + 4n) % 4n;
859
+ let resultScaled;
860
+ let negate = false;
861
+ if (quadrant === 0n) resultScaled = rawCosSeries(rScaled, bitPrecision);
862
+ else if (quadrant === 1n) {
863
+ resultScaled = rawSinSeries(rScaled, bitPrecision);
864
+ negate = true;
865
+ } else if (quadrant === 2n) {
866
+ resultScaled = rawCosSeries(rScaled, bitPrecision);
867
+ negate = true;
868
+ } else resultScaled = rawSinSeries(rScaled, bitPrecision);
869
+ return {
870
+ n: negate ? -resultScaled : resultScaled,
871
+ d: scale
872
+ };
873
+ }
874
+ // src/lib/interpreter.ts
537
875
  const precedence = {
538
876
  LPAREN: 0,
539
877
  ABS_OPEN: 0,
@@ -542,103 +880,94 @@ const precedence = {
542
880
  MULTIPLY: 2,
543
881
  DIVIDE: 2,
544
882
  MOD: 2,
545
- UNARY_PLUS: 4,
546
- UNARY_MINUS: 4,
547
- EXP: 6,
548
- IMPLICIT_MUL: 6,
549
- ABS_FN: 8,
550
- CEIL_FN: 8,
551
- FLOOR_FN: 8,
552
- SQRT_FN: 8,
553
- FACTORIAL: 10
883
+ UNARY_PLUS: 3,
884
+ UNARY_MINUS: 3,
885
+ FN_CALL: 4,
886
+ EXP: 5,
887
+ IMPLICIT_MUL: 5,
888
+ FACTORIAL: 6
554
889
  };
555
890
  function isFunctionOp(op) {
556
- return op === "ABS_FN" || op === "CEIL_FN" || op === "FLOOR_FN" || op === "SQRT_FN";
891
+ return typeof op === "object";
557
892
  }
558
893
  function isUnaryOperation(op) {
559
894
  return op === "UNARY_PLUS" || op === "UNARY_MINUS" || isFunctionOp(op);
560
895
  }
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;
896
+ function getOpPrecedence(op) {
897
+ if (isFunctionOp(op)) return precedence.FN_CALL;
898
+ return precedence[op];
899
+ }
573
900
  function evaluate(tokens, options = {}) {
574
901
  if (tokens.length === 0) throw new EmptyExpressionError();
902
+ const { maxDecimals } = options;
575
903
  const values = [];
576
904
  const ops = [];
577
905
  const applyOp = (pos) => {
578
906
  const op = ops.pop();
579
- if (!op || op === "LPAREN" || op === "ABS_OPEN") return;
907
+ if (!op || op === "LPAREN" || op === "ABS_OPEN" || op === "UNARY_PLUS") return;
580
908
  const right = values.pop();
581
- if (isUnaryOperation(op)) switch (op) {
582
- case "UNARY_PLUS":
583
- values.push(right);
909
+ if (isUnaryOperation(op)) {
910
+ if (op === "UNARY_MINUS") {
911
+ values.push(negate(right));
584
912
  return;
585
- case "UNARY_MINUS": {
586
- const rN = right.n;
587
- if (rN === "OVERFLOW") {
588
- values.push(OverflowValue);
589
- return;
590
- }
591
- values.push({
592
- ...right,
593
- n: -rN
594
- });
913
+ }
914
+ const { id } = op;
915
+ if (id === "sqrt") {
916
+ values.push(sqrt(right, maxDecimals));
595
917
  return;
596
918
  }
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
- });
919
+ const n = right.n;
920
+ if (n === "OVERFLOW") {
921
+ values.push(OverflowValue);
608
922
  return;
609
923
  }
610
- case "CEIL_FN":
611
- if (right.n === "OVERFLOW") {
612
- values.push(OverflowValue);
924
+ switch (id) {
925
+ case "abs":
926
+ values.push({
927
+ ...right,
928
+ n: n < 0 ? -n : n
929
+ });
613
930
  return;
614
- }
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);
931
+ case "ceil":
932
+ values.push({
933
+ n: ceil(toSimpleFraction(right, maxDecimals)),
934
+ d: 1n
935
+ });
623
936
  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;
937
+ case "floor":
938
+ values.push({
939
+ n: floor(toSimpleFraction(right, maxDecimals)),
940
+ d: 1n
941
+ });
942
+ return;
943
+ case "arctan":
944
+ values.push(arctan(toSimpleFraction(right, maxDecimals), maxDecimals));
945
+ return;
946
+ case "arctanh":
947
+ values.push(arctanh(toSimpleFraction(right, maxDecimals), maxDecimals));
948
+ return;
949
+ case "ln":
950
+ values.push(ln(right, maxDecimals));
951
+ return;
952
+ case "exp":
953
+ values.push(exp(right, maxDecimals));
954
+ return;
955
+ case "sin":
956
+ values.push(sin(right, maxDecimals));
957
+ return;
958
+ case "cos":
959
+ values.push(cos(right, maxDecimals));
960
+ return;
961
+ }
633
962
  }
634
963
  if (op === "FACTORIAL") {
635
964
  if (right.n === "OVERFLOW") {
636
965
  values.push(OverflowValue);
637
966
  return;
638
967
  }
639
- const reduced = simplify(right);
968
+ const reduced = toSimpleFraction(simplify(right));
640
969
  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) {
970
+ if (reduced.n >= 2e4) {
642
971
  values.push(OverflowValue);
643
972
  return;
644
973
  }
@@ -652,44 +981,44 @@ function evaluate(tokens, options = {}) {
652
981
  switch (op) {
653
982
  case "ADD":
654
983
  case "SUBTRACT":
655
- values.push(add(left, right, op === "SUBTRACT"));
984
+ values.push(add(left, right, maxDecimals, op === "SUBTRACT"));
656
985
  return;
657
986
  case "MULTIPLY":
658
987
  case "IMPLICIT_MUL":
659
- values.push(multiply(left, right));
988
+ values.push(multiply(left, right, maxDecimals));
660
989
  return;
661
990
  case "DIVIDE":
662
- values.push(divide(left, right));
991
+ values.push(divide(left, right, maxDecimals));
663
992
  return;
664
993
  case "MOD":
665
994
  if (left.n === "OVERFLOW" || right.n === "OVERFLOW") {
666
995
  values.push(OverflowValue);
667
996
  return;
668
997
  }
669
- values.push(mod(left, right));
998
+ values.push(mod(toSimpleFraction(left), toSimpleFraction(right)));
670
999
  return;
671
1000
  case "EXP":
672
- values.push(exponentiate(left, right, options));
1001
+ values.push(exponentiate(left, right, maxDecimals));
673
1002
  return;
674
1003
  }
675
1004
  };
676
1005
  const pushOpWithPrecedence = (currentOp, pos) => {
677
1006
  const isUnary = isUnaryOperation(currentOp);
678
1007
  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);
1008
+ while (ops.length > 0 && !isUnary && (isRightAssociative ? getOpPrecedence(ops.at(-1)) > getOpPrecedence(currentOp) : getOpPrecedence(ops.at(-1)) >= getOpPrecedence(currentOp))) applyOp(pos);
680
1009
  ops.push(currentOp);
681
1010
  };
682
1011
  for (const token of tokens) switch (token.type) {
683
1012
  case "NUMBER": {
684
1013
  const frac = token.fraction || "";
685
1014
  const fl = frac.length;
686
- if (fl > MAX_PRECISION) throw new MaximumPrecisionError(fl, MAX_PRECISION);
1015
+ if (fl > 5e4) throw new MaximumPrecisionError(fl, MAX_PRECISION);
687
1016
  let n = BigInt(token.whole + frac);
688
1017
  let d = fl === 0 ? 1n : 10n ** BigInt(fl);
689
1018
  if (token.exponent) {
690
- if (token.exponent.length > MAX_PRECISION) throw new MaximumPrecisionError(token.exponent.length, MAX_PRECISION);
1019
+ if (token.exponent.length > 5e4) throw new MaximumPrecisionError(token.exponent.length, MAX_PRECISION);
691
1020
  const expValue = BigInt(token.exponent);
692
- if (expValue > MAX_SCIENTIFIC_NUMBER_EXPONENT) {
1021
+ if (expValue > 3e5) {
693
1022
  values.push(OverflowValue);
694
1023
  break;
695
1024
  }
@@ -707,27 +1036,21 @@ function evaluate(tokens, options = {}) {
707
1036
  break;
708
1037
  }
709
1038
  case "CONST":
710
- if (options.format === "precise") values.push({
1039
+ values.push({
711
1040
  n: 1n,
712
1041
  d: 1n,
713
1042
  c: token.id
714
1043
  });
715
- else values.push(getConst(token.id));
716
1044
  break;
717
1045
  case "LPAREN":
718
1046
  case "ABS_OPEN":
719
1047
  ops.push(token.type);
720
1048
  break;
721
1049
  case "RPAREN": {
722
- let foundMatch = false;
723
1050
  while (ops.length > 0) {
724
- if (ops.at(-1) === "LPAREN") {
725
- foundMatch = true;
726
- break;
727
- }
1051
+ if (ops.at(-1) === "LPAREN") break;
728
1052
  applyOp(token.pos);
729
1053
  }
730
- if (!foundMatch) throw new MismatchedParenthesisError(token.pos);
731
1054
  ops.pop();
732
1055
  const nextOp = ops.at(-1);
733
1056
  if (!nextOp) break;
@@ -774,22 +1097,7 @@ function evaluate(tokens, options = {}) {
774
1097
  case "MOD":
775
1098
  pushOpWithPrecedence(token.type, token.pos);
776
1099
  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;
1100
+ case "FUNC": pushOpWithPrecedence(token, token.pos);
793
1101
  }
794
1102
  while (ops.length > 0) {
795
1103
  const top = ops.at(-1);
@@ -804,8 +1112,7 @@ function evaluate(tokens, options = {}) {
804
1112
  if (finalValue.n === "OVERFLOW") throw new OverflowError();
805
1113
  return simplify(finalValue);
806
1114
  }
807
- //#endregion
808
- //#region src/lib/lexer.ts
1115
+ // src/lib/lexer.ts
809
1116
  var LexerError = class extends PicocalcError {
810
1117
  constructor(message, pos) {
811
1118
  super("LexerError", message, pos);
@@ -912,8 +1219,7 @@ function tokenize(expression, options = {}) {
912
1219
  }
913
1220
  return tokens;
914
1221
  }
915
- //#endregion
916
- //#region src/lib/symbol.ts
1222
+ // src/lib/symbol.ts
917
1223
  const symbolMap = {
918
1224
  PLUS: "+",
919
1225
  UNARY_PLUS: "+",
@@ -937,8 +1243,22 @@ function getSym(t) {
937
1243
  if (t.type === "IDENTIFIER" || t.type === "FUNC" || t.type === "CONST") return t.id;
938
1244
  return symbolMap[t.type];
939
1245
  }
940
- //#endregion
941
- //#region src/lib/parser.ts
1246
+ // src/lib/parser.ts
1247
+ const KNOWN_FUNCTIONS = /* @__PURE__ */ new Set([
1248
+ "abs",
1249
+ "arctan",
1250
+ "arctanh",
1251
+ "ceil",
1252
+ "cos",
1253
+ "exp",
1254
+ "floor",
1255
+ "ln",
1256
+ "sin",
1257
+ "sqrt"
1258
+ ]);
1259
+ function isKnownFunctionName(name) {
1260
+ return KNOWN_FUNCTIONS.has(name);
1261
+ }
942
1262
  /**
943
1263
  * Helper: Is the last token an "operand" (something that can be followed by a closing pipe or implicit mul)?
944
1264
  */
@@ -974,7 +1294,7 @@ function resolveIdentifier(token) {
974
1294
  id: token.id,
975
1295
  pos
976
1296
  };
977
- if (token.id === "abs" || token.id === "ceil" || token.id === "floor" || token.id === "sqrt") return {
1297
+ if (isKnownFunctionName(token.id)) return {
978
1298
  type: "FUNC",
979
1299
  id: token.id,
980
1300
  pos
@@ -989,9 +1309,9 @@ function parse(tokens) {
989
1309
  let token = tokens[i];
990
1310
  const prev = tokens[i - 1];
991
1311
  if (token.type === "IDENTIFIER") token = resolveIdentifier(token);
992
- const pos = token.pos;
1312
+ const { pos, type } = token;
993
1313
  const isPrevOperand = isOperand(result.at(-1));
994
- if (token.type === "PIPE") {
1314
+ if (type === "PIPE") {
995
1315
  if (absStack > 0 && isPrevOperand) {
996
1316
  result.push({
997
1317
  type: "ABS_CLOSE",
@@ -1011,19 +1331,19 @@ function parse(tokens) {
1011
1331
  absStack++;
1012
1332
  continue;
1013
1333
  }
1014
- if ((token.type === "LPAREN" || token.type === "FUNC" || token.type === "NUMBER" || token.type === "CONST") && isPrevOperand) result.push({
1334
+ if ((type === "LPAREN" || type === "FUNC" || type === "NUMBER" || type === "CONST") && isPrevOperand) result.push({
1015
1335
  type: "IMPLICIT_MUL",
1016
1336
  pos
1017
1337
  });
1018
1338
  const prevParsed = result.at(-1);
1019
1339
  if (isBinaryOperator(token) && isUnaryContext(prevParsed)) throw new UnexpectedOperatorError(`Unexpected operator '${getSym(token)}'`, token.pos);
1020
- if ((token.type === "FACTORIAL" || token.type === "RPAREN") && isUnaryContext(prevParsed)) {
1340
+ if ((type === "FACTORIAL" || type === "RPAREN") && isUnaryContext(prevParsed)) {
1021
1341
  if (!prevParsed) throw new UnexpectedOperatorError(`Unexpected '${getSym(token)}' at the beginning of expression`, token.pos);
1022
1342
  throw new UnexpectedOperatorError(`Unexpected '${getSym(token)}' after '${getSym(prevParsed)}'`, token.pos);
1023
1343
  }
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);
1025
- if ((token.type === "PLUS" || token.type === "MINUS") && isUnaryContext(prevParsed)) result.push({
1026
- type: token.type === "PLUS" ? "UNARY_PLUS" : "UNARY_MINUS",
1344
+ if (prev && (prev.type === "NUMBER" || prev.type === "IDENTIFIER" && resolveIdentifier(prev).type === "CONST") && type === "NUMBER") throw new ParserError("Missing operator between numbers", token.pos);
1345
+ if ((type === "PLUS" || type === "MINUS") && isUnaryContext(prevParsed)) result.push({
1346
+ type: type === "PLUS" ? "UNARY_PLUS" : "UNARY_MINUS",
1027
1347
  pos
1028
1348
  });
1029
1349
  else result.push(token);
@@ -1041,8 +1361,20 @@ function parse(tokens) {
1041
1361
  ].includes(last.type))) throw new IncompleteExpressionError(`trailing operator '${last.type === "FUNC" ? last.id : getSym(last)}'`, last.pos);
1042
1362
  return result;
1043
1363
  }
1044
- //#endregion
1045
- //#region src/utils/format-result.ts
1364
+ // src/lib/decimals.ts
1365
+ function toDecimal({ n, d }, { maxDecimals = 30, roundingMode = ROUNDING_MODE }) {
1366
+ if (d === 0n) throw new Error("Division by zero");
1367
+ if (n === 0n) return "0";
1368
+ const isNegative = n < 0n;
1369
+ let resultStr = (((isNegative ? -n : n) * 10n ** BigInt(maxDecimals) + (roundingMode === "round" ? d / 2n : 0n)) / d).toString();
1370
+ if (resultStr.length <= maxDecimals) resultStr = resultStr.padStart(maxDecimals + 1, "0");
1371
+ const splitIdx = resultStr.length - maxDecimals;
1372
+ const integerPart = resultStr.slice(0, splitIdx);
1373
+ const fractionalPart = resultStr.slice(splitIdx).replace(/0+$/, "");
1374
+ const finalResult = fractionalPart ? `${integerPart}.${fractionalPart}` : integerPart;
1375
+ return (isNegative && finalResult !== "0" ? "-" : "") + finalResult;
1376
+ }
1377
+ // src/utils/format-result.ts
1046
1378
  function getConstantStr(coeff, c, e) {
1047
1379
  if (coeff === 0n) return "0";
1048
1380
  if (!c) return `${coeff}`;
@@ -1077,26 +1409,14 @@ function formatPrecise(v) {
1077
1409
  * Converts Result to a Decimal or Fraction String.
1078
1410
  */
1079
1411
  function formatResult(v, options = {}) {
1080
- const { n, d } = v;
1081
1412
  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;
1413
+ return toDecimal(toSimpleFraction(v, options.maxDecimals), options);
1092
1414
  }
1093
- //#endregion
1094
- //#region src/index.ts
1415
+ // src/index.ts
1095
1416
  /**
1096
1417
  * The main entry point for the library.
1097
1418
  */
1098
1419
  function calculate(expression, options) {
1099
1420
  return formatResult(evaluate(parse(tokenize(expression, options)), options), options);
1100
1421
  }
1101
- //#endregion
1102
1422
  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.6",
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.77.0",
36
+ "oxlint": "1.92.0",
37
+ "oxlint-tsgolint": "7.0.2006",
38
+ "tsdown": "0.23.5",
39
+ "typescript": "7.0.2"
40
40
  }
41
41
  }