picocalc 0.0.3 → 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 +4 -9
- package/dist/index.mjs +492 -245
- package/package.json +8 -8
package/dist/index.d.mts
CHANGED
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
import "bigint.bitlength";
|
|
2
|
-
|
|
2
|
+
type RoundingMode = "round" | "truncate";
|
|
3
3
|
interface PrecisionOptions {
|
|
4
4
|
readonly format?: "decimal" | "precise";
|
|
5
5
|
readonly maxDecimals?: number;
|
|
6
|
-
readonly roundingMode?:
|
|
6
|
+
readonly roundingMode?: RoundingMode;
|
|
7
7
|
}
|
|
8
|
-
|
|
9
|
-
//#region src/lib/errors.d.ts
|
|
10
|
-
declare class PicocalcErrror 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
|
-
|
|
24
|
-
export { PicocalcErrror, calculate };
|
|
19
|
+
export { calculate };
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "bigint.bitlength";
|
|
2
|
-
|
|
3
|
-
var
|
|
2
|
+
// src/lib/errors.ts
|
|
3
|
+
var PicocalcError = class extends Error {
|
|
4
4
|
pos;
|
|
5
5
|
constructor(name, message, pos) {
|
|
6
6
|
if (pos === void 0) super(message);
|
|
@@ -9,7 +9,7 @@ var PicocalcErrror = class extends Error {
|
|
|
9
9
|
this.name = name;
|
|
10
10
|
}
|
|
11
11
|
};
|
|
12
|
-
var InterpreterError = class extends
|
|
12
|
+
var InterpreterError = class extends PicocalcError {
|
|
13
13
|
constructor(message, pos) {
|
|
14
14
|
super("InterpreterError", message, pos);
|
|
15
15
|
}
|
|
@@ -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
|
-
|
|
48
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const
|
|
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:
|
|
70
|
-
d
|
|
87
|
+
n: d + n,
|
|
88
|
+
d
|
|
71
89
|
};
|
|
72
90
|
}
|
|
73
|
-
|
|
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
|
-
|
|
92
|
-
|
|
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
|
-
|
|
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 =
|
|
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(
|
|
201
|
+
n: iNthRoot(s.n * scaleFactor / s.d, n),
|
|
186
202
|
d: 10n ** BigInt(precisionDigits)
|
|
187
203
|
});
|
|
188
204
|
}
|
|
189
|
-
|
|
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
|
|
224
|
-
|
|
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
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
-
|
|
281
|
-
|
|
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
|
-
|
|
294
|
-
|
|
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
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
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
|
-
|
|
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
|
-
|
|
359
|
-
|
|
360
|
-
const
|
|
361
|
-
|
|
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
|
|
364
|
-
|
|
365
|
-
if (
|
|
366
|
-
if (
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
406
|
-
|
|
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(
|
|
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
|
-
|
|
421
|
-
|
|
422
|
-
const
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
563
|
-
|
|
564
|
-
|
|
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))
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
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
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
});
|
|
842
|
+
}
|
|
843
|
+
const { id } = op;
|
|
844
|
+
if (id === "sqrt") {
|
|
845
|
+
values.push(sqrt(right, options));
|
|
595
846
|
return;
|
|
596
847
|
}
|
|
597
|
-
|
|
598
|
-
|
|
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
|
-
|
|
611
|
-
|
|
612
|
-
|
|
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
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
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
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
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 >=
|
|
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 ?
|
|
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 >
|
|
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 >
|
|
943
|
+
if (token.exponent.length > 5e4) throw new MaximumPrecisionError(token.exponent.length, MAX_PRECISION);
|
|
691
944
|
const expValue = BigInt(token.exponent);
|
|
692
|
-
if (expValue >
|
|
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
|
-
|
|
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,9 +1041,8 @@ function evaluate(tokens, options = {}) {
|
|
|
804
1041
|
if (finalValue.n === "OVERFLOW") throw new OverflowError();
|
|
805
1042
|
return simplify(finalValue);
|
|
806
1043
|
}
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
var LexerError = class extends PicocalcErrror {
|
|
1044
|
+
// src/lib/lexer.ts
|
|
1045
|
+
var LexerError = class extends PicocalcError {
|
|
810
1046
|
constructor(message, pos) {
|
|
811
1047
|
super("LexerError", message, pos);
|
|
812
1048
|
}
|
|
@@ -912,8 +1148,7 @@ function tokenize(expression, options = {}) {
|
|
|
912
1148
|
}
|
|
913
1149
|
return tokens;
|
|
914
1150
|
}
|
|
915
|
-
|
|
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
|
-
|
|
941
|
-
|
|
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
|
*/
|
|
@@ -952,7 +1199,7 @@ function isBinaryOperator(token) {
|
|
|
952
1199
|
function isUnaryContext(last) {
|
|
953
1200
|
return !isOperand(last);
|
|
954
1201
|
}
|
|
955
|
-
var ParserError = class extends
|
|
1202
|
+
var ParserError = class extends PicocalcError {
|
|
956
1203
|
constructor(message, pos) {
|
|
957
1204
|
super("ParserError", message, pos);
|
|
958
1205
|
}
|
|
@@ -974,7 +1221,7 @@ function resolveIdentifier(token) {
|
|
|
974
1221
|
id: token.id,
|
|
975
1222
|
pos
|
|
976
1223
|
};
|
|
977
|
-
if (token.id
|
|
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
|
-
|
|
1045
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1102
|
-
export { PicocalcErrror, calculate };
|
|
1349
|
+
export { PicocalcError, calculate };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "picocalc",
|
|
3
|
-
"version": "0.0.
|
|
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.
|
|
34
|
-
"@types/bun": "1.
|
|
35
|
-
"oxfmt": "0.
|
|
36
|
-
"oxlint": "1.
|
|
37
|
-
"oxlint-tsgolint": "0.
|
|
38
|
-
"tsdown": "0.
|
|
39
|
-
"typescript": "
|
|
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
|
}
|