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