math-expression-compiler 4.4.0 → 4.5.0
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/README.md +1 -1
- package/dist/index.d.ts +439 -0
- package/dist/index.js +2095 -0
- package/package.json +7 -7
package/dist/index.js
ADDED
|
@@ -0,0 +1,2095 @@
|
|
|
1
|
+
import { exhaustiveGuard } from "@esposter/shared";
|
|
2
|
+
import { err, ok } from "neverthrow";
|
|
3
|
+
// src/models/compiler/MathExpressionBitwiseAbsorption.ts
|
|
4
|
+
let MathExpressionBitwiseAbsorption = /* @__PURE__ */ function(MathExpressionBitwiseAbsorption) {
|
|
5
|
+
MathExpressionBitwiseAbsorption["BitAnd"] = "bitAnd";
|
|
6
|
+
MathExpressionBitwiseAbsorption["BitOr"] = "bitOr";
|
|
7
|
+
return MathExpressionBitwiseAbsorption;
|
|
8
|
+
}({});
|
|
9
|
+
// src/models/function/MathExpressionFunctionName.ts
|
|
10
|
+
let MathExpressionFunctionName = /* @__PURE__ */ function(MathExpressionFunctionName) {
|
|
11
|
+
MathExpressionFunctionName["CTranspose"] = "ctranspose";
|
|
12
|
+
MathExpressionFunctionName["Factorial"] = "factorial";
|
|
13
|
+
MathExpressionFunctionName["UnaryMinus"] = "unaryMinus";
|
|
14
|
+
MathExpressionFunctionName["UnaryPlus"] = "unaryPlus";
|
|
15
|
+
return MathExpressionFunctionName;
|
|
16
|
+
}({});
|
|
17
|
+
// src/models/function/MathExpressionParameterType.ts
|
|
18
|
+
let MathExpressionParameterType = /* @__PURE__ */ function(MathExpressionParameterType) {
|
|
19
|
+
MathExpressionParameterType["Any"] = "any";
|
|
20
|
+
MathExpressionParameterType["List"] = "list";
|
|
21
|
+
MathExpressionParameterType["Number"] = "number";
|
|
22
|
+
return MathExpressionParameterType;
|
|
23
|
+
}({});
|
|
24
|
+
// src/models/parser/MathExpressionBinaryOperatorSymbol.ts
|
|
25
|
+
let MathExpressionBinaryOperatorSymbol = /* @__PURE__ */ function(MathExpressionBinaryOperatorSymbol) {
|
|
26
|
+
MathExpressionBinaryOperatorSymbol["Add"] = "+";
|
|
27
|
+
MathExpressionBinaryOperatorSymbol["And"] = "and";
|
|
28
|
+
MathExpressionBinaryOperatorSymbol["BitwiseAnd"] = "&";
|
|
29
|
+
MathExpressionBinaryOperatorSymbol["BitwiseOr"] = "|";
|
|
30
|
+
MathExpressionBinaryOperatorSymbol["BitwiseXor"] = "^|";
|
|
31
|
+
MathExpressionBinaryOperatorSymbol["Divide"] = "/";
|
|
32
|
+
MathExpressionBinaryOperatorSymbol["ElementwiseDivide"] = "./";
|
|
33
|
+
MathExpressionBinaryOperatorSymbol["ElementwiseMultiply"] = ".*";
|
|
34
|
+
MathExpressionBinaryOperatorSymbol["Equal"] = "==";
|
|
35
|
+
MathExpressionBinaryOperatorSymbol["GreaterThan"] = ">";
|
|
36
|
+
MathExpressionBinaryOperatorSymbol["GreaterThanOrEqual"] = ">=";
|
|
37
|
+
MathExpressionBinaryOperatorSymbol["LessThan"] = "<";
|
|
38
|
+
MathExpressionBinaryOperatorSymbol["LessThanOrEqual"] = "<=";
|
|
39
|
+
MathExpressionBinaryOperatorSymbol["Mod"] = "mod";
|
|
40
|
+
MathExpressionBinaryOperatorSymbol["Modulo"] = "%";
|
|
41
|
+
MathExpressionBinaryOperatorSymbol["Multiply"] = "*";
|
|
42
|
+
MathExpressionBinaryOperatorSymbol["NotEqual"] = "!=";
|
|
43
|
+
MathExpressionBinaryOperatorSymbol["Or"] = "or";
|
|
44
|
+
MathExpressionBinaryOperatorSymbol["ShiftLeft"] = "<<";
|
|
45
|
+
MathExpressionBinaryOperatorSymbol["ShiftRight"] = ">>";
|
|
46
|
+
MathExpressionBinaryOperatorSymbol["Subtract"] = "-";
|
|
47
|
+
MathExpressionBinaryOperatorSymbol["UnsignedShiftRight"] = ">>>";
|
|
48
|
+
MathExpressionBinaryOperatorSymbol["Xor"] = "xor";
|
|
49
|
+
return MathExpressionBinaryOperatorSymbol;
|
|
50
|
+
}({});
|
|
51
|
+
// src/models/parser/MathExpressionGrammarRule.ts
|
|
52
|
+
let MathExpressionGrammarRule = /* @__PURE__ */ function(MathExpressionGrammarRule) {
|
|
53
|
+
MathExpressionGrammarRule["Assignment"] = "Assignment";
|
|
54
|
+
MathExpressionGrammarRule["Binary"] = "Binary";
|
|
55
|
+
MathExpressionGrammarRule["Conditional"] = "Conditional";
|
|
56
|
+
MathExpressionGrammarRule["DivisionBeforeProduct"] = "DivisionBeforeProduct";
|
|
57
|
+
MathExpressionGrammarRule["ImplicitProduct"] = "ImplicitProduct";
|
|
58
|
+
MathExpressionGrammarRule["LeftHand"] = "LeftHand";
|
|
59
|
+
MathExpressionGrammarRule["Nullish"] = "Nullish";
|
|
60
|
+
MathExpressionGrammarRule["Percentage"] = "Percentage";
|
|
61
|
+
MathExpressionGrammarRule["Power"] = "Power";
|
|
62
|
+
MathExpressionGrammarRule["Symbol"] = "Symbol";
|
|
63
|
+
MathExpressionGrammarRule["Unary"] = "Unary";
|
|
64
|
+
return MathExpressionGrammarRule;
|
|
65
|
+
}({});
|
|
66
|
+
// src/models/parser/MathExpressionTokenType.ts
|
|
67
|
+
let MathExpressionTokenType = /* @__PURE__ */ function(MathExpressionTokenType) {
|
|
68
|
+
MathExpressionTokenType["Delimiter"] = "delimiter";
|
|
69
|
+
MathExpressionTokenType["Null"] = "null";
|
|
70
|
+
MathExpressionTokenType["Number"] = "number";
|
|
71
|
+
MathExpressionTokenType["Symbol"] = "symbol";
|
|
72
|
+
MathExpressionTokenType["Unknown"] = "unknown";
|
|
73
|
+
return MathExpressionTokenType;
|
|
74
|
+
}({});
|
|
75
|
+
// src/models/parser/token/MathExpressionRadix.ts
|
|
76
|
+
let MathExpressionRadix = /* @__PURE__ */ function(MathExpressionRadix) {
|
|
77
|
+
MathExpressionRadix["Binary"] = "b";
|
|
78
|
+
MathExpressionRadix["Hexadecimal"] = "x";
|
|
79
|
+
MathExpressionRadix["Octal"] = "o";
|
|
80
|
+
return MathExpressionRadix;
|
|
81
|
+
}({});
|
|
82
|
+
// src/models/shared/MathExpressionError.ts
|
|
83
|
+
var MathExpressionError = class extends Error {
|
|
84
|
+
constructor(message) {
|
|
85
|
+
super(message);
|
|
86
|
+
this.name = "MathExpressionError";
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
// src/models/shared/MathExpressionLogicalOperator.ts
|
|
90
|
+
let MathExpressionLogicalOperator = /* @__PURE__ */ function(MathExpressionLogicalOperator) {
|
|
91
|
+
MathExpressionLogicalOperator["And"] = "and";
|
|
92
|
+
MathExpressionLogicalOperator["Nullish"] = "nullish";
|
|
93
|
+
MathExpressionLogicalOperator["Or"] = "or";
|
|
94
|
+
return MathExpressionLogicalOperator;
|
|
95
|
+
}({});
|
|
96
|
+
// src/models/shared/MathExpressionNodeType.ts
|
|
97
|
+
let MathExpressionNodeType = /* @__PURE__ */ function(MathExpressionNodeType) {
|
|
98
|
+
MathExpressionNodeType["Assignment"] = "assignment";
|
|
99
|
+
MathExpressionNodeType["Binary"] = "binary";
|
|
100
|
+
MathExpressionNodeType["Call"] = "call";
|
|
101
|
+
MathExpressionNodeType["Comparison"] = "comparison";
|
|
102
|
+
MathExpressionNodeType["Conditional"] = "conditional";
|
|
103
|
+
MathExpressionNodeType["Group"] = "group";
|
|
104
|
+
MathExpressionNodeType["List"] = "list";
|
|
105
|
+
MathExpressionNodeType["Logical"] = "logical";
|
|
106
|
+
MathExpressionNodeType["Name"] = "name";
|
|
107
|
+
MathExpressionNodeType["Number"] = "number";
|
|
108
|
+
MathExpressionNodeType["Percentage"] = "percentage";
|
|
109
|
+
MathExpressionNodeType["Unary"] = "unary";
|
|
110
|
+
return MathExpressionNodeType;
|
|
111
|
+
}({});
|
|
112
|
+
// src/services/compiler/constants.ts
|
|
113
|
+
const NO_VALUES = [];
|
|
114
|
+
const NO_LOCALS = [];
|
|
115
|
+
const ADDITIVE_NAMES = ["add", "subtract"];
|
|
116
|
+
// src/services/compiler/checkIsPercentageExpression.ts
|
|
117
|
+
const checkIsPercentageExpression = (node) => {
|
|
118
|
+
if (node.type === "percentage") return true;
|
|
119
|
+
if (node.type !== "binary") return false;
|
|
120
|
+
if (!ADDITIVE_NAMES.includes(node.name)) return false;
|
|
121
|
+
return checkIsPercentageExpression(node.left) && checkIsPercentageExpression(node.right);
|
|
122
|
+
};
|
|
123
|
+
// src/services/shared/getMathExpressionChildren.ts
|
|
124
|
+
const getMathExpressionChildren = (node) => {
|
|
125
|
+
switch (node.type) {
|
|
126
|
+
case "assignment": return [node.value];
|
|
127
|
+
case "binary":
|
|
128
|
+
case "logical": return [node.left, node.right];
|
|
129
|
+
case "call": return node.arguments;
|
|
130
|
+
case "comparison": return node.operands;
|
|
131
|
+
case "conditional": return [
|
|
132
|
+
node.condition,
|
|
133
|
+
node.whenTrue,
|
|
134
|
+
node.whenFalse
|
|
135
|
+
];
|
|
136
|
+
case "group":
|
|
137
|
+
case "percentage":
|
|
138
|
+
case "unary": return [node.operand];
|
|
139
|
+
case "list": return node.elements;
|
|
140
|
+
case "name":
|
|
141
|
+
case "number": return [];
|
|
142
|
+
default: return exhaustiveGuard(node);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
// src/services/compiler/collectMathExpressionAssignedNames.ts
|
|
146
|
+
const collectMathExpressionAssignedNames = (node, names) => {
|
|
147
|
+
if (node.type === "assignment") names.add(node.name);
|
|
148
|
+
for (const child of getMathExpressionChildren(node)) collectMathExpressionAssignedNames(child, names);
|
|
149
|
+
};
|
|
150
|
+
// src/services/compiler/compileList.ts
|
|
151
|
+
const compileList = (elements, context) => {
|
|
152
|
+
const compiledElements = elements.map((element) => context.compile(element));
|
|
153
|
+
return {
|
|
154
|
+
evaluate: (values, locals) => {
|
|
155
|
+
const numbers = [];
|
|
156
|
+
for (const element of compiledElements) {
|
|
157
|
+
const elementValue = element.evaluate(values, locals);
|
|
158
|
+
if (elementValue === null) return null;
|
|
159
|
+
numbers.push(elementValue);
|
|
160
|
+
}
|
|
161
|
+
return numbers;
|
|
162
|
+
},
|
|
163
|
+
isConstant: compiledElements.every((element) => element.isConstant)
|
|
164
|
+
};
|
|
165
|
+
};
|
|
166
|
+
// src/services/compiler/compileArgument.ts
|
|
167
|
+
const compileArgument = (node, context) => {
|
|
168
|
+
if (node.type === "list") return compileList(node.elements, context);
|
|
169
|
+
return context.compile(node);
|
|
170
|
+
};
|
|
171
|
+
// src/services/compiler/node/compileAssignment.ts
|
|
172
|
+
const compileAssignment = (node, context) => {
|
|
173
|
+
const slot = context.assignedSlots.get(node.name) ?? 0;
|
|
174
|
+
const compiledValue = context.compile(node.value);
|
|
175
|
+
return {
|
|
176
|
+
evaluate: (values, locals) => {
|
|
177
|
+
const value = compiledValue.evaluate(values, locals);
|
|
178
|
+
locals[slot] = value;
|
|
179
|
+
return value;
|
|
180
|
+
},
|
|
181
|
+
isConstant: false
|
|
182
|
+
};
|
|
183
|
+
};
|
|
184
|
+
// src/services/function/constants.ts
|
|
185
|
+
const MATH_EXPRESSION_RELATIVE_TOLERANCE = 1e-12;
|
|
186
|
+
const MATH_EXPRESSION_ABSOLUTE_TOLERANCE = 1e-15;
|
|
187
|
+
const SQRT_PI = Math.sqrt(Math.PI);
|
|
188
|
+
const BERNOULLI_CLOSED_FORM_INDEX = 54;
|
|
189
|
+
const MAXIMUM_DIGITS = 15;
|
|
190
|
+
const MAXIMUM_STIRLING_WORK = 4e6;
|
|
191
|
+
const MILLER_RABIN_BASES = [
|
|
192
|
+
2n,
|
|
193
|
+
3n,
|
|
194
|
+
5n,
|
|
195
|
+
7n,
|
|
196
|
+
11n,
|
|
197
|
+
13n,
|
|
198
|
+
17n,
|
|
199
|
+
19n,
|
|
200
|
+
23n,
|
|
201
|
+
29n,
|
|
202
|
+
31n,
|
|
203
|
+
37n
|
|
204
|
+
];
|
|
205
|
+
const REFINEMENT_STEPS = 4;
|
|
206
|
+
const LOW_TAIL_COEFFICIENTS = [
|
|
207
|
+
3.43273939e-7,
|
|
208
|
+
-35233877e-13,
|
|
209
|
+
-439150654e-14,
|
|
210
|
+
.00021858087,
|
|
211
|
+
-.00125372503,
|
|
212
|
+
-.00417768164,
|
|
213
|
+
.246640727,
|
|
214
|
+
1.50140941
|
|
215
|
+
];
|
|
216
|
+
const HIGH_TAIL_COEFFICIENTS = [
|
|
217
|
+
.000100950558,
|
|
218
|
+
.00134934322,
|
|
219
|
+
-.00367342844,
|
|
220
|
+
.00573950773,
|
|
221
|
+
-.0076224613,
|
|
222
|
+
.00943887047,
|
|
223
|
+
1.00167406,
|
|
224
|
+
2.83297682
|
|
225
|
+
];
|
|
226
|
+
const OVERFLOW_ARGUMENT = 171.35;
|
|
227
|
+
const LAST_FACTORIAL_ARGUMENT = 171;
|
|
228
|
+
const STIRLING_THRESHOLD = 15;
|
|
229
|
+
const STIRLING_TERMS = [
|
|
230
|
+
1 / 12,
|
|
231
|
+
-1 / 360,
|
|
232
|
+
1 / 1260,
|
|
233
|
+
-1 / 1680,
|
|
234
|
+
1 / 1188,
|
|
235
|
+
-691 / 360360,
|
|
236
|
+
1 / 156
|
|
237
|
+
];
|
|
238
|
+
const EULER_MACLAURIN_COEFFICIENTS = [
|
|
239
|
+
1 / 12,
|
|
240
|
+
-1 / 720,
|
|
241
|
+
1 / 30240,
|
|
242
|
+
-1 / 1209600,
|
|
243
|
+
1 / 47900160,
|
|
244
|
+
-691 / 1307674368e3,
|
|
245
|
+
1 / 74724249600,
|
|
246
|
+
-3617 / 0x25e8dffc150000
|
|
247
|
+
];
|
|
248
|
+
const SUMMATION_LIMIT = 10;
|
|
249
|
+
// src/services/function/checkIsNearlyEqual.ts
|
|
250
|
+
const checkIsNearlyEqual = (left, right) => {
|
|
251
|
+
if (Number.isNaN(left) || Number.isNaN(right)) return false;
|
|
252
|
+
if (!Number.isFinite(left) || !Number.isFinite(right)) return left === right;
|
|
253
|
+
if (left === right) return true;
|
|
254
|
+
const tolerance = Math.max(MATH_EXPRESSION_RELATIVE_TOLERANCE * Math.max(Math.abs(left), Math.abs(right)), MATH_EXPRESSION_ABSOLUTE_TOLERANCE);
|
|
255
|
+
return Math.abs(left - right) <= tolerance;
|
|
256
|
+
};
|
|
257
|
+
// src/services/function/checkIsLarger.ts
|
|
258
|
+
const checkIsLarger = (left, right) => left > right && !checkIsNearlyEqual(left, right);
|
|
259
|
+
// src/services/function/computeModularPower.ts
|
|
260
|
+
const computeModularPower = (base, exponent, modulus) => {
|
|
261
|
+
let result = 1n;
|
|
262
|
+
let square = base % modulus;
|
|
263
|
+
let remaining = exponent;
|
|
264
|
+
while (remaining > 0n) {
|
|
265
|
+
if (remaining % 2n === 1n) result = result * square % modulus;
|
|
266
|
+
square = square * square % modulus;
|
|
267
|
+
remaining /= 2n;
|
|
268
|
+
}
|
|
269
|
+
return result;
|
|
270
|
+
};
|
|
271
|
+
// src/services/function/checkIsMillerRabinPassing.ts
|
|
272
|
+
const checkIsMillerRabinPassing = (candidate, base, oddPart, twos) => {
|
|
273
|
+
const minusOne = candidate - 1n;
|
|
274
|
+
let power = computeModularPower(base, oddPart, candidate);
|
|
275
|
+
if (power === 1n || power === minusOne) return true;
|
|
276
|
+
for (let round = 1; round < twos; round++) {
|
|
277
|
+
power = power * power % candidate;
|
|
278
|
+
if (power === minusOne) return true;
|
|
279
|
+
}
|
|
280
|
+
return false;
|
|
281
|
+
};
|
|
282
|
+
// src/services/function/checkIsPrime.ts
|
|
283
|
+
const checkIsPrime = (value) => {
|
|
284
|
+
if (!Number.isInteger(value) || value < 2 || value > Number.MAX_SAFE_INTEGER) return false;
|
|
285
|
+
const candidate = BigInt(value);
|
|
286
|
+
for (const base of MILLER_RABIN_BASES) {
|
|
287
|
+
if (candidate === base) return true;
|
|
288
|
+
if (candidate % base === 0n) return false;
|
|
289
|
+
}
|
|
290
|
+
let oddPart = candidate - 1n;
|
|
291
|
+
let twos = 0;
|
|
292
|
+
while (oddPart % 2n === 0n) {
|
|
293
|
+
oddPart /= 2n;
|
|
294
|
+
twos++;
|
|
295
|
+
}
|
|
296
|
+
return MILLER_RABIN_BASES.every((base) => checkIsMillerRabinPassing(candidate, base, oddPart, twos));
|
|
297
|
+
};
|
|
298
|
+
// src/services/function/checkIsSmaller.ts
|
|
299
|
+
const checkIsSmaller = (left, right) => left < right && !checkIsNearlyEqual(left, right);
|
|
300
|
+
// src/services/function/computeBellNumber.ts
|
|
301
|
+
const computeBellNumber = (index) => {
|
|
302
|
+
if (!Number.isInteger(index) || index < 0) return null;
|
|
303
|
+
let row = [1];
|
|
304
|
+
for (let step = 1; step <= index; step++) {
|
|
305
|
+
const next = [row.at(-1) ?? 0];
|
|
306
|
+
for (const above of row) next.push((next.at(-1) ?? 0) + above);
|
|
307
|
+
if (next[0] === Number.POSITIVE_INFINITY) return Number.POSITIVE_INFINITY;
|
|
308
|
+
row = next;
|
|
309
|
+
}
|
|
310
|
+
return row[0] ?? NaN;
|
|
311
|
+
};
|
|
312
|
+
// src/services/function/computeBernoulliNumber.ts
|
|
313
|
+
const computeBernoulliNumber = (index) => {
|
|
314
|
+
if (index === 1) return -.5;
|
|
315
|
+
if (index > 1 && index % 2 === 1) return 0;
|
|
316
|
+
if (index >= 54) {
|
|
317
|
+
let product = 1;
|
|
318
|
+
for (let factor = 1; factor <= index && Number.isFinite(product); factor++) product *= factor / (2 * Math.PI);
|
|
319
|
+
return index / 2 % 2 === 1 ? 2 * product : -2 * product;
|
|
320
|
+
}
|
|
321
|
+
const numbers = [1];
|
|
322
|
+
for (let current = 1; current <= index; current++) {
|
|
323
|
+
let sum = 0;
|
|
324
|
+
let binomial = 1;
|
|
325
|
+
for (let previous = 0; previous < current; previous++) {
|
|
326
|
+
sum += binomial * (numbers[previous] ?? 0);
|
|
327
|
+
binomial = binomial * (current + 1 - previous) / (previous + 1);
|
|
328
|
+
}
|
|
329
|
+
numbers.push(-sum / (current + 1));
|
|
330
|
+
}
|
|
331
|
+
return numbers[index] ?? NaN;
|
|
332
|
+
};
|
|
333
|
+
// src/services/function/computeBitwiseAnd.ts
|
|
334
|
+
const computeBitwiseAnd = (left, right) => left & right;
|
|
335
|
+
// src/services/function/computeBitwiseOr.ts
|
|
336
|
+
const computeBitwiseOr = (left, right) => left | right;
|
|
337
|
+
// src/services/function/computeCombinations.ts
|
|
338
|
+
const computeCombinations = (available, chosen) => {
|
|
339
|
+
const smaller = Math.min(chosen, available - chosen);
|
|
340
|
+
let result = 1;
|
|
341
|
+
for (let step = 1; step <= smaller; step++) {
|
|
342
|
+
result = result * (available - smaller + step) / step;
|
|
343
|
+
if (result > Number.MAX_VALUE) return Number.POSITIVE_INFINITY;
|
|
344
|
+
}
|
|
345
|
+
return Math.round(result);
|
|
346
|
+
};
|
|
347
|
+
// src/services/function/computeCatalan.ts
|
|
348
|
+
const computeCatalan = (index) => {
|
|
349
|
+
if (!Number.isInteger(index) || index < 0) return null;
|
|
350
|
+
return Math.round(computeCombinations(2 * index, index) / (index + 1));
|
|
351
|
+
};
|
|
352
|
+
// src/services/function/computeComparison.ts
|
|
353
|
+
const computeComparison = (left, right) => {
|
|
354
|
+
if (checkIsNearlyEqual(left, right)) return 0;
|
|
355
|
+
return left > right ? 1 : -1;
|
|
356
|
+
};
|
|
357
|
+
// src/services/function/computeCompositions.ts
|
|
358
|
+
const computeCompositions = (total, parts) => {
|
|
359
|
+
if (!Number.isInteger(total) || !Number.isInteger(parts) || parts < 1 || parts > total) return null;
|
|
360
|
+
return computeCombinations(total - 1, parts - 1);
|
|
361
|
+
};
|
|
362
|
+
// src/services/function/computeDotProduct.ts
|
|
363
|
+
const computeDotProduct = (left, right) => {
|
|
364
|
+
if (left.length !== right.length) return null;
|
|
365
|
+
return left.reduce((total, value, index) => total + value * (right[index] ?? NaN), 0);
|
|
366
|
+
};
|
|
367
|
+
// src/services/function/computeSum.ts
|
|
368
|
+
const computeSum = (values) => values.length === 0 ? 0 : values.reduce((total, value) => total + value);
|
|
369
|
+
// src/services/function/computeCorrelation.ts
|
|
370
|
+
const computeCorrelation = (left, right) => {
|
|
371
|
+
if (left.length !== right.length) return null;
|
|
372
|
+
const count = left.length;
|
|
373
|
+
const sumLeft = computeSum(left);
|
|
374
|
+
const sumRight = computeSum(right);
|
|
375
|
+
const sumProducts = computeDotProduct(left, right) ?? NaN;
|
|
376
|
+
const sumLeftSquares = computeSum(left.map((value) => value ** 2));
|
|
377
|
+
const sumRightSquares = computeSum(right.map((value) => value ** 2));
|
|
378
|
+
return (count * sumProducts - sumLeft * sumRight) / Math.sqrt((count * sumLeftSquares - sumLeft ** 2) * (count * sumRightSquares - sumRight ** 2));
|
|
379
|
+
};
|
|
380
|
+
// src/services/function/computeDeterminant.ts
|
|
381
|
+
const computeDeterminant = (values) => {
|
|
382
|
+
if (values.length === 0) return 1;
|
|
383
|
+
return values.length === 1 ? values[0] ?? null : null;
|
|
384
|
+
};
|
|
385
|
+
// src/services/function/computePointToLine2D.ts
|
|
386
|
+
const computePointToLine2D = (point, line) => {
|
|
387
|
+
const [x = NaN, y = NaN] = point;
|
|
388
|
+
const [a = NaN, b = NaN, c = NaN] = line;
|
|
389
|
+
return Math.abs(a * x + b * y + c) / Math.hypot(a, b);
|
|
390
|
+
};
|
|
391
|
+
// src/services/function/computePointToLine3D.ts
|
|
392
|
+
const computePointToLine3D = (point, line) => {
|
|
393
|
+
const [x = NaN, y = NaN, z = NaN] = point;
|
|
394
|
+
const [x0 = NaN, y0 = NaN, z0 = NaN, a = NaN, b = NaN, c = NaN] = line;
|
|
395
|
+
const first = (y0 - y) * c - (z0 - z) * b;
|
|
396
|
+
const second = (z0 - z) * a - (x0 - x) * c;
|
|
397
|
+
const third = (x0 - x) * b - (y0 - y) * a;
|
|
398
|
+
return Math.hypot(first, second, third) / Math.hypot(a, b, c);
|
|
399
|
+
};
|
|
400
|
+
// src/services/function/computeDistance.ts
|
|
401
|
+
const computeDistance = (point, other) => {
|
|
402
|
+
if (point.length === 2 && other.length === 3) return computePointToLine2D(point, other);
|
|
403
|
+
if (point.length === 3 && other.length === 6) return computePointToLine3D(point, other);
|
|
404
|
+
if (point.length !== other.length || point.length === 0) return null;
|
|
405
|
+
let total = 0;
|
|
406
|
+
for (const [index, value] of point.entries()) {
|
|
407
|
+
const difference = value - (other[index] ?? NaN);
|
|
408
|
+
total = difference * difference + total;
|
|
409
|
+
}
|
|
410
|
+
return Math.sqrt(total);
|
|
411
|
+
};
|
|
412
|
+
// src/services/function/computeErf.ts
|
|
413
|
+
const SERIES_LIMIT = 3;
|
|
414
|
+
const CONTINUED_FRACTION_DEPTH = 80;
|
|
415
|
+
const computeErf = (value) => {
|
|
416
|
+
if (Number.isNaN(value)) return NaN;
|
|
417
|
+
const magnitude = Math.abs(value);
|
|
418
|
+
const sign = value < 0 ? -1 : 1;
|
|
419
|
+
if (magnitude < SERIES_LIMIT) {
|
|
420
|
+
const squared = magnitude * magnitude;
|
|
421
|
+
let term = magnitude;
|
|
422
|
+
let total = magnitude;
|
|
423
|
+
for (let index = 1; index < 200; index++) {
|
|
424
|
+
term *= 2 * squared / (2 * index + 1);
|
|
425
|
+
total += term;
|
|
426
|
+
if (term < total * 1e-17) break;
|
|
427
|
+
}
|
|
428
|
+
return sign * 2 * Math.exp(-squared) * total / SQRT_PI;
|
|
429
|
+
}
|
|
430
|
+
let fraction = magnitude;
|
|
431
|
+
for (let index = CONTINUED_FRACTION_DEPTH; index >= 1; index--) fraction = magnitude + index / 2 / fraction;
|
|
432
|
+
return sign * (1 - Math.exp(-magnitude * magnitude) / SQRT_PI / fraction);
|
|
433
|
+
};
|
|
434
|
+
// src/services/function/computePolynomial.ts
|
|
435
|
+
const computePolynomial = (leading, coefficients, variable) => {
|
|
436
|
+
let polynomial = leading;
|
|
437
|
+
for (const coefficient of coefficients) polynomial = coefficient + polynomial * variable;
|
|
438
|
+
return polynomial;
|
|
439
|
+
};
|
|
440
|
+
// src/services/function/computeInitialGuess.ts
|
|
441
|
+
const computeInitialGuess = (value) => {
|
|
442
|
+
const weight = -Math.log((1 - value) * (1 + value));
|
|
443
|
+
if (weight < 5) return computePolynomial(2.81022636e-8, LOW_TAIL_COEFFICIENTS, weight - 2.5) * value;
|
|
444
|
+
return computePolynomial(-.000200214257, HIGH_TAIL_COEFFICIENTS, Math.sqrt(weight) - 3) * value;
|
|
445
|
+
};
|
|
446
|
+
// src/services/function/computeErfInverse.ts
|
|
447
|
+
const computeErfInverse = (value) => {
|
|
448
|
+
if (Number.isNaN(value) || value < -1 || value > 1) return NaN;
|
|
449
|
+
if (value === 1) return Number.POSITIVE_INFINITY;
|
|
450
|
+
if (value === -1) return Number.NEGATIVE_INFINITY;
|
|
451
|
+
let estimate = computeInitialGuess(value);
|
|
452
|
+
for (let step = 0; step < 4; step++) {
|
|
453
|
+
const slope = 2 / SQRT_PI * Math.exp(-estimate * estimate);
|
|
454
|
+
estimate -= (computeErf(estimate) - value) / slope;
|
|
455
|
+
}
|
|
456
|
+
return estimate;
|
|
457
|
+
};
|
|
458
|
+
// src/services/function/computeStirlingLogGamma.ts
|
|
459
|
+
const computeStirlingLogGamma = (value) => {
|
|
460
|
+
const inverse = 1 / value;
|
|
461
|
+
const squared = inverse * inverse;
|
|
462
|
+
let power = inverse;
|
|
463
|
+
let series = 0;
|
|
464
|
+
for (const term of STIRLING_TERMS) {
|
|
465
|
+
series += term * power;
|
|
466
|
+
power *= squared;
|
|
467
|
+
}
|
|
468
|
+
return (value - .5) * Math.log(value) - value + .5 * Math.log(2 * Math.PI) + series;
|
|
469
|
+
};
|
|
470
|
+
// src/services/function/computeLogGammaOfPositive.ts
|
|
471
|
+
const computeLogGammaOfPositive = (value) => {
|
|
472
|
+
let shifted = value;
|
|
473
|
+
let product = 1;
|
|
474
|
+
while (shifted < 15) {
|
|
475
|
+
product *= shifted;
|
|
476
|
+
shifted += 1;
|
|
477
|
+
}
|
|
478
|
+
return {
|
|
479
|
+
logGamma: computeStirlingLogGamma(shifted),
|
|
480
|
+
product
|
|
481
|
+
};
|
|
482
|
+
};
|
|
483
|
+
// src/services/function/computeProductOfRange.ts
|
|
484
|
+
const computeProductOfRange = (first, last) => {
|
|
485
|
+
if (last < first) return 1;
|
|
486
|
+
if (last === first) return last;
|
|
487
|
+
const middle = Math.floor((first + last) / 2);
|
|
488
|
+
return computeProductOfRange(first, middle) * computeProductOfRange(middle + 1, last);
|
|
489
|
+
};
|
|
490
|
+
// src/services/function/computeGamma.ts
|
|
491
|
+
const computeGamma = (value) => {
|
|
492
|
+
if (Number.isNaN(value)) return NaN;
|
|
493
|
+
if (Number.isInteger(value)) {
|
|
494
|
+
if (value <= 0 || value > 171) return Number.POSITIVE_INFINITY;
|
|
495
|
+
return computeProductOfRange(1, value - 1);
|
|
496
|
+
}
|
|
497
|
+
if (value < .5) return Math.PI / (Math.sin(Math.PI * value) * computeGamma(1 - value));
|
|
498
|
+
if (value >= 171.35) return Number.POSITIVE_INFINITY;
|
|
499
|
+
const { logGamma, product } = computeLogGammaOfPositive(value);
|
|
500
|
+
return Math.exp(logGamma) / product;
|
|
501
|
+
};
|
|
502
|
+
// src/services/function/computeFactorial.ts
|
|
503
|
+
const computeFactorial = (value) => {
|
|
504
|
+
if (value < 0) return null;
|
|
505
|
+
return computeGamma(value + 1);
|
|
506
|
+
};
|
|
507
|
+
// src/services/function/computeHypotenuse.ts
|
|
508
|
+
const computeHypotenuse = (values) => {
|
|
509
|
+
let largest = 0;
|
|
510
|
+
let total = 0;
|
|
511
|
+
for (const value of values) {
|
|
512
|
+
const magnitude = Math.abs(value);
|
|
513
|
+
if (largest < magnitude) {
|
|
514
|
+
const ratio = largest / magnitude;
|
|
515
|
+
total = total * (ratio * ratio) + 1;
|
|
516
|
+
largest = magnitude;
|
|
517
|
+
} else if (magnitude > 0) {
|
|
518
|
+
const ratio = magnitude === largest ? 1 : magnitude / largest;
|
|
519
|
+
total += ratio * ratio;
|
|
520
|
+
} else total += magnitude;
|
|
521
|
+
}
|
|
522
|
+
return largest * Math.sqrt(total);
|
|
523
|
+
};
|
|
524
|
+
// src/services/function/computeGreatestCommonDivisor.ts
|
|
525
|
+
const computeGreatestCommonDivisor = (left, right) => right === 0 ? Math.abs(left) : computeGreatestCommonDivisor(right, left % right);
|
|
526
|
+
// src/services/function/computeIntegerGreatestCommonDivisor.ts
|
|
527
|
+
const computeIntegerGreatestCommonDivisor = (values) => {
|
|
528
|
+
if (!values.every((value) => Number.isInteger(value))) return null;
|
|
529
|
+
return values.reduce((total, value) => computeGreatestCommonDivisor(total, value), 0);
|
|
530
|
+
};
|
|
531
|
+
// src/services/function/computeInverseCoth.ts
|
|
532
|
+
const computeInverseCoth = (value) => {
|
|
533
|
+
const magnitude = Math.abs(value);
|
|
534
|
+
if (magnitude < 1) return NaN;
|
|
535
|
+
if (magnitude === 1) return value * Number.POSITIVE_INFINITY;
|
|
536
|
+
return Math.sign(value) * .5 * Math.log1p(2 / (magnitude - 1));
|
|
537
|
+
};
|
|
538
|
+
// src/services/function/computeKullbackLeibler.ts
|
|
539
|
+
const computeKullbackLeibler = (distribution, reference) => {
|
|
540
|
+
if (distribution.length !== reference.length) return null;
|
|
541
|
+
const distributionSum = computeSum(distribution);
|
|
542
|
+
const referenceSum = computeSum(reference);
|
|
543
|
+
if (distributionSum === 0 || referenceSum === 0) return null;
|
|
544
|
+
const normalizedDistribution = distribution.map((value) => value / distributionSum);
|
|
545
|
+
const normalizedReference = reference.map((value) => value / referenceSum);
|
|
546
|
+
return computeSum(normalizedDistribution.map((value, index) => value * Math.log(value / (normalizedReference[index] ?? NaN))));
|
|
547
|
+
};
|
|
548
|
+
// src/services/function/computeLeastCommonMultiple.ts
|
|
549
|
+
const computeLeastCommonMultiple = (left, right) => {
|
|
550
|
+
if (!Number.isInteger(left) || !Number.isInteger(right)) return null;
|
|
551
|
+
const divisor = computeGreatestCommonDivisor(left, right);
|
|
552
|
+
return divisor === 0 ? 0 : Math.abs(left * right / divisor);
|
|
553
|
+
};
|
|
554
|
+
// src/services/function/computeListComparison.ts
|
|
555
|
+
const computeListComparison = (left, right) => {
|
|
556
|
+
for (const [index, value] of left.entries()) {
|
|
557
|
+
const other = right[index];
|
|
558
|
+
if (other === void 0) return 1;
|
|
559
|
+
const comparison = computeComparison(value, other);
|
|
560
|
+
if (comparison !== 0) return comparison;
|
|
561
|
+
}
|
|
562
|
+
return left.length < right.length ? -1 : 0;
|
|
563
|
+
};
|
|
564
|
+
// src/services/function/computeLogarithm.ts
|
|
565
|
+
const computeLogarithm = (value, base) => base === void 0 ? Math.log(value) : Math.log(value) / Math.log(base);
|
|
566
|
+
// src/services/function/computeLogGamma.ts
|
|
567
|
+
const computeLogGamma = (value) => {
|
|
568
|
+
if (Number.isNaN(value) || value < 0) return NaN;
|
|
569
|
+
if ([0, Number.POSITIVE_INFINITY].includes(value)) return Number.POSITIVE_INFINITY;
|
|
570
|
+
if ([1, 2].includes(value)) return 0;
|
|
571
|
+
const { logGamma, product } = computeLogGammaOfPositive(value);
|
|
572
|
+
return logGamma - Math.log(product);
|
|
573
|
+
};
|
|
574
|
+
// src/services/function/computeMean.ts
|
|
575
|
+
const computeMean = (values) => computeSum(values) / values.length;
|
|
576
|
+
// src/services/function/compareNumbers.ts
|
|
577
|
+
const compareNumbers = (left, right) => left - right;
|
|
578
|
+
// src/services/function/computeMedian.ts
|
|
579
|
+
const computeMedian = (values) => {
|
|
580
|
+
const sorted = values.toSorted(compareNumbers);
|
|
581
|
+
const middle = Math.floor(sorted.length / 2);
|
|
582
|
+
if (sorted.length % 2 === 1) return sorted[middle] ?? NaN;
|
|
583
|
+
return ((sorted[middle - 1] ?? NaN) + (sorted[middle] ?? NaN)) / 2;
|
|
584
|
+
};
|
|
585
|
+
// src/services/function/computeMedianAbsoluteDeviation.ts
|
|
586
|
+
const computeMedianAbsoluteDeviation = (values) => {
|
|
587
|
+
const center = computeMedian(values);
|
|
588
|
+
return computeMedian(values.map((value) => Math.abs(value - center)));
|
|
589
|
+
};
|
|
590
|
+
// src/services/function/computeModulo.ts
|
|
591
|
+
const computeModulo = (dividend, divisor) => {
|
|
592
|
+
if (divisor === 0) return dividend;
|
|
593
|
+
if (!Number.isFinite(dividend) || !Number.isFinite(divisor)) return NaN;
|
|
594
|
+
if (Number.isInteger(dividend) && Number.isInteger(divisor)) {
|
|
595
|
+
const remainder = dividend % divisor;
|
|
596
|
+
if (remainder === 0) return 0;
|
|
597
|
+
return Math.sign(remainder) === Math.sign(divisor) ? remainder : remainder + divisor;
|
|
598
|
+
}
|
|
599
|
+
const quotient = dividend / divisor;
|
|
600
|
+
if (checkIsNearlyEqual(quotient, Math.round(quotient))) return 0;
|
|
601
|
+
return dividend - divisor * Math.floor(quotient);
|
|
602
|
+
};
|
|
603
|
+
// src/services/function/computeModularInverse.ts
|
|
604
|
+
const computeModularInverse = (value, modulus) => {
|
|
605
|
+
if (!Number.isInteger(value) || !Number.isInteger(modulus) || modulus === 0) return null;
|
|
606
|
+
let remainder = value;
|
|
607
|
+
let nextRemainder = modulus;
|
|
608
|
+
let coefficient = 0;
|
|
609
|
+
let previousCoefficient = 1;
|
|
610
|
+
while (nextRemainder !== 0) {
|
|
611
|
+
const quotient = Math.floor(remainder / nextRemainder);
|
|
612
|
+
[coefficient, previousCoefficient] = [previousCoefficient - quotient * coefficient, coefficient];
|
|
613
|
+
[remainder, nextRemainder] = [nextRemainder, remainder - quotient * nextRemainder];
|
|
614
|
+
}
|
|
615
|
+
if ((remainder < 0 ? -remainder : remainder) !== 1) return NaN;
|
|
616
|
+
const inverse = computeModulo(remainder < 0 ? -previousCoefficient : previousCoefficient, modulus);
|
|
617
|
+
return inverse < 0 ? inverse + modulus : inverse;
|
|
618
|
+
};
|
|
619
|
+
// src/services/function/computeMultinomial.ts
|
|
620
|
+
const computeMultinomial = (parts) => {
|
|
621
|
+
if (!parts.every((part) => Number.isInteger(part) && part > 0)) return null;
|
|
622
|
+
let result = 1;
|
|
623
|
+
let running = 0;
|
|
624
|
+
for (const part of parts) {
|
|
625
|
+
running += part;
|
|
626
|
+
result *= computeCombinations(running, part);
|
|
627
|
+
}
|
|
628
|
+
return result;
|
|
629
|
+
};
|
|
630
|
+
// src/services/function/computeNorm.ts
|
|
631
|
+
const computeNorm = (values) => {
|
|
632
|
+
if (values.length === 0) return 0;
|
|
633
|
+
if (values.length === 1) return Math.abs(values[0] ?? NaN);
|
|
634
|
+
return Math.sqrt(computeSum(values.map((value) => value * value)));
|
|
635
|
+
};
|
|
636
|
+
// src/services/function/computeNthRoot.ts
|
|
637
|
+
const computeNthRoot = (value, root) => {
|
|
638
|
+
const isInverse = root < 0;
|
|
639
|
+
const degree = Math.abs(root);
|
|
640
|
+
if (degree === 0) return null;
|
|
641
|
+
if (value < 0 && degree % 2 !== 1) return null;
|
|
642
|
+
if (value === 0) return isInverse ? Number.POSITIVE_INFINITY : 0;
|
|
643
|
+
if (!Number.isFinite(value)) return isInverse ? 0 : value;
|
|
644
|
+
let result = Math.abs(value) ** (1 / degree);
|
|
645
|
+
const rounded = Math.round(result);
|
|
646
|
+
if (Number.isInteger(degree) && rounded ** degree === Math.abs(value)) result = rounded;
|
|
647
|
+
if (value < 0) result = -result;
|
|
648
|
+
return isInverse ? 1 / result : result;
|
|
649
|
+
};
|
|
650
|
+
// src/services/function/computePartitionSelect.ts
|
|
651
|
+
const computePartitionSelect = (values, index) => {
|
|
652
|
+
if (!Number.isInteger(index) || index < 0 || index >= values.length) return null;
|
|
653
|
+
return values.toSorted(compareNumbers)[index] ?? null;
|
|
654
|
+
};
|
|
655
|
+
// src/services/function/computePermutations.ts
|
|
656
|
+
const computePermutations = (available, chosen) => {
|
|
657
|
+
if (!Number.isInteger(available) || !Number.isInteger(chosen) || chosen < 0 || chosen > available) return null;
|
|
658
|
+
let result = 1;
|
|
659
|
+
for (let factor = available - chosen + 1; factor <= available; factor++) {
|
|
660
|
+
result *= factor;
|
|
661
|
+
if (result > Number.MAX_VALUE) return Number.POSITIVE_INFINITY;
|
|
662
|
+
}
|
|
663
|
+
return result;
|
|
664
|
+
};
|
|
665
|
+
// src/services/function/scaleList.ts
|
|
666
|
+
const scaleList = (list, scalar) => list.map((element) => element * scalar);
|
|
667
|
+
// src/services/function/multiplyPair.ts
|
|
668
|
+
const multiplyPair = (left, right) => {
|
|
669
|
+
if (typeof left === "number" && typeof right === "number") return left * right;
|
|
670
|
+
if (typeof left === "number" && Array.isArray(right)) return scaleList(right, left);
|
|
671
|
+
if (Array.isArray(left) && typeof right === "number") return scaleList(left, right);
|
|
672
|
+
if (Array.isArray(left) && Array.isArray(right)) return computeDotProduct(left, right);
|
|
673
|
+
return null;
|
|
674
|
+
};
|
|
675
|
+
// src/services/function/computeProduct.ts
|
|
676
|
+
const computeProduct = (values) => {
|
|
677
|
+
let total = 1;
|
|
678
|
+
for (const value of values) {
|
|
679
|
+
if (total === null) return null;
|
|
680
|
+
total = multiplyPair(total, value);
|
|
681
|
+
}
|
|
682
|
+
return typeof total === "number" ? total : null;
|
|
683
|
+
};
|
|
684
|
+
// src/services/function/computeQuantile.ts
|
|
685
|
+
const computeQuantile = (values, probability, isSorted) => {
|
|
686
|
+
if (values.length === 0 || probability < 0 || probability > 1) return null;
|
|
687
|
+
const ordered = isSorted ? values : values.toSorted(compareNumbers);
|
|
688
|
+
const index = probability * (ordered.length - 1);
|
|
689
|
+
const integerPart = Math.floor(index);
|
|
690
|
+
if (Number.isInteger(index)) return ordered[integerPart] ?? NaN;
|
|
691
|
+
const fraction = index % 1;
|
|
692
|
+
return (ordered[integerPart] ?? NaN) * (1 - fraction) + (ordered[integerPart + 1] ?? NaN) * fraction;
|
|
693
|
+
};
|
|
694
|
+
// src/services/function/computeStirlingColumn.ts
|
|
695
|
+
const computeStirlingColumn = (count, parts) => {
|
|
696
|
+
const column = Array.from({ length: parts + 1 }, (_element, index) => index === 0 ? 1 : 0);
|
|
697
|
+
for (let step = 1; step <= count; step++) {
|
|
698
|
+
for (let part = parts; part >= 1; part--) column[part] = part * (column[part] ?? 0) + (column[part - 1] ?? 0);
|
|
699
|
+
column[0] = 0;
|
|
700
|
+
}
|
|
701
|
+
return column[parts] ?? 0;
|
|
702
|
+
};
|
|
703
|
+
// src/services/function/computeStirlingDiagonal.ts
|
|
704
|
+
const computeStirlingDiagonal = (count, index) => {
|
|
705
|
+
const diagonal = Array.from({ length: index + 1 }, (_element, position) => position === 0 ? 1 : 0);
|
|
706
|
+
for (let step = 1; step <= count; step++) for (let position = Math.min(step, index); position >= 1; position--) diagonal[position] = (step - position) * (diagonal[position - 1] ?? 0) + (diagonal[position] ?? 0);
|
|
707
|
+
return diagonal[index] ?? 0;
|
|
708
|
+
};
|
|
709
|
+
// src/services/function/computeStirlingSecondKind.ts
|
|
710
|
+
const computeStirlingSecondKind = (count, parts) => {
|
|
711
|
+
if (!Number.isInteger(count) || !Number.isInteger(parts) || count < 0 || parts < 0 || parts > count) return null;
|
|
712
|
+
if (parts === 0) return count === 0 ? 1 : 0;
|
|
713
|
+
if (parts === count) return 1;
|
|
714
|
+
const band = Math.min(parts, count - parts);
|
|
715
|
+
if (count * band > 4e6) return null;
|
|
716
|
+
return band === parts ? computeStirlingColumn(count, parts) : computeStirlingDiagonal(count, count - parts);
|
|
717
|
+
};
|
|
718
|
+
// src/services/shared/checkIsTruthy.ts
|
|
719
|
+
const checkIsTruthy = (value) => value !== 0 && !Number.isNaN(value);
|
|
720
|
+
// src/services/function/computeTruthyCount.ts
|
|
721
|
+
const computeTruthyCount = (values) => values.filter((value) => checkIsTruthy(value)).length;
|
|
722
|
+
// src/services/function/computeVariance.ts
|
|
723
|
+
const computeVariance = (values) => {
|
|
724
|
+
if (values.length === 1) return Number.isFinite(values[0] ?? NaN) ? 0 : NaN;
|
|
725
|
+
const average = computeMean(values);
|
|
726
|
+
return computeSum(values.map((value) => (value - average) ** 2)) / (values.length - 1);
|
|
727
|
+
};
|
|
728
|
+
// src/services/function/computeExtreme.ts
|
|
729
|
+
const computeExtreme = (values, initial, checkIsPreferred) => {
|
|
730
|
+
let extreme = initial;
|
|
731
|
+
for (const value of values) {
|
|
732
|
+
if (Number.isNaN(value)) return NaN;
|
|
733
|
+
if (checkIsPreferred(value, extreme)) extreme = value;
|
|
734
|
+
}
|
|
735
|
+
return extreme;
|
|
736
|
+
};
|
|
737
|
+
// src/services/function/computeVectorNorm.ts
|
|
738
|
+
const computeVectorNorm = (values, power) => {
|
|
739
|
+
if (Number.isNaN(power)) return null;
|
|
740
|
+
const magnitudes = values.map((value) => Math.abs(value));
|
|
741
|
+
if (Math.abs(power) === Number.POSITIVE_INFINITY) {
|
|
742
|
+
const [first] = magnitudes;
|
|
743
|
+
if (first === void 0) return 0;
|
|
744
|
+
return computeExtreme(magnitudes, first, power > 0 ? checkIsLarger : checkIsSmaller);
|
|
745
|
+
}
|
|
746
|
+
if (checkIsNearlyEqual(power, 0)) return Number.POSITIVE_INFINITY;
|
|
747
|
+
return computeSum(magnitudes.map((magnitude) => magnitude ** power)) ** (1 / power);
|
|
748
|
+
};
|
|
749
|
+
// src/services/function/computeSummedZeta.ts
|
|
750
|
+
const computeSummedZeta = (value) => {
|
|
751
|
+
let total = 0;
|
|
752
|
+
for (let integer = 1; integer < 10; integer++) total += integer ** -value;
|
|
753
|
+
total += 10 ** (1 - value) / (value - 1) + .5 * 10 ** -value;
|
|
754
|
+
let rising = value;
|
|
755
|
+
for (let index = 1; index <= EULER_MACLAURIN_COEFFICIENTS.length; index++) {
|
|
756
|
+
const coefficient = EULER_MACLAURIN_COEFFICIENTS[index - 1] ?? 0;
|
|
757
|
+
total += coefficient * rising * 10 ** (-value - 2 * index + 1);
|
|
758
|
+
rising *= (value + 2 * index - 1) * (value + 2 * index);
|
|
759
|
+
}
|
|
760
|
+
return total;
|
|
761
|
+
};
|
|
762
|
+
// src/services/function/computeZeta.ts
|
|
763
|
+
const computeZeta = (value) => {
|
|
764
|
+
if (value === Number.POSITIVE_INFINITY) return 1;
|
|
765
|
+
if (!Number.isFinite(value)) return NaN;
|
|
766
|
+
if (value === 1) return NaN;
|
|
767
|
+
if (value === 0) return -.5;
|
|
768
|
+
if (value > 0) return computeSummedZeta(value);
|
|
769
|
+
if (Number.isInteger(value) && value % 2 === 0) return 0;
|
|
770
|
+
if (1 - value >= 171.35) {
|
|
771
|
+
const sine = Math.sin(Math.PI * value / 2);
|
|
772
|
+
const logMagnitude = value * Math.LN2 + (value - 1) * Math.log(Math.PI) + Math.log(Math.abs(sine)) + computeLogGamma(1 - value) + Math.log(computeSummedZeta(1 - value));
|
|
773
|
+
return Math.sign(sine) * Math.exp(logMagnitude);
|
|
774
|
+
}
|
|
775
|
+
return 2 ** value * Math.PI ** (value - 1) * Math.sin(Math.PI * value / 2) * computeGamma(1 - value) * computeSummedZeta(1 - value);
|
|
776
|
+
};
|
|
777
|
+
// src/services/function/signature/createNumericCompute.ts
|
|
778
|
+
const createNumericCompute = (compute) => (values) => compute(values.flat());
|
|
779
|
+
// src/services/function/signature/createSignature.ts
|
|
780
|
+
const createSignature = (parameters, minArity, maxArity, compute, scalarCompute = {}) => ({
|
|
781
|
+
compute,
|
|
782
|
+
maxArity,
|
|
783
|
+
minArity,
|
|
784
|
+
parameters,
|
|
785
|
+
...scalarCompute
|
|
786
|
+
});
|
|
787
|
+
// src/services/function/signature/createNumberSignature.ts
|
|
788
|
+
const createNumberSignature = (minArity, maxArity, compute, scalarCompute) => createSignature(["number"], minArity, maxArity, createNumericCompute(compute), scalarCompute);
|
|
789
|
+
// src/services/function/signature/createRangedSignatures.ts
|
|
790
|
+
const createRangedSignatures = (minArity, maxArity, compute, scalarCompute) => [createNumberSignature(minArity, maxArity, compute, scalarCompute)];
|
|
791
|
+
// src/services/function/signature/createBinarySignatures.ts
|
|
792
|
+
const createBinarySignatures = (compute) => createRangedSignatures(2, 2, (values) => compute(values[0] ?? NaN, values[1] ?? NaN), { computeBinary: compute });
|
|
793
|
+
// src/services/function/signature/createBitwisePair.ts
|
|
794
|
+
const createBitwisePair = (absorbing, compute) => (left, right) => {
|
|
795
|
+
if (left === absorbing) return absorbing;
|
|
796
|
+
if (!Number.isInteger(left) || !Number.isInteger(right)) return null;
|
|
797
|
+
return compute(left, right);
|
|
798
|
+
};
|
|
799
|
+
// src/services/function/signature/createBitwiseFold.ts
|
|
800
|
+
const createBitwiseFold = (absorbing, compute) => {
|
|
801
|
+
const computePair = createBitwisePair(absorbing, compute);
|
|
802
|
+
return (values) => values.slice(1).reduce((result, value) => result === null ? null : computePair(result, value), values[0] ?? NaN);
|
|
803
|
+
};
|
|
804
|
+
// src/services/function/shiftDecimalPlaces.ts
|
|
805
|
+
const shiftDecimalPlaces = (value, places) => {
|
|
806
|
+
if (!Number.isFinite(value)) return value;
|
|
807
|
+
const [mantissa = "", exponent = "0"] = value.toExponential().split("e");
|
|
808
|
+
return Number(`${mantissa}e${Number(exponent) + places}`);
|
|
809
|
+
};
|
|
810
|
+
// src/services/function/roundToDigits.ts
|
|
811
|
+
const roundToDigits = (rounder, value, digits) => {
|
|
812
|
+
if (!Number.isInteger(digits) || digits < 0 || digits > 15) return null;
|
|
813
|
+
if (!Number.isFinite(value)) return value;
|
|
814
|
+
if (Number.isInteger(value)) return value + 0;
|
|
815
|
+
if (digits === 0) return rounder(value) + 0;
|
|
816
|
+
return shiftDecimalPlaces(rounder(shiftDecimalPlaces(value, digits)), -digits);
|
|
817
|
+
};
|
|
818
|
+
// src/services/function/signature/createDigitSignatures.ts
|
|
819
|
+
const createDigitSignatures = (rounder) => createRangedSignatures(1, 2, (values) => {
|
|
820
|
+
const [value = NaN, digits = 0] = values;
|
|
821
|
+
return roundToDigits(rounder, value, digits);
|
|
822
|
+
}, {
|
|
823
|
+
computeBinary: (value, digits) => roundToDigits(rounder, value, digits),
|
|
824
|
+
computeUnary: (value) => roundToDigits(rounder, value, 0)
|
|
825
|
+
});
|
|
826
|
+
// src/services/function/signature/createNonEmptyCompute.ts
|
|
827
|
+
const createNonEmptyCompute = (compute) => (values) => values.length === 0 ? null : compute(values);
|
|
828
|
+
// src/services/function/signature/createListSignature.ts
|
|
829
|
+
const createListSignature = (compute) => createSignature(["list"], 1, 1, createNumericCompute(compute));
|
|
830
|
+
// src/services/function/signature/createVariadicSignatures.ts
|
|
831
|
+
const createVariadicSignatures = (minArity, compute, computeBinary) => [createNumberSignature(minArity, Number.POSITIVE_INFINITY, compute, { computeBinary })];
|
|
832
|
+
// src/services/function/signature/createSpreadSignatures.ts
|
|
833
|
+
const createSpreadSignatures = (minArity, compute) => [...createVariadicSignatures(minArity, compute), createListSignature(compute)];
|
|
834
|
+
// src/services/function/signature/createExtremeSignatures.ts
|
|
835
|
+
const createExtremeSignatures = (checkIsPreferred) => createSpreadSignatures(1, createNonEmptyCompute((values) => computeExtreme(values, values[0] ?? NaN, checkIsPreferred)));
|
|
836
|
+
// src/services/function/signature/createSingletonListSignature.ts
|
|
837
|
+
const createSingletonListSignature = (compute) => createListSignature((values) => values.length === 1 ? compute(values[0] ?? NaN) : null);
|
|
838
|
+
// src/services/function/signature/createUnarySignatures.ts
|
|
839
|
+
const createUnarySignatures = (compute) => createRangedSignatures(1, 1, (values) => compute(values[0] ?? NaN), { computeUnary: compute });
|
|
840
|
+
// src/services/function/signature/createIdentitySignatures.ts
|
|
841
|
+
const createIdentitySignatures = () => [...createUnarySignatures((value) => value), createSingletonListSignature((value) => value)];
|
|
842
|
+
// src/services/function/signature/createIntegerBinarySignatures.ts
|
|
843
|
+
const createIntegerBinarySignatures = (compute) => createBinarySignatures((left, right) => Number.isInteger(left) && Number.isInteger(right) ? compute(left, right) : null);
|
|
844
|
+
// src/services/function/signature/createIntegerUnarySignatures.ts
|
|
845
|
+
const createIntegerUnarySignatures = (compute) => createUnarySignatures((value) => Number.isInteger(value) ? compute(value) : null);
|
|
846
|
+
// src/services/function/signature/createListFirstCompute.ts
|
|
847
|
+
const createListFirstCompute = (compute) => (values) => compute(values.slice(0, 1).flat(), values.slice(1).flat());
|
|
848
|
+
// src/services/function/signature/createListPairCompute.ts
|
|
849
|
+
const createListPairCompute = (compute) => (values) => compute(values.slice(0, 1).flat(), values.slice(1, 2).flat());
|
|
850
|
+
// src/services/function/signature/createListPairSignature.ts
|
|
851
|
+
const createListPairSignature = (compute) => createSignature(["list", "list"], 2, 2, createListPairCompute(compute));
|
|
852
|
+
// src/services/function/signature/createOrderedListSignatures.ts
|
|
853
|
+
const createOrderedListSignatures = (compute) => {
|
|
854
|
+
const computePair = createListPairCompute(compute);
|
|
855
|
+
return [
|
|
856
|
+
["number", "list"],
|
|
857
|
+
["list", "number"],
|
|
858
|
+
["list", "list"]
|
|
859
|
+
].map((parameters) => createSignature(parameters, 2, 2, computePair));
|
|
860
|
+
};
|
|
861
|
+
// src/services/shared/toNumber.ts
|
|
862
|
+
const toNumber = (flag) => flag ? 1 : 0;
|
|
863
|
+
// src/services/function/MathExpressionFunctionMap.ts
|
|
864
|
+
const MathExpressionFunctionMap = {
|
|
865
|
+
abs: createUnarySignatures(Math.abs),
|
|
866
|
+
acos: createUnarySignatures(Math.acos),
|
|
867
|
+
acosh: createUnarySignatures(Math.acosh),
|
|
868
|
+
acot: createUnarySignatures((value) => Math.atan(1 / value)),
|
|
869
|
+
acoth: createUnarySignatures(computeInverseCoth),
|
|
870
|
+
acsc: createUnarySignatures((value) => Math.asin(1 / value)),
|
|
871
|
+
acsch: createUnarySignatures((value) => Math.asinh(1 / value)),
|
|
872
|
+
add: createVariadicSignatures(2, computeSum, (left, right) => left + right),
|
|
873
|
+
addScalar: createBinarySignatures((left, right) => left + right),
|
|
874
|
+
and: createVariadicSignatures(1, (values) => toNumber(values.every((value) => checkIsTruthy(value)))),
|
|
875
|
+
arg: createUnarySignatures((value) => Number.isNaN(value) ? NaN : value < 0 ? Math.PI : 0),
|
|
876
|
+
asec: createUnarySignatures((value) => Math.acos(1 / value)),
|
|
877
|
+
asech: createUnarySignatures((value) => Math.acosh(1 / value)),
|
|
878
|
+
asin: createUnarySignatures(Math.asin),
|
|
879
|
+
asinh: createUnarySignatures(Math.asinh),
|
|
880
|
+
atan: createUnarySignatures(Math.atan),
|
|
881
|
+
atan2: createBinarySignatures((left, right) => Math.atan2(left, right)),
|
|
882
|
+
atanh: createUnarySignatures(Math.atanh),
|
|
883
|
+
bellNumbers: createUnarySignatures(computeBellNumber),
|
|
884
|
+
bernoulli: createUnarySignatures((value) => Number.isInteger(value) && value >= 0 ? computeBernoulliNumber(value) : null),
|
|
885
|
+
bitAnd: createVariadicSignatures(2, createBitwiseFold(0, computeBitwiseAnd), createBitwisePair(0, computeBitwiseAnd)),
|
|
886
|
+
bitNot: createIntegerUnarySignatures((value) => ~value),
|
|
887
|
+
bitOr: createVariadicSignatures(2, createBitwiseFold(-1, computeBitwiseOr), createBitwisePair(-1, computeBitwiseOr)),
|
|
888
|
+
bitXor: createIntegerBinarySignatures((left, right) => left ^ right),
|
|
889
|
+
boolean: createRangedSignatures(0, 1, (values) => values.length === 0 ? 0 : toNumber(checkIsTruthy(values[0] ?? 0))),
|
|
890
|
+
catalan: createUnarySignatures(computeCatalan),
|
|
891
|
+
cbrt: createUnarySignatures(Math.cbrt),
|
|
892
|
+
ceil: createDigitSignatures(Math.ceil),
|
|
893
|
+
clone: createUnarySignatures((value) => value),
|
|
894
|
+
combinations: createBinarySignatures((available, chosen) => Number.isInteger(available) && Number.isInteger(chosen) && chosen >= 0 && chosen <= available ? computeCombinations(available, chosen) : null),
|
|
895
|
+
combinationsWithRep: createBinarySignatures((available, chosen) => Number.isInteger(available) && Number.isInteger(chosen) && available >= 1 && chosen >= 0 ? computeCombinations(available + chosen - 1, chosen) : null),
|
|
896
|
+
compare: createBinarySignatures(computeComparison),
|
|
897
|
+
compareNatural: [...createBinarySignatures(computeComparison), ...createOrderedListSignatures(computeListComparison)],
|
|
898
|
+
composition: createBinarySignatures(computeCompositions),
|
|
899
|
+
conj: createUnarySignatures((value) => value),
|
|
900
|
+
corr: [createListPairSignature(computeCorrelation)],
|
|
901
|
+
cos: createUnarySignatures(Math.cos),
|
|
902
|
+
cosh: createUnarySignatures(Math.cosh),
|
|
903
|
+
cot: createUnarySignatures((value) => 1 / Math.tan(value)),
|
|
904
|
+
coth: createUnarySignatures((value) => 1 / Math.tanh(value)),
|
|
905
|
+
count: [createListSignature((values) => values.length)],
|
|
906
|
+
csc: createUnarySignatures((value) => 1 / Math.sin(value)),
|
|
907
|
+
csch: createUnarySignatures((value) => 1 / Math.sinh(value)),
|
|
908
|
+
cube: createUnarySignatures((value) => value * value * value),
|
|
909
|
+
deepEqual: [
|
|
910
|
+
...createBinarySignatures((left, right) => toNumber(checkIsNearlyEqual(left, right))),
|
|
911
|
+
createListPairSignature((left, right) => toNumber(left.length === right.length && left.every((value, index) => checkIsNearlyEqual(value, right[index] ?? NaN)))),
|
|
912
|
+
createSignature(["number", "list"], 2, 2, () => toNumber(false)),
|
|
913
|
+
createSignature(["list", "number"], 2, 2, () => toNumber(false))
|
|
914
|
+
],
|
|
915
|
+
det: [...createUnarySignatures((value) => value), createListSignature(computeDeterminant)],
|
|
916
|
+
distance: [createListPairSignature(computeDistance)],
|
|
917
|
+
divide: createBinarySignatures((left, right) => left / right),
|
|
918
|
+
divideScalar: createBinarySignatures((left, right) => left / right),
|
|
919
|
+
dot: [createListPairSignature(computeDotProduct)],
|
|
920
|
+
dotDivide: createBinarySignatures((left, right) => left / right),
|
|
921
|
+
dotMultiply: createBinarySignatures((left, right) => left * right),
|
|
922
|
+
dotPow: createBinarySignatures((left, right) => left ** right),
|
|
923
|
+
equal: createBinarySignatures((left, right) => toNumber(checkIsNearlyEqual(left, right))),
|
|
924
|
+
equalScalar: createBinarySignatures((left, right) => toNumber(checkIsNearlyEqual(left, right))),
|
|
925
|
+
erf: createUnarySignatures(computeErf),
|
|
926
|
+
erfinv: createUnarySignatures(computeErfInverse),
|
|
927
|
+
exp: createUnarySignatures(Math.exp),
|
|
928
|
+
expm1: createUnarySignatures(Math.expm1),
|
|
929
|
+
fix: createDigitSignatures(Math.trunc),
|
|
930
|
+
floor: createDigitSignatures(Math.floor),
|
|
931
|
+
gamma: createUnarySignatures(computeGamma),
|
|
932
|
+
gcd: createSpreadSignatures(2, (values) => values.length < 2 ? null : computeIntegerGreatestCommonDivisor(values)),
|
|
933
|
+
hypot: createSpreadSignatures(1, computeHypotenuse),
|
|
934
|
+
im: createUnarySignatures(() => 0),
|
|
935
|
+
inv: createUnarySignatures((value) => 1 / value),
|
|
936
|
+
invmod: createBinarySignatures(computeModularInverse),
|
|
937
|
+
isBounded: [...createUnarySignatures((value) => toNumber(Number.isFinite(value))), createListSignature((values) => toNumber(values.every((value) => Number.isFinite(value))))],
|
|
938
|
+
isFinite: createUnarySignatures((value) => toNumber(Number.isFinite(value))),
|
|
939
|
+
isInteger: createUnarySignatures((value) => toNumber(Number.isInteger(value))),
|
|
940
|
+
isNaN: createUnarySignatures((value) => toNumber(Number.isNaN(value))),
|
|
941
|
+
isNegative: createUnarySignatures((value) => toNumber(value < 0)),
|
|
942
|
+
isPositive: createUnarySignatures((value) => toNumber(value > 0)),
|
|
943
|
+
isPrime: createUnarySignatures((value) => toNumber(checkIsPrime(value))),
|
|
944
|
+
isZero: createUnarySignatures((value) => toNumber(value === 0)),
|
|
945
|
+
kldivergence: [createListPairSignature(computeKullbackLeibler)],
|
|
946
|
+
larger: createBinarySignatures((left, right) => toNumber(checkIsLarger(left, right))),
|
|
947
|
+
largerEq: createBinarySignatures((left, right) => toNumber(left >= right || checkIsNearlyEqual(left, right))),
|
|
948
|
+
lcm: createVariadicSignatures(2, (values) => {
|
|
949
|
+
const [first = NaN, ...rest] = values;
|
|
950
|
+
return rest.reduce((total, value) => total === null ? null : computeLeastCommonMultiple(total, value), first);
|
|
951
|
+
}),
|
|
952
|
+
leftShift: createIntegerBinarySignatures((left, right) => left << right),
|
|
953
|
+
lgamma: createUnarySignatures(computeLogGamma),
|
|
954
|
+
log: createRangedSignatures(1, 2, (values) => computeLogarithm(values[0] ?? NaN, values[1])),
|
|
955
|
+
log1p: createRangedSignatures(1, 2, (values) => {
|
|
956
|
+
const [value = NaN, base] = values;
|
|
957
|
+
return base === void 0 ? Math.log1p(value) : Math.log1p(value) / Math.log(base);
|
|
958
|
+
}),
|
|
959
|
+
log2: createUnarySignatures(Math.log2),
|
|
960
|
+
log10: createUnarySignatures(Math.log10),
|
|
961
|
+
mad: createSpreadSignatures(1, createNonEmptyCompute(computeMedianAbsoluteDeviation)),
|
|
962
|
+
["ctranspose"]: createUnarySignatures((value) => value),
|
|
963
|
+
["factorial"]: createUnarySignatures(computeFactorial),
|
|
964
|
+
["unaryMinus"]: createUnarySignatures((value) => -value),
|
|
965
|
+
["unaryPlus"]: createUnarySignatures((value) => value),
|
|
966
|
+
max: createExtremeSignatures(checkIsLarger),
|
|
967
|
+
mean: createSpreadSignatures(1, createNonEmptyCompute(computeMean)),
|
|
968
|
+
median: createSpreadSignatures(1, createNonEmptyCompute(computeMedian)),
|
|
969
|
+
min: createExtremeSignatures(checkIsSmaller),
|
|
970
|
+
mod: createBinarySignatures(computeModulo),
|
|
971
|
+
multinomial: [createListSignature(computeMultinomial)],
|
|
972
|
+
multiply: [
|
|
973
|
+
...createVariadicSignatures(2, (values) => values.reduce((total, value) => total * value, 1), (left, right) => left * right),
|
|
974
|
+
createListPairSignature(computeDotProduct),
|
|
975
|
+
createSignature(["any"], 2, Number.POSITIVE_INFINITY, computeProduct)
|
|
976
|
+
],
|
|
977
|
+
multiplyScalar: createBinarySignatures((left, right) => left * right),
|
|
978
|
+
norm: [
|
|
979
|
+
...createRangedSignatures(1, 1, computeNorm),
|
|
980
|
+
createListSignature(computeNorm),
|
|
981
|
+
createSignature(["list", "number"], 2, 2, createListFirstCompute((list, [power = NaN]) => computeVectorNorm(list, power)))
|
|
982
|
+
],
|
|
983
|
+
not: createUnarySignatures((value) => toNumber(!checkIsTruthy(value))),
|
|
984
|
+
nthRoot: createRangedSignatures(1, 2, (values) => computeNthRoot(values[0] ?? NaN, values[1] ?? 2)),
|
|
985
|
+
nullish: createVariadicSignatures(1, (values) => values[0] ?? NaN),
|
|
986
|
+
number: createRangedSignatures(0, 1, (values) => values.length === 0 ? 0 : values[0] ?? NaN),
|
|
987
|
+
numeric: createUnarySignatures((value) => value),
|
|
988
|
+
or: createVariadicSignatures(1, (values) => toNumber(computeTruthyCount(values) > 0)),
|
|
989
|
+
partitionSelect: [createSignature(["list", "number"], 2, 2, createListFirstCompute((list, [index = NaN]) => computePartitionSelect(list, index)))],
|
|
990
|
+
permutations: createRangedSignatures(1, 2, (values) => {
|
|
991
|
+
const [available = NaN, chosen = available] = values;
|
|
992
|
+
return computePermutations(available, chosen);
|
|
993
|
+
}),
|
|
994
|
+
pinv: createUnarySignatures((value) => value === 0 ? 0 : 1 / value),
|
|
995
|
+
pow: createBinarySignatures((left, right) => left ** right),
|
|
996
|
+
prod: createSpreadSignatures(1, createNonEmptyCompute((values) => values.reduce((total, value) => total * value, 1))),
|
|
997
|
+
quantileSeq: [createSignature(["list", "number"], 2, 3, createListFirstCompute((list, [probability = NaN, sorted = 0]) => computeQuantile(list, probability, checkIsTruthy(sorted))))],
|
|
998
|
+
re: createUnarySignatures((value) => value),
|
|
999
|
+
rightArithShift: createIntegerBinarySignatures((left, right) => left >> right),
|
|
1000
|
+
rightLogShift: createIntegerBinarySignatures((left, right) => left >>> right),
|
|
1001
|
+
round: createDigitSignatures((value) => Math.sign(value) * Math.round(Math.abs(value))),
|
|
1002
|
+
sec: createUnarySignatures((value) => 1 / Math.cos(value)),
|
|
1003
|
+
sech: createUnarySignatures((value) => 1 / Math.cosh(value)),
|
|
1004
|
+
setIsSubset: [createListPairSignature((left, right) => toNumber(left.every((value) => right.includes(value))))],
|
|
1005
|
+
setMultiplicity: [createSignature(["number", "list"], 2, 2, createListFirstCompute(([value = NaN], list) => list.filter((element) => element === value).length))],
|
|
1006
|
+
setSize: [createListSignature((values) => values.length)],
|
|
1007
|
+
sign: createUnarySignatures(Math.sign),
|
|
1008
|
+
sin: createUnarySignatures(Math.sin),
|
|
1009
|
+
sinh: createUnarySignatures(Math.sinh),
|
|
1010
|
+
smaller: createBinarySignatures((left, right) => toNumber(checkIsSmaller(left, right))),
|
|
1011
|
+
smallerEq: createBinarySignatures((left, right) => toNumber(left <= right || checkIsNearlyEqual(left, right))),
|
|
1012
|
+
sqrt: createUnarySignatures(Math.sqrt),
|
|
1013
|
+
square: createUnarySignatures((value) => value * value),
|
|
1014
|
+
squeeze: createIdentitySignatures(),
|
|
1015
|
+
std: createSpreadSignatures(1, createNonEmptyCompute((values) => Math.sqrt(computeVariance(values)))),
|
|
1016
|
+
stirlingS2: createBinarySignatures(computeStirlingSecondKind),
|
|
1017
|
+
subtract: createBinarySignatures((left, right) => left - right),
|
|
1018
|
+
subtractScalar: createBinarySignatures((left, right) => left - right),
|
|
1019
|
+
sum: createSpreadSignatures(1, computeSum),
|
|
1020
|
+
tan: createUnarySignatures(Math.tan),
|
|
1021
|
+
tanh: createUnarySignatures(Math.tanh),
|
|
1022
|
+
trace: createIdentitySignatures(),
|
|
1023
|
+
transpose: createIdentitySignatures(),
|
|
1024
|
+
unequal: createBinarySignatures((left, right) => toNumber(!checkIsNearlyEqual(left, right))),
|
|
1025
|
+
variance: createSpreadSignatures(1, createNonEmptyCompute(computeVariance)),
|
|
1026
|
+
xor: createVariadicSignatures(1, (values) => toNumber(computeTruthyCount(values) % 2 === 1), (left, right) => toNumber(checkIsTruthy(left) !== checkIsTruthy(right))),
|
|
1027
|
+
zeta: createUnarySignatures(computeZeta)
|
|
1028
|
+
};
|
|
1029
|
+
// src/services/shared/getOwnValue.ts
|
|
1030
|
+
const getOwnValue = (record, key) => Object.hasOwn(record, key) ? record[key] : void 0;
|
|
1031
|
+
// src/services/shared/getMathExpressionSignatures.ts
|
|
1032
|
+
const getMathExpressionSignatures = (name) => getOwnValue(MathExpressionFunctionMap, name);
|
|
1033
|
+
// src/services/compiler/getMathExpressionScalarCompute.ts
|
|
1034
|
+
const getMathExpressionScalarCompute = (name, key) => {
|
|
1035
|
+
const signature = getMathExpressionSignatures(name)?.[0];
|
|
1036
|
+
if (signature === void 0) return () => null;
|
|
1037
|
+
const scalarCompute = signature[key];
|
|
1038
|
+
if (scalarCompute !== void 0) return scalarCompute;
|
|
1039
|
+
return (...values) => signature.compute(values);
|
|
1040
|
+
};
|
|
1041
|
+
// src/services/compiler/MathExpressionBitwiseAbsorptionMap.ts
|
|
1042
|
+
const MathExpressionBitwiseAbsorptionMap = {
|
|
1043
|
+
["bitAnd"]: {
|
|
1044
|
+
absorbs: 0,
|
|
1045
|
+
result: 0
|
|
1046
|
+
},
|
|
1047
|
+
["bitOr"]: {
|
|
1048
|
+
absorbs: -1,
|
|
1049
|
+
result: -1
|
|
1050
|
+
}
|
|
1051
|
+
};
|
|
1052
|
+
// src/services/compiler/node/compileBinary.ts
|
|
1053
|
+
const compileBinary = (node, context) => {
|
|
1054
|
+
const { left, name, right } = node;
|
|
1055
|
+
const compiledLeft = context.compile(left);
|
|
1056
|
+
if (ADDITIVE_NAMES.includes(name) && right.type === "percentage" && !checkIsPercentageExpression(left)) {
|
|
1057
|
+
const compiledPercent = context.compile(right.operand);
|
|
1058
|
+
const sign = name === "add" ? 1 : -1;
|
|
1059
|
+
const evaluate = (values, locals) => {
|
|
1060
|
+
const base = compiledLeft.evaluate(values, locals);
|
|
1061
|
+
const percent = compiledPercent.evaluate(values, locals);
|
|
1062
|
+
if (base === null || percent === null) return null;
|
|
1063
|
+
return base + sign * base * (percent / 100);
|
|
1064
|
+
};
|
|
1065
|
+
return {
|
|
1066
|
+
evaluate,
|
|
1067
|
+
isConstant: compiledLeft.isConstant && compiledPercent.isConstant
|
|
1068
|
+
};
|
|
1069
|
+
}
|
|
1070
|
+
const compiledRight = context.compile(right);
|
|
1071
|
+
const compute = getMathExpressionScalarCompute(name, "computeBinary");
|
|
1072
|
+
const absorption = MathExpressionBitwiseAbsorptionMap[name];
|
|
1073
|
+
const evaluate = (values, locals) => {
|
|
1074
|
+
const leftValue = compiledLeft.evaluate(values, locals);
|
|
1075
|
+
if (leftValue === null) return null;
|
|
1076
|
+
if (absorption?.absorbs === leftValue) return absorption.result;
|
|
1077
|
+
const rightValue = compiledRight.evaluate(values, locals);
|
|
1078
|
+
if (rightValue === null) return null;
|
|
1079
|
+
return compute(leftValue, rightValue);
|
|
1080
|
+
};
|
|
1081
|
+
return {
|
|
1082
|
+
evaluate,
|
|
1083
|
+
isConstant: compiledLeft.isConstant && compiledRight.isConstant
|
|
1084
|
+
};
|
|
1085
|
+
};
|
|
1086
|
+
// src/services/shared/getMathExpressionParameterType.ts
|
|
1087
|
+
const getMathExpressionParameterType = (node) => node.type === "list" ? "list" : "number";
|
|
1088
|
+
// src/services/shared/checkIsMatchingKind.ts
|
|
1089
|
+
const checkIsMatchingKind = (parameterType, argumentType) => parameterType === "any" || parameterType === argumentType;
|
|
1090
|
+
// src/services/shared/getParameterType.ts
|
|
1091
|
+
const getParameterType = (signature, index) => signature.parameters[Math.min(index, signature.parameters.length - 1)];
|
|
1092
|
+
// src/services/shared/checkIsMatching.ts
|
|
1093
|
+
const checkIsMatching = (signature, parameterTypes) => parameterTypes.length >= signature.minArity && parameterTypes.length <= signature.maxArity && parameterTypes.every((parameterType, index) => checkIsMatchingKind(getParameterType(signature, index), parameterType));
|
|
1094
|
+
// src/services/shared/matchMathExpressionSignature.ts
|
|
1095
|
+
const matchMathExpressionSignature = (signatures, parameterTypes) => signatures.find((signature) => checkIsMatching(signature, parameterTypes));
|
|
1096
|
+
// src/services/compiler/node/compileCall.ts
|
|
1097
|
+
const compileCall = (node, context) => {
|
|
1098
|
+
const signatures = getMathExpressionSignatures(node.name);
|
|
1099
|
+
const parameterTypes = node.arguments.map((argument) => getMathExpressionParameterType(argument));
|
|
1100
|
+
const signature = matchMathExpressionSignature(signatures ?? [], parameterTypes);
|
|
1101
|
+
const compute = signature?.compute ?? (() => null);
|
|
1102
|
+
const computeUnary = signature?.computeUnary;
|
|
1103
|
+
const computeBinary = signature?.computeBinary;
|
|
1104
|
+
const compiledArguments = node.arguments.map((argument) => compileArgument(argument, context));
|
|
1105
|
+
const isConstant = compiledArguments.every((argument) => argument.isConstant);
|
|
1106
|
+
const [first, second] = compiledArguments;
|
|
1107
|
+
if (compiledArguments.length === 1 && first !== void 0) return {
|
|
1108
|
+
evaluate: (values, locals) => {
|
|
1109
|
+
const firstValue = first.evaluate(values, locals);
|
|
1110
|
+
if (firstValue === null) return null;
|
|
1111
|
+
if (computeUnary !== void 0 && typeof firstValue === "number") return computeUnary(firstValue);
|
|
1112
|
+
return compute([firstValue]);
|
|
1113
|
+
},
|
|
1114
|
+
isConstant
|
|
1115
|
+
};
|
|
1116
|
+
if (compiledArguments.length === 2 && first !== void 0 && second !== void 0) return {
|
|
1117
|
+
evaluate: (values, locals) => {
|
|
1118
|
+
const firstValue = first.evaluate(values, locals);
|
|
1119
|
+
if (firstValue === null) return null;
|
|
1120
|
+
const secondValue = second.evaluate(values, locals);
|
|
1121
|
+
if (secondValue === null) return null;
|
|
1122
|
+
if (computeBinary !== void 0 && typeof firstValue === "number" && typeof secondValue === "number") return computeBinary(firstValue, secondValue);
|
|
1123
|
+
return compute([firstValue, secondValue]);
|
|
1124
|
+
},
|
|
1125
|
+
isConstant
|
|
1126
|
+
};
|
|
1127
|
+
return {
|
|
1128
|
+
evaluate: (values, locals) => {
|
|
1129
|
+
const argumentValues = [];
|
|
1130
|
+
for (const argument of compiledArguments) {
|
|
1131
|
+
const argumentValue = argument.evaluate(values, locals);
|
|
1132
|
+
if (argumentValue === null) return null;
|
|
1133
|
+
argumentValues.push(argumentValue);
|
|
1134
|
+
}
|
|
1135
|
+
return compute(argumentValues);
|
|
1136
|
+
},
|
|
1137
|
+
isConstant
|
|
1138
|
+
};
|
|
1139
|
+
};
|
|
1140
|
+
// src/services/compiler/node/compileComparison.ts
|
|
1141
|
+
const compileComparison = (node, context) => {
|
|
1142
|
+
const compiledOperands = node.operands.map((operand) => context.compile(operand));
|
|
1143
|
+
const computes = node.names.map((name) => getMathExpressionScalarCompute(name, "computeBinary"));
|
|
1144
|
+
const evaluate = (values, locals) => {
|
|
1145
|
+
let left = compiledOperands[0]?.evaluate(values, locals) ?? null;
|
|
1146
|
+
let result = 1;
|
|
1147
|
+
for (const [index, compute] of computes.entries()) {
|
|
1148
|
+
const right = compiledOperands[index + 1]?.evaluate(values, locals) ?? null;
|
|
1149
|
+
if (left === null || right === null) return null;
|
|
1150
|
+
result = compute(left, right);
|
|
1151
|
+
if (result !== 1) return result;
|
|
1152
|
+
left = right;
|
|
1153
|
+
}
|
|
1154
|
+
return result;
|
|
1155
|
+
};
|
|
1156
|
+
return {
|
|
1157
|
+
evaluate,
|
|
1158
|
+
isConstant: compiledOperands.every((operand) => operand.isConstant)
|
|
1159
|
+
};
|
|
1160
|
+
};
|
|
1161
|
+
// src/services/compiler/node/compileConditional.ts
|
|
1162
|
+
const compileConditional = (node, context) => {
|
|
1163
|
+
const compiledCondition = context.compile(node.condition);
|
|
1164
|
+
const compiledWhenTrue = context.compile(node.whenTrue);
|
|
1165
|
+
const compiledWhenFalse = context.compile(node.whenFalse);
|
|
1166
|
+
const evaluate = (values, locals) => {
|
|
1167
|
+
const test = compiledCondition.evaluate(values, locals);
|
|
1168
|
+
if (test === null) return null;
|
|
1169
|
+
return checkIsTruthy(test) ? compiledWhenTrue.evaluate(values, locals) : compiledWhenFalse.evaluate(values, locals);
|
|
1170
|
+
};
|
|
1171
|
+
return {
|
|
1172
|
+
evaluate,
|
|
1173
|
+
isConstant: compiledCondition.isConstant && compiledWhenTrue.isConstant && compiledWhenFalse.isConstant
|
|
1174
|
+
};
|
|
1175
|
+
};
|
|
1176
|
+
// src/services/compiler/node/compileGroup.ts
|
|
1177
|
+
const compileGroup = (node, context) => context.compile(node.operand);
|
|
1178
|
+
// src/services/compiler/node/compileListNode.ts
|
|
1179
|
+
const compileListNode = () => ({
|
|
1180
|
+
evaluate: () => null,
|
|
1181
|
+
isConstant: false
|
|
1182
|
+
});
|
|
1183
|
+
// src/services/compiler/node/compileLogical.ts
|
|
1184
|
+
const compileLogical = (node, context) => {
|
|
1185
|
+
const compiledLeft = context.compile(node.left);
|
|
1186
|
+
if (node.operator === "nullish") return compiledLeft;
|
|
1187
|
+
const compiledRight = context.compile(node.right);
|
|
1188
|
+
const isConstant = compiledLeft.isConstant && compiledRight.isConstant;
|
|
1189
|
+
switch (node.operator) {
|
|
1190
|
+
case "and": return {
|
|
1191
|
+
evaluate: (values, locals) => {
|
|
1192
|
+
const leftValue = compiledLeft.evaluate(values, locals);
|
|
1193
|
+
if (leftValue === null) return null;
|
|
1194
|
+
if (!checkIsTruthy(leftValue)) return 0;
|
|
1195
|
+
const rightValue = compiledRight.evaluate(values, locals);
|
|
1196
|
+
return rightValue === null ? null : toNumber(checkIsTruthy(rightValue));
|
|
1197
|
+
},
|
|
1198
|
+
isConstant
|
|
1199
|
+
};
|
|
1200
|
+
case "or": return {
|
|
1201
|
+
evaluate: (values, locals) => {
|
|
1202
|
+
const leftValue = compiledLeft.evaluate(values, locals);
|
|
1203
|
+
if (leftValue === null) return null;
|
|
1204
|
+
if (checkIsTruthy(leftValue)) return 1;
|
|
1205
|
+
const rightValue = compiledRight.evaluate(values, locals);
|
|
1206
|
+
return rightValue === null ? null : toNumber(checkIsTruthy(rightValue));
|
|
1207
|
+
},
|
|
1208
|
+
isConstant
|
|
1209
|
+
};
|
|
1210
|
+
default: return exhaustiveGuard(node.operator);
|
|
1211
|
+
}
|
|
1212
|
+
};
|
|
1213
|
+
// src/services/shared/MathExpressionConstantMap.ts
|
|
1214
|
+
const MathExpressionConstantMap = {
|
|
1215
|
+
E: Math.E,
|
|
1216
|
+
e: Math.E,
|
|
1217
|
+
false: 0,
|
|
1218
|
+
Infinity: Number.POSITIVE_INFINITY,
|
|
1219
|
+
LN2: Math.LN2,
|
|
1220
|
+
LN10: Math.LN10,
|
|
1221
|
+
LOG2E: Math.LOG2E,
|
|
1222
|
+
LOG10E: Math.LOG10E,
|
|
1223
|
+
NaN: NaN,
|
|
1224
|
+
phi: (1 + Math.sqrt(5)) / 2,
|
|
1225
|
+
PI: Math.PI,
|
|
1226
|
+
pi: Math.PI,
|
|
1227
|
+
SQRT1_2: Math.SQRT1_2,
|
|
1228
|
+
SQRT2: Math.SQRT2,
|
|
1229
|
+
tau: 2 * Math.PI,
|
|
1230
|
+
true: 1
|
|
1231
|
+
};
|
|
1232
|
+
// src/services/compiler/getMathExpressionConstant.ts
|
|
1233
|
+
const getMathExpressionConstant = (name) => getOwnValue(MathExpressionConstantMap, name);
|
|
1234
|
+
// src/services/compiler/node/compileName.ts
|
|
1235
|
+
const compileName = (node, context) => {
|
|
1236
|
+
const slot = context.assignedSlots.get(node.name);
|
|
1237
|
+
if (slot !== void 0) return {
|
|
1238
|
+
evaluate: (_values, locals) => locals[slot] ?? null,
|
|
1239
|
+
isConstant: false
|
|
1240
|
+
};
|
|
1241
|
+
const index = context.variableIndices.get(node.name);
|
|
1242
|
+
if (index !== void 0) return {
|
|
1243
|
+
evaluate: (values) => values[index] ?? NaN,
|
|
1244
|
+
isConstant: false
|
|
1245
|
+
};
|
|
1246
|
+
const constant = getMathExpressionConstant(node.name);
|
|
1247
|
+
return {
|
|
1248
|
+
evaluate: () => constant ?? null,
|
|
1249
|
+
isConstant: true
|
|
1250
|
+
};
|
|
1251
|
+
};
|
|
1252
|
+
// src/services/compiler/node/compileNumber.ts
|
|
1253
|
+
const compileNumber = (node) => {
|
|
1254
|
+
const { value } = node;
|
|
1255
|
+
return {
|
|
1256
|
+
evaluate: () => value,
|
|
1257
|
+
isConstant: true
|
|
1258
|
+
};
|
|
1259
|
+
};
|
|
1260
|
+
// src/services/compiler/node/compilePercentage.ts
|
|
1261
|
+
const compilePercentage = (node, context) => {
|
|
1262
|
+
const compiledOperand = context.compile(node.operand);
|
|
1263
|
+
return {
|
|
1264
|
+
evaluate: (values, locals) => {
|
|
1265
|
+
const operandValue = compiledOperand.evaluate(values, locals);
|
|
1266
|
+
return operandValue === null ? null : operandValue / 100;
|
|
1267
|
+
},
|
|
1268
|
+
isConstant: compiledOperand.isConstant
|
|
1269
|
+
};
|
|
1270
|
+
};
|
|
1271
|
+
// src/services/compiler/node/compileUnary.ts
|
|
1272
|
+
const compileUnary = (node, context) => {
|
|
1273
|
+
const compute = getMathExpressionScalarCompute(node.name, "computeUnary");
|
|
1274
|
+
const compiledOperand = context.compile(node.operand);
|
|
1275
|
+
return {
|
|
1276
|
+
evaluate: (values, locals) => {
|
|
1277
|
+
const operandValue = compiledOperand.evaluate(values, locals);
|
|
1278
|
+
return operandValue === null ? null : compute(operandValue);
|
|
1279
|
+
},
|
|
1280
|
+
isConstant: compiledOperand.isConstant
|
|
1281
|
+
};
|
|
1282
|
+
};
|
|
1283
|
+
// src/services/compiler/MathExpressionNodeCompileMap.ts
|
|
1284
|
+
const MathExpressionNodeCompileMap = {
|
|
1285
|
+
["assignment"]: compileAssignment,
|
|
1286
|
+
["binary"]: compileBinary,
|
|
1287
|
+
["call"]: compileCall,
|
|
1288
|
+
["comparison"]: compileComparison,
|
|
1289
|
+
["conditional"]: compileConditional,
|
|
1290
|
+
["group"]: compileGroup,
|
|
1291
|
+
["list"]: compileListNode,
|
|
1292
|
+
["logical"]: compileLogical,
|
|
1293
|
+
["name"]: compileName,
|
|
1294
|
+
["number"]: compileNumber,
|
|
1295
|
+
["percentage"]: compilePercentage,
|
|
1296
|
+
["unary"]: compileUnary
|
|
1297
|
+
};
|
|
1298
|
+
// src/services/compiler/compileMathExpressionNode.ts
|
|
1299
|
+
const compileMathExpressionNode = (node, context) => {
|
|
1300
|
+
const compileOfType = MathExpressionNodeCompileMap[node.type];
|
|
1301
|
+
const compiled = compileOfType(node, context);
|
|
1302
|
+
if (!compiled.isConstant) return compiled;
|
|
1303
|
+
const value = compiled.evaluate(NO_VALUES, NO_LOCALS);
|
|
1304
|
+
return {
|
|
1305
|
+
evaluate: () => value,
|
|
1306
|
+
isConstant: true
|
|
1307
|
+
};
|
|
1308
|
+
};
|
|
1309
|
+
// src/services/compiler/getMathExpressionOuterValue.ts
|
|
1310
|
+
const getMathExpressionOuterValue = (outer, values) => {
|
|
1311
|
+
if (outer.variableIndex !== void 0) return values[outer.variableIndex] ?? NaN;
|
|
1312
|
+
return outer.constant ?? null;
|
|
1313
|
+
};
|
|
1314
|
+
// src/services/compiler/compileMathExpression.ts
|
|
1315
|
+
const compileMathExpression = (node, variableNames) => {
|
|
1316
|
+
const assignedNames = /* @__PURE__ */ new Set();
|
|
1317
|
+
collectMathExpressionAssignedNames(node, assignedNames);
|
|
1318
|
+
const assignedSlots = new Map(Array.from(assignedNames, (name, slot) => [name, slot]));
|
|
1319
|
+
const variableIndices = new Map(variableNames.map((name, index) => [name, index]));
|
|
1320
|
+
const context = {
|
|
1321
|
+
assignedSlots,
|
|
1322
|
+
compile: (child) => compileMathExpressionNode(child, context),
|
|
1323
|
+
variableIndices
|
|
1324
|
+
};
|
|
1325
|
+
const compiled = context.compile(node);
|
|
1326
|
+
const outerBindings = Array.from(assignedNames, (name) => ({
|
|
1327
|
+
constant: getMathExpressionConstant(name),
|
|
1328
|
+
variableIndex: variableIndices.get(name)
|
|
1329
|
+
}));
|
|
1330
|
+
return (values) => {
|
|
1331
|
+
const locals = outerBindings.length === 0 ? NO_LOCALS : outerBindings.map((outer) => getMathExpressionOuterValue(outer, values));
|
|
1332
|
+
return compiled.evaluate(values, locals) ?? NaN;
|
|
1333
|
+
};
|
|
1334
|
+
};
|
|
1335
|
+
// src/services/names/walkNode.ts
|
|
1336
|
+
const walkNode = (node, bound, free) => {
|
|
1337
|
+
switch (node.type) {
|
|
1338
|
+
case "assignment":
|
|
1339
|
+
walkNode(node.value, bound, free);
|
|
1340
|
+
bound.add(node.name);
|
|
1341
|
+
return;
|
|
1342
|
+
case "binary": {
|
|
1343
|
+
walkNode(node.left, bound, free);
|
|
1344
|
+
const rightBound = Object.hasOwn(MathExpressionBitwiseAbsorptionMap, node.name) ? new Set(bound) : bound;
|
|
1345
|
+
walkNode(node.right, rightBound, free);
|
|
1346
|
+
return;
|
|
1347
|
+
}
|
|
1348
|
+
case "call":
|
|
1349
|
+
case "group":
|
|
1350
|
+
case "list":
|
|
1351
|
+
case "number":
|
|
1352
|
+
case "percentage":
|
|
1353
|
+
case "unary":
|
|
1354
|
+
for (const child of getMathExpressionChildren(node)) walkNode(child, bound, free);
|
|
1355
|
+
return;
|
|
1356
|
+
case "comparison": {
|
|
1357
|
+
let chainBound = bound;
|
|
1358
|
+
for (const [index, operand] of node.operands.entries()) {
|
|
1359
|
+
if (index === 2) chainBound = new Set(bound);
|
|
1360
|
+
walkNode(operand, chainBound, free);
|
|
1361
|
+
}
|
|
1362
|
+
return;
|
|
1363
|
+
}
|
|
1364
|
+
case "conditional": {
|
|
1365
|
+
walkNode(node.condition, bound, free);
|
|
1366
|
+
const whenTrueBound = new Set(bound);
|
|
1367
|
+
const whenFalseBound = new Set(bound);
|
|
1368
|
+
walkNode(node.whenTrue, whenTrueBound, free);
|
|
1369
|
+
walkNode(node.whenFalse, whenFalseBound, free);
|
|
1370
|
+
for (const name of whenTrueBound) if (whenFalseBound.has(name)) bound.add(name);
|
|
1371
|
+
return;
|
|
1372
|
+
}
|
|
1373
|
+
case "logical":
|
|
1374
|
+
walkNode(node.left, bound, free);
|
|
1375
|
+
if (node.operator !== "nullish") walkNode(node.right, new Set(bound), free);
|
|
1376
|
+
return;
|
|
1377
|
+
case "name":
|
|
1378
|
+
if (!bound.has(node.name)) free.add(node.name);
|
|
1379
|
+
return;
|
|
1380
|
+
default: return exhaustiveGuard(node);
|
|
1381
|
+
}
|
|
1382
|
+
};
|
|
1383
|
+
// src/services/names/getMathExpressionNames.ts
|
|
1384
|
+
const getMathExpressionNames = (node) => {
|
|
1385
|
+
const bound = /* @__PURE__ */ new Set();
|
|
1386
|
+
const free = /* @__PURE__ */ new Set();
|
|
1387
|
+
walkNode(node, bound, free);
|
|
1388
|
+
return [...free].filter((name) => getMathExpressionConstant(name) === void 0);
|
|
1389
|
+
};
|
|
1390
|
+
// src/services/parser/constants.ts
|
|
1391
|
+
const MAX_MATH_EXPRESSION_DEPTH = 64;
|
|
1392
|
+
const PLACEHOLDER = {
|
|
1393
|
+
type: "number",
|
|
1394
|
+
value: NaN
|
|
1395
|
+
};
|
|
1396
|
+
// src/services/parser/failMathExpressionAt.ts
|
|
1397
|
+
const failMathExpressionAt = (state, message, column) => {
|
|
1398
|
+
if (state.error !== null) return;
|
|
1399
|
+
state.error = new MathExpressionError(`${message} (char ${column})`);
|
|
1400
|
+
};
|
|
1401
|
+
// src/services/parser/getMathExpressionColumn.ts
|
|
1402
|
+
const getMathExpressionColumn = (state) => state.index - state.token.length + 1;
|
|
1403
|
+
// src/services/parser/failMathExpression.ts
|
|
1404
|
+
const failMathExpression = (state, message) => {
|
|
1405
|
+
failMathExpressionAt(state, message, getMathExpressionColumn(state));
|
|
1406
|
+
};
|
|
1407
|
+
// src/services/parser/checkCanMathExpressionNest.ts
|
|
1408
|
+
const checkCanMathExpressionNest = (state) => {
|
|
1409
|
+
if (state.depth < 64) return true;
|
|
1410
|
+
failMathExpression(state, `Formula nests deeper than 64 levels`);
|
|
1411
|
+
return false;
|
|
1412
|
+
};
|
|
1413
|
+
// src/services/parser/checkIsNameToken.ts
|
|
1414
|
+
const checkIsNameToken = (state) => state.tokenType === "symbol" || state.tokenType === "delimiter" && getMathExpressionSignatures(state.token) !== void 0;
|
|
1415
|
+
// src/services/parser/checkIsOperatorOperand.ts
|
|
1416
|
+
const checkIsOperatorOperand = (node) => {
|
|
1417
|
+
if (node.type === "unary") return node.name !== "factorial";
|
|
1418
|
+
return node.type === "binary" || node.type === "comparison" || node.type === "logical";
|
|
1419
|
+
};
|
|
1420
|
+
// src/services/parser/checkIsSignedNumber.ts
|
|
1421
|
+
const checkIsSignedNumber = (node) => {
|
|
1422
|
+
if (node.type === "number") return true;
|
|
1423
|
+
return node.type === "unary" && (node.name === "unaryMinus" || node.name === "unaryPlus") && node.operand.type === "number";
|
|
1424
|
+
};
|
|
1425
|
+
// src/services/parser/checkIsToken.ts
|
|
1426
|
+
const checkIsToken = (state, token) => state.token === token;
|
|
1427
|
+
// src/services/parser/MathExpressionOperatorMap.ts
|
|
1428
|
+
const MathExpressionOperatorMap = {
|
|
1429
|
+
["Nullish"]: { "??": "nullish" },
|
|
1430
|
+
["Power"]: {
|
|
1431
|
+
".^": "dotPow",
|
|
1432
|
+
"^": "pow"
|
|
1433
|
+
},
|
|
1434
|
+
["Unary"]: {
|
|
1435
|
+
"+": "unaryPlus",
|
|
1436
|
+
"-": "unaryMinus",
|
|
1437
|
+
not: "not",
|
|
1438
|
+
"~": "bitNot"
|
|
1439
|
+
}
|
|
1440
|
+
};
|
|
1441
|
+
// src/services/parser/checkIsUnaryStart.ts
|
|
1442
|
+
const checkIsUnaryStart = (state) => state.tokenType === "number" || checkIsNameToken(state) || checkIsToken(state, "(") || getOwnValue(MathExpressionOperatorMap["Unary"], state.token) !== void 0;
|
|
1443
|
+
// src/services/parser/token/MathExpressionRadixMap.ts
|
|
1444
|
+
const MathExpressionRadixMap = {
|
|
1445
|
+
["b"]: {
|
|
1446
|
+
digit: /^[01]$/u,
|
|
1447
|
+
radix: 2
|
|
1448
|
+
},
|
|
1449
|
+
["x"]: {
|
|
1450
|
+
digit: /^[0-9a-fA-F]$/u,
|
|
1451
|
+
radix: 16
|
|
1452
|
+
},
|
|
1453
|
+
["o"]: {
|
|
1454
|
+
digit: /^[0-7]$/u,
|
|
1455
|
+
radix: 8
|
|
1456
|
+
}
|
|
1457
|
+
};
|
|
1458
|
+
// src/services/parser/computeNumberLiteral.ts
|
|
1459
|
+
const computeNumberLiteral = (token) => {
|
|
1460
|
+
const radixEntry = MathExpressionRadixMap[token.charAt(1)];
|
|
1461
|
+
if (!token.startsWith("0") || radixEntry === void 0) return Number(token);
|
|
1462
|
+
const { radix } = radixEntry;
|
|
1463
|
+
const [digits = "", wordSize] = token.slice(2).split("i");
|
|
1464
|
+
const [integerDigits = "", fractionDigits = ""] = digits.split(".");
|
|
1465
|
+
let value = 0;
|
|
1466
|
+
for (const digit of integerDigits) value = value * radix + Number.parseInt(digit, radix);
|
|
1467
|
+
let scale = 1 / radix;
|
|
1468
|
+
for (const digit of fractionDigits) {
|
|
1469
|
+
value += Number.parseInt(digit, radix) * scale;
|
|
1470
|
+
scale /= radix;
|
|
1471
|
+
}
|
|
1472
|
+
if (wordSize === void 0) return value;
|
|
1473
|
+
const bits = Number(wordSize);
|
|
1474
|
+
if (value > 2 ** bits - 1) return null;
|
|
1475
|
+
return value >= 2 ** (bits - 1) ? value - 2 ** bits : value;
|
|
1476
|
+
};
|
|
1477
|
+
// src/services/parser/createBinaryNode.ts
|
|
1478
|
+
const createBinaryNode = (left, name, right) => ({
|
|
1479
|
+
left,
|
|
1480
|
+
name,
|
|
1481
|
+
right,
|
|
1482
|
+
type: "binary"
|
|
1483
|
+
});
|
|
1484
|
+
// src/services/parser/createLogicalNode.ts
|
|
1485
|
+
const createLogicalNode = (left, operator, right) => ({
|
|
1486
|
+
left,
|
|
1487
|
+
operator,
|
|
1488
|
+
right,
|
|
1489
|
+
type: "logical"
|
|
1490
|
+
});
|
|
1491
|
+
// src/services/parser/createBinaryOperation.ts
|
|
1492
|
+
const createBinaryOperation = (left, operator, right) => {
|
|
1493
|
+
if (operator.type === "logical") return createLogicalNode(left, operator.operator, right);
|
|
1494
|
+
if (operator.type === "binary") return createBinaryNode(left, operator.name, right);
|
|
1495
|
+
if (left.type === "comparison") {
|
|
1496
|
+
left.names.push(operator.name);
|
|
1497
|
+
left.operands.push(right);
|
|
1498
|
+
return left;
|
|
1499
|
+
}
|
|
1500
|
+
return {
|
|
1501
|
+
names: [operator.name],
|
|
1502
|
+
operands: [left, right],
|
|
1503
|
+
type: "comparison"
|
|
1504
|
+
};
|
|
1505
|
+
};
|
|
1506
|
+
// src/services/parser/getArgumentCountText.ts
|
|
1507
|
+
const getArgumentCountText = (count) => `${count} ${count === 1 ? "argument" : "arguments"}`;
|
|
1508
|
+
// src/services/parser/getMathExpressionArityMessage.ts
|
|
1509
|
+
const getMathExpressionArityMessage = (name, minArity, maxArity) => {
|
|
1510
|
+
if (maxArity === Number.POSITIVE_INFINITY) return `"${name}" takes at least ${getArgumentCountText(minArity)}`;
|
|
1511
|
+
if (minArity === maxArity) return `"${name}" takes ${getArgumentCountText(minArity)}`;
|
|
1512
|
+
return `"${name}" takes ${minArity} or ${maxArity} arguments`;
|
|
1513
|
+
};
|
|
1514
|
+
// src/services/parser/getMathExpressionSignatureError.ts
|
|
1515
|
+
const getMathExpressionSignatureError = (name, signatures, parameterTypes) => {
|
|
1516
|
+
const count = parameterTypes.length;
|
|
1517
|
+
if (signatures.filter((signature) => count >= signature.minArity && count <= signature.maxArity).length === 0) return getMathExpressionArityMessage(name, Math.min(...signatures.map((signature) => signature.minArity)), Math.max(...signatures.map((signature) => signature.maxArity)));
|
|
1518
|
+
if (parameterTypes.includes("list")) return `"${name}" does not take a list argument`;
|
|
1519
|
+
return `"${name}" takes a list argument`;
|
|
1520
|
+
};
|
|
1521
|
+
// src/services/parser/token/checkIsDigitCharacter.ts
|
|
1522
|
+
const checkIsDigitCharacter = (character) => character >= "0" && character <= "9";
|
|
1523
|
+
// src/services/parser/token/constants.ts
|
|
1524
|
+
const SYMBOLIC_BINARY_SYMBOLS = Object.values(MathExpressionBinaryOperatorSymbol).filter((symbol) => !/\p{L}/u.test(symbol));
|
|
1525
|
+
const SYMBOLIC_BINARY_SYMBOLS_OF_THREE = SYMBOLIC_BINARY_SYMBOLS.filter((symbol) => symbol.length === 3);
|
|
1526
|
+
const SYMBOLIC_BINARY_SYMBOLS_OF_TWO = SYMBOLIC_BINARY_SYMBOLS.filter((symbol) => symbol.length === 2);
|
|
1527
|
+
const SYMBOLIC_BINARY_SYMBOLS_OF_ONE = SYMBOLIC_BINARY_SYMBOLS.filter((symbol) => symbol.length === 1);
|
|
1528
|
+
const DELIMITERS_OF_THREE = new Set(SYMBOLIC_BINARY_SYMBOLS_OF_THREE);
|
|
1529
|
+
const DELIMITERS_OF_TWO = /* @__PURE__ */ new Set([
|
|
1530
|
+
...SYMBOLIC_BINARY_SYMBOLS_OF_TWO,
|
|
1531
|
+
".^",
|
|
1532
|
+
"??"
|
|
1533
|
+
]);
|
|
1534
|
+
const DELIMITERS_OF_ONE = /* @__PURE__ */ new Set([
|
|
1535
|
+
...SYMBOLIC_BINARY_SYMBOLS_OF_ONE,
|
|
1536
|
+
"!",
|
|
1537
|
+
"'",
|
|
1538
|
+
"(",
|
|
1539
|
+
")",
|
|
1540
|
+
",",
|
|
1541
|
+
":",
|
|
1542
|
+
"=",
|
|
1543
|
+
"?",
|
|
1544
|
+
"[",
|
|
1545
|
+
"]",
|
|
1546
|
+
"^",
|
|
1547
|
+
"~"
|
|
1548
|
+
]);
|
|
1549
|
+
const FIRST_CHARACTERS_OF_THREE = new Set(Array.from(DELIMITERS_OF_THREE, (delimiter) => delimiter.charAt(0)));
|
|
1550
|
+
const FIRST_CHARACTERS_OF_TWO = new Set(Array.from(DELIMITERS_OF_TWO, (delimiter) => delimiter.charAt(0)));
|
|
1551
|
+
const NAMED_DELIMITERS = /* @__PURE__ */ new Set([
|
|
1552
|
+
"in",
|
|
1553
|
+
"and",
|
|
1554
|
+
"mod",
|
|
1555
|
+
"or",
|
|
1556
|
+
"xor",
|
|
1557
|
+
"not",
|
|
1558
|
+
"to"
|
|
1559
|
+
]);
|
|
1560
|
+
const DECIMAL_MARK_OPERATOR_FOLLOWERS = /* @__PURE__ */ new Set([
|
|
1561
|
+
"^",
|
|
1562
|
+
"/",
|
|
1563
|
+
"*"
|
|
1564
|
+
]);
|
|
1565
|
+
const WORD_START_REGEX = /^[\p{L}_$]$/u;
|
|
1566
|
+
const WORD_CHARACTER_REGEX = /^[\p{L}\p{Nd}_$]$/u;
|
|
1567
|
+
// src/services/parser/token/checkIsWordStartCharacter.ts
|
|
1568
|
+
const checkIsWordStartCharacter = (character) => {
|
|
1569
|
+
if (character >= "") return WORD_START_REGEX.test(character);
|
|
1570
|
+
return character >= "a" && character <= "z" || character >= "A" && character <= "Z" || character === "_" || character === "$";
|
|
1571
|
+
};
|
|
1572
|
+
// src/services/parser/token/getMathExpressionCharacter.ts
|
|
1573
|
+
const getMathExpressionCharacter = (state, offset) => state.source.charAt(state.index + offset);
|
|
1574
|
+
// src/services/parser/token/readMathExpressionDelimiter.ts
|
|
1575
|
+
const readMathExpressionDelimiter = (state) => {
|
|
1576
|
+
const character = getMathExpressionCharacter(state, 0);
|
|
1577
|
+
let length = 0;
|
|
1578
|
+
if (FIRST_CHARACTERS_OF_THREE.has(character) && DELIMITERS_OF_THREE.has(state.source.slice(state.index, state.index + 3))) length = 3;
|
|
1579
|
+
else if (FIRST_CHARACTERS_OF_TWO.has(character) && DELIMITERS_OF_TWO.has(state.source.slice(state.index, state.index + 2))) length = 2;
|
|
1580
|
+
else if (DELIMITERS_OF_ONE.has(character)) length = 1;
|
|
1581
|
+
if (length === 0) return 0;
|
|
1582
|
+
state.token = state.source.slice(state.index, state.index + length);
|
|
1583
|
+
state.index += length;
|
|
1584
|
+
state.tokenType = "delimiter";
|
|
1585
|
+
if (["(", "["].includes(state.token)) state.nestingLevel++;
|
|
1586
|
+
if ([")", "]"].includes(state.token)) state.nestingLevel--;
|
|
1587
|
+
return length;
|
|
1588
|
+
};
|
|
1589
|
+
// src/services/parser/token/takeMathExpressionMatching.ts
|
|
1590
|
+
const takeMathExpressionMatching = (state, checkCharacter) => {
|
|
1591
|
+
const start = state.index;
|
|
1592
|
+
while (checkCharacter(getMathExpressionCharacter(state, 0))) state.index++;
|
|
1593
|
+
return state.source.slice(start, state.index);
|
|
1594
|
+
};
|
|
1595
|
+
// src/services/parser/token/readRadixNumber.ts
|
|
1596
|
+
const readRadixNumber = (state, prefix, baseDigits) => {
|
|
1597
|
+
const checkBaseDigit = (character) => baseDigits.test(character);
|
|
1598
|
+
state.index += 2;
|
|
1599
|
+
let digits = takeMathExpressionMatching(state, checkBaseDigit);
|
|
1600
|
+
if (digits === "") {
|
|
1601
|
+
state.index--;
|
|
1602
|
+
state.token = "0";
|
|
1603
|
+
state.tokenType = "number";
|
|
1604
|
+
return;
|
|
1605
|
+
}
|
|
1606
|
+
if (getMathExpressionCharacter(state, 0) === ".") {
|
|
1607
|
+
state.index++;
|
|
1608
|
+
digits += `.${takeMathExpressionMatching(state, checkBaseDigit)}`;
|
|
1609
|
+
}
|
|
1610
|
+
let wordSize = "";
|
|
1611
|
+
if (getMathExpressionCharacter(state, 0) === "i" && checkIsDigitCharacter(getMathExpressionCharacter(state, 1))) {
|
|
1612
|
+
state.index++;
|
|
1613
|
+
wordSize = `i${takeMathExpressionMatching(state, checkIsDigitCharacter)}`;
|
|
1614
|
+
}
|
|
1615
|
+
state.token = `0${prefix}${digits}${wordSize}`;
|
|
1616
|
+
state.tokenType = "number";
|
|
1617
|
+
};
|
|
1618
|
+
// src/services/parser/token/checkIsDecimalMark.ts
|
|
1619
|
+
const checkIsDecimalMark = (character, nextCharacter) => character === "." && !DECIMAL_MARK_OPERATOR_FOLLOWERS.has(nextCharacter);
|
|
1620
|
+
// src/services/parser/token/failExponent.ts
|
|
1621
|
+
const failExponent = (state, text) => {
|
|
1622
|
+
state.token = text;
|
|
1623
|
+
failMathExpression(state, `Digit expected, got "${getMathExpressionCharacter(state, 0)}"`);
|
|
1624
|
+
};
|
|
1625
|
+
// src/services/parser/token/takeExponent.ts
|
|
1626
|
+
const takeExponent = (state, text) => {
|
|
1627
|
+
const exponentMarker = getMathExpressionCharacter(state, 0);
|
|
1628
|
+
if (!["e", "E"].includes(exponentMarker)) return text;
|
|
1629
|
+
const afterMarker = getMathExpressionCharacter(state, 1);
|
|
1630
|
+
if (checkIsDecimalMark(afterMarker, getMathExpressionCharacter(state, 2))) {
|
|
1631
|
+
state.index++;
|
|
1632
|
+
failExponent(state, text);
|
|
1633
|
+
return text;
|
|
1634
|
+
}
|
|
1635
|
+
if (!checkIsDigitCharacter(afterMarker) && !["+", "-"].includes(afterMarker)) return text;
|
|
1636
|
+
state.index++;
|
|
1637
|
+
let exponent = "e";
|
|
1638
|
+
if (["+", "-"].includes(afterMarker)) {
|
|
1639
|
+
exponent += afterMarker;
|
|
1640
|
+
state.index++;
|
|
1641
|
+
}
|
|
1642
|
+
if (!checkIsDigitCharacter(getMathExpressionCharacter(state, 0))) {
|
|
1643
|
+
const partial = text + exponent;
|
|
1644
|
+
failExponent(state, partial);
|
|
1645
|
+
return partial;
|
|
1646
|
+
}
|
|
1647
|
+
const taken = text + exponent + takeMathExpressionMatching(state, checkIsDigitCharacter);
|
|
1648
|
+
if (checkIsDecimalMark(getMathExpressionCharacter(state, 0), getMathExpressionCharacter(state, 1))) failExponent(state, taken);
|
|
1649
|
+
return taken;
|
|
1650
|
+
};
|
|
1651
|
+
// src/services/parser/token/readMathExpressionNumber.ts
|
|
1652
|
+
const readMathExpressionNumber = (state) => {
|
|
1653
|
+
const prefix = getMathExpressionCharacter(state, 1);
|
|
1654
|
+
const radixEntry = MathExpressionRadixMap[prefix];
|
|
1655
|
+
if (getMathExpressionCharacter(state, 0) === "0" && radixEntry !== void 0) {
|
|
1656
|
+
readRadixNumber(state, prefix, radixEntry.digit);
|
|
1657
|
+
return;
|
|
1658
|
+
}
|
|
1659
|
+
const start = state.index;
|
|
1660
|
+
takeMathExpressionMatching(state, checkIsDigitCharacter);
|
|
1661
|
+
if (getMathExpressionCharacter(state, 0) === ".") {
|
|
1662
|
+
state.index++;
|
|
1663
|
+
takeMathExpressionMatching(state, checkIsDigitCharacter);
|
|
1664
|
+
}
|
|
1665
|
+
state.token = takeExponent(state, state.source.slice(start, state.index));
|
|
1666
|
+
state.tokenType = "number";
|
|
1667
|
+
};
|
|
1668
|
+
// src/services/parser/token/checkIsWordCharacter.ts
|
|
1669
|
+
const checkIsWordCharacter = (character) => {
|
|
1670
|
+
if (character >= "") return WORD_CHARACTER_REGEX.test(character);
|
|
1671
|
+
return checkIsWordStartCharacter(character) || checkIsDigitCharacter(character);
|
|
1672
|
+
};
|
|
1673
|
+
// src/services/parser/token/readMathExpressionWord.ts
|
|
1674
|
+
const readMathExpressionWord = (state) => {
|
|
1675
|
+
state.token = takeMathExpressionMatching(state, checkIsWordCharacter);
|
|
1676
|
+
state.tokenType = NAMED_DELIMITERS.has(state.token) ? "delimiter" : "symbol";
|
|
1677
|
+
};
|
|
1678
|
+
// src/services/parser/token/skipMathExpressionSpaces.ts
|
|
1679
|
+
const skipMathExpressionSpaces = (state) => {
|
|
1680
|
+
while (state.index < state.source.length) {
|
|
1681
|
+
const character = getMathExpressionCharacter(state, 0);
|
|
1682
|
+
if ([
|
|
1683
|
+
" ",
|
|
1684
|
+
" ",
|
|
1685
|
+
"\xA0"
|
|
1686
|
+
].includes(character) || character === "\n" && state.nestingLevel > 0) state.index++;
|
|
1687
|
+
else if (character === "#") while (state.index < state.source.length && getMathExpressionCharacter(state, 0) !== "\n") state.index++;
|
|
1688
|
+
else break;
|
|
1689
|
+
}
|
|
1690
|
+
};
|
|
1691
|
+
// src/services/parser/getMathExpressionToken.ts
|
|
1692
|
+
const getMathExpressionToken = (state) => {
|
|
1693
|
+
state.token = "";
|
|
1694
|
+
state.tokenType = "null";
|
|
1695
|
+
skipMathExpressionSpaces(state);
|
|
1696
|
+
if (state.index >= state.source.length || state.error !== null) return;
|
|
1697
|
+
const character = getMathExpressionCharacter(state, 0);
|
|
1698
|
+
const next = getMathExpressionCharacter(state, 1);
|
|
1699
|
+
if (checkIsDigitCharacter(character) || character === "." && checkIsDigitCharacter(next)) readMathExpressionNumber(state);
|
|
1700
|
+
else if (checkIsWordStartCharacter(character)) readMathExpressionWord(state);
|
|
1701
|
+
else if (readMathExpressionDelimiter(state) === 0) {
|
|
1702
|
+
state.token = character;
|
|
1703
|
+
state.index++;
|
|
1704
|
+
state.tokenType = "unknown";
|
|
1705
|
+
}
|
|
1706
|
+
};
|
|
1707
|
+
// src/services/parser/MathExpressionBinaryOperatorMap.ts
|
|
1708
|
+
const MathExpressionBinaryOperatorMap = {
|
|
1709
|
+
["+"]: {
|
|
1710
|
+
name: "add",
|
|
1711
|
+
power: 9,
|
|
1712
|
+
type: "binary"
|
|
1713
|
+
},
|
|
1714
|
+
["and"]: {
|
|
1715
|
+
operator: "and",
|
|
1716
|
+
power: 3,
|
|
1717
|
+
type: "logical"
|
|
1718
|
+
},
|
|
1719
|
+
["&"]: {
|
|
1720
|
+
name: "bitAnd",
|
|
1721
|
+
power: 6,
|
|
1722
|
+
type: "binary"
|
|
1723
|
+
},
|
|
1724
|
+
["|"]: {
|
|
1725
|
+
name: "bitOr",
|
|
1726
|
+
power: 4,
|
|
1727
|
+
type: "binary"
|
|
1728
|
+
},
|
|
1729
|
+
["^|"]: {
|
|
1730
|
+
name: "bitXor",
|
|
1731
|
+
power: 5,
|
|
1732
|
+
type: "binary"
|
|
1733
|
+
},
|
|
1734
|
+
["/"]: {
|
|
1735
|
+
name: "divide",
|
|
1736
|
+
power: 10,
|
|
1737
|
+
type: "binary"
|
|
1738
|
+
},
|
|
1739
|
+
["./"]: {
|
|
1740
|
+
name: "dotDivide",
|
|
1741
|
+
power: 10,
|
|
1742
|
+
type: "binary"
|
|
1743
|
+
},
|
|
1744
|
+
[".*"]: {
|
|
1745
|
+
name: "dotMultiply",
|
|
1746
|
+
power: 10,
|
|
1747
|
+
type: "binary"
|
|
1748
|
+
},
|
|
1749
|
+
["=="]: {
|
|
1750
|
+
name: "equal",
|
|
1751
|
+
power: 7,
|
|
1752
|
+
type: "comparison"
|
|
1753
|
+
},
|
|
1754
|
+
[">"]: {
|
|
1755
|
+
name: "larger",
|
|
1756
|
+
power: 7,
|
|
1757
|
+
type: "comparison"
|
|
1758
|
+
},
|
|
1759
|
+
[">="]: {
|
|
1760
|
+
name: "largerEq",
|
|
1761
|
+
power: 7,
|
|
1762
|
+
type: "comparison"
|
|
1763
|
+
},
|
|
1764
|
+
["<"]: {
|
|
1765
|
+
name: "smaller",
|
|
1766
|
+
power: 7,
|
|
1767
|
+
type: "comparison"
|
|
1768
|
+
},
|
|
1769
|
+
["<="]: {
|
|
1770
|
+
name: "smallerEq",
|
|
1771
|
+
power: 7,
|
|
1772
|
+
type: "comparison"
|
|
1773
|
+
},
|
|
1774
|
+
["mod"]: {
|
|
1775
|
+
name: "mod",
|
|
1776
|
+
power: 10,
|
|
1777
|
+
type: "binary"
|
|
1778
|
+
},
|
|
1779
|
+
["%"]: {
|
|
1780
|
+
name: "mod",
|
|
1781
|
+
power: 10,
|
|
1782
|
+
type: "binary"
|
|
1783
|
+
},
|
|
1784
|
+
["*"]: {
|
|
1785
|
+
name: "multiply",
|
|
1786
|
+
power: 10,
|
|
1787
|
+
type: "binary"
|
|
1788
|
+
},
|
|
1789
|
+
["!="]: {
|
|
1790
|
+
name: "unequal",
|
|
1791
|
+
power: 7,
|
|
1792
|
+
type: "comparison"
|
|
1793
|
+
},
|
|
1794
|
+
["or"]: {
|
|
1795
|
+
operator: "or",
|
|
1796
|
+
power: 1,
|
|
1797
|
+
type: "logical"
|
|
1798
|
+
},
|
|
1799
|
+
["<<"]: {
|
|
1800
|
+
name: "leftShift",
|
|
1801
|
+
power: 8,
|
|
1802
|
+
type: "binary"
|
|
1803
|
+
},
|
|
1804
|
+
[">>"]: {
|
|
1805
|
+
name: "rightArithShift",
|
|
1806
|
+
power: 8,
|
|
1807
|
+
type: "binary"
|
|
1808
|
+
},
|
|
1809
|
+
["-"]: {
|
|
1810
|
+
name: "subtract",
|
|
1811
|
+
power: 9,
|
|
1812
|
+
type: "binary"
|
|
1813
|
+
},
|
|
1814
|
+
[">>>"]: {
|
|
1815
|
+
name: "rightLogShift",
|
|
1816
|
+
power: 8,
|
|
1817
|
+
type: "binary"
|
|
1818
|
+
},
|
|
1819
|
+
["xor"]: {
|
|
1820
|
+
name: "xor",
|
|
1821
|
+
power: 2,
|
|
1822
|
+
type: "binary"
|
|
1823
|
+
}
|
|
1824
|
+
};
|
|
1825
|
+
// src/services/parser/rule/parseAssignment.ts
|
|
1826
|
+
const parseAssignment = (state, parse) => {
|
|
1827
|
+
if (!checkCanMathExpressionNest(state)) return PLACEHOLDER;
|
|
1828
|
+
state.depth++;
|
|
1829
|
+
const left = parse(state, "Conditional");
|
|
1830
|
+
let node = left;
|
|
1831
|
+
if (state.error === null && checkIsToken(state, "=")) if (left.type === "name") {
|
|
1832
|
+
getMathExpressionToken(state);
|
|
1833
|
+
const value = parse(state, "Assignment");
|
|
1834
|
+
node = {
|
|
1835
|
+
name: left.name,
|
|
1836
|
+
type: "assignment",
|
|
1837
|
+
value
|
|
1838
|
+
};
|
|
1839
|
+
} else failMathExpression(state, "Invalid left hand side of assignment operator =");
|
|
1840
|
+
state.depth--;
|
|
1841
|
+
return node;
|
|
1842
|
+
};
|
|
1843
|
+
// src/services/parser/parseBinaryOperators.ts
|
|
1844
|
+
const parseBinaryOperators = (state, parse, minimumPower) => {
|
|
1845
|
+
let node = parse(state, "ImplicitProduct");
|
|
1846
|
+
let operator = getOwnValue(MathExpressionBinaryOperatorMap, state.token);
|
|
1847
|
+
while (state.error === null && operator !== void 0 && operator.power >= minimumPower) {
|
|
1848
|
+
getMathExpressionToken(state);
|
|
1849
|
+
const right = parseBinaryOperators(state, parse, operator.power + 1);
|
|
1850
|
+
node = createBinaryOperation(node, operator, right);
|
|
1851
|
+
operator = getOwnValue(MathExpressionBinaryOperatorMap, state.token);
|
|
1852
|
+
}
|
|
1853
|
+
return node;
|
|
1854
|
+
};
|
|
1855
|
+
// src/services/parser/rule/parseBinary.ts
|
|
1856
|
+
const parseBinary = (state, parse) => parseBinaryOperators(state, parse, 0);
|
|
1857
|
+
// src/services/parser/rule/parseConditional.ts
|
|
1858
|
+
const parseConditional = (state, parse) => {
|
|
1859
|
+
const condition = parse(state, "Binary");
|
|
1860
|
+
if (state.error !== null || !checkIsToken(state, "?")) return condition;
|
|
1861
|
+
getMathExpressionToken(state);
|
|
1862
|
+
if (checkIsToken(state, ":")) {
|
|
1863
|
+
failMathExpression(state, "The true-expression of a conditional operator may not be empty");
|
|
1864
|
+
return PLACEHOLDER;
|
|
1865
|
+
}
|
|
1866
|
+
const whenTrue = parse(state, "Assignment");
|
|
1867
|
+
if (state.error === null && !checkIsToken(state, ":")) failMathExpression(state, "False part of conditional expression expected");
|
|
1868
|
+
if (state.error !== null) return PLACEHOLDER;
|
|
1869
|
+
getMathExpressionToken(state);
|
|
1870
|
+
state.depth++;
|
|
1871
|
+
const whenFalse = parse(state, "Conditional");
|
|
1872
|
+
state.depth--;
|
|
1873
|
+
return {
|
|
1874
|
+
condition,
|
|
1875
|
+
type: "conditional",
|
|
1876
|
+
whenFalse,
|
|
1877
|
+
whenTrue
|
|
1878
|
+
};
|
|
1879
|
+
};
|
|
1880
|
+
// src/services/parser/rule/parseDivisionBeforeProduct.ts
|
|
1881
|
+
const parseDivisionBeforeProduct = (state, parse) => {
|
|
1882
|
+
const dividend = parse(state, "Percentage");
|
|
1883
|
+
if (state.error !== null || !checkIsToken(state, "/") || !checkIsSignedNumber(dividend)) return dividend;
|
|
1884
|
+
const snapshot = { ...state };
|
|
1885
|
+
getMathExpressionToken(state);
|
|
1886
|
+
const isNumber = state.tokenType === "number";
|
|
1887
|
+
if (isNumber) getMathExpressionToken(state);
|
|
1888
|
+
if (isNumber && state.error === null && (state.tokenType === "symbol" || checkIsToken(state, "("))) {
|
|
1889
|
+
Object.assign(state, snapshot);
|
|
1890
|
+
getMathExpressionToken(state);
|
|
1891
|
+
return createBinaryNode(dividend, MathExpressionBinaryOperatorMap["/"].name, parse(state, "Percentage"));
|
|
1892
|
+
}
|
|
1893
|
+
Object.assign(state, snapshot);
|
|
1894
|
+
return dividend;
|
|
1895
|
+
};
|
|
1896
|
+
// src/services/parser/rule/parseImplicitProduct.ts
|
|
1897
|
+
const parseImplicitProduct = (state, parse) => {
|
|
1898
|
+
let node = parse(state, "DivisionBeforeProduct");
|
|
1899
|
+
let last = node;
|
|
1900
|
+
while (state.error === null) {
|
|
1901
|
+
if (!(state.tokenType === "symbol" || checkIsToken(state, "(") || state.tokenType === "number" && last.type !== "number" && !checkIsOperatorOperand(last))) break;
|
|
1902
|
+
last = parse(state, "DivisionBeforeProduct");
|
|
1903
|
+
node = createBinaryNode(node, MathExpressionBinaryOperatorMap["*"].name, last);
|
|
1904
|
+
}
|
|
1905
|
+
return node;
|
|
1906
|
+
};
|
|
1907
|
+
// src/services/parser/rule/parseLeftHand.ts
|
|
1908
|
+
const parseLeftHand = (state, parse) => {
|
|
1909
|
+
let node = parse(state, "Symbol");
|
|
1910
|
+
while (state.error === null && (checkIsToken(state, "!") || checkIsToken(state, "'"))) {
|
|
1911
|
+
const name = checkIsToken(state, "!") ? "factorial" : "ctranspose";
|
|
1912
|
+
getMathExpressionToken(state);
|
|
1913
|
+
node = {
|
|
1914
|
+
name,
|
|
1915
|
+
operand: node,
|
|
1916
|
+
type: "unary"
|
|
1917
|
+
};
|
|
1918
|
+
}
|
|
1919
|
+
return node;
|
|
1920
|
+
};
|
|
1921
|
+
// src/services/parser/rule/parseNullish.ts
|
|
1922
|
+
const parseNullish = (state, parse) => {
|
|
1923
|
+
const operators = MathExpressionOperatorMap["Nullish"];
|
|
1924
|
+
let node = parse(state, "LeftHand");
|
|
1925
|
+
let operator = getOwnValue(operators, state.token);
|
|
1926
|
+
while (state.error === null && operator !== void 0) {
|
|
1927
|
+
getMathExpressionToken(state);
|
|
1928
|
+
const right = parse(state, "LeftHand");
|
|
1929
|
+
node = createLogicalNode(node, operator, right);
|
|
1930
|
+
operator = getOwnValue(operators, state.token);
|
|
1931
|
+
}
|
|
1932
|
+
return node;
|
|
1933
|
+
};
|
|
1934
|
+
// src/services/parser/rule/parsePercentage.ts
|
|
1935
|
+
const parsePercentage = (state, parse) => {
|
|
1936
|
+
const node = parse(state, "Unary");
|
|
1937
|
+
if (state.error !== null || !checkIsToken(state, "%")) return node;
|
|
1938
|
+
const snapshot = { ...state };
|
|
1939
|
+
getMathExpressionToken(state);
|
|
1940
|
+
if (state.error === null && !checkIsToken(state, "+") && !checkIsToken(state, "-") && checkIsUnaryStart(state)) {
|
|
1941
|
+
Object.assign(state, snapshot);
|
|
1942
|
+
return node;
|
|
1943
|
+
}
|
|
1944
|
+
return {
|
|
1945
|
+
operand: node,
|
|
1946
|
+
type: "percentage"
|
|
1947
|
+
};
|
|
1948
|
+
};
|
|
1949
|
+
// src/services/parser/rule/parsePower.ts
|
|
1950
|
+
const parsePower = (state, parse) => {
|
|
1951
|
+
const base = parse(state, "Nullish");
|
|
1952
|
+
const name = getOwnValue(MathExpressionOperatorMap["Power"], state.token);
|
|
1953
|
+
if (state.error !== null || name === void 0) return base;
|
|
1954
|
+
if (!checkCanMathExpressionNest(state)) return PLACEHOLDER;
|
|
1955
|
+
getMathExpressionToken(state);
|
|
1956
|
+
state.depth++;
|
|
1957
|
+
const exponent = parse(state, "Unary");
|
|
1958
|
+
state.depth--;
|
|
1959
|
+
return createBinaryNode(base, name, exponent);
|
|
1960
|
+
};
|
|
1961
|
+
// src/services/parser/parseSequence.ts
|
|
1962
|
+
const parseSequence = (state, closing, parseItem) => {
|
|
1963
|
+
const nodes = [];
|
|
1964
|
+
if (checkIsToken(state, closing)) return nodes;
|
|
1965
|
+
while (state.error === null) {
|
|
1966
|
+
nodes.push(parseItem(state));
|
|
1967
|
+
if (!checkIsToken(state, ",")) break;
|
|
1968
|
+
getMathExpressionToken(state);
|
|
1969
|
+
}
|
|
1970
|
+
if (state.error === null && !checkIsToken(state, closing)) failMathExpression(state, closing === ")" ? "Parenthesis ) expected" : "Parenthesis ] expected");
|
|
1971
|
+
return nodes;
|
|
1972
|
+
};
|
|
1973
|
+
// src/services/parser/parseList.ts
|
|
1974
|
+
const parseList = (state, parse) => {
|
|
1975
|
+
getMathExpressionToken(state);
|
|
1976
|
+
const elements = parseSequence(state, "]", (item) => parse(item, "Assignment"));
|
|
1977
|
+
if (state.error !== null) return PLACEHOLDER;
|
|
1978
|
+
getMathExpressionToken(state);
|
|
1979
|
+
return {
|
|
1980
|
+
elements,
|
|
1981
|
+
type: "list"
|
|
1982
|
+
};
|
|
1983
|
+
};
|
|
1984
|
+
// src/services/parser/parseArgument.ts
|
|
1985
|
+
const parseArgument = (state, parse) => checkIsToken(state, "[") ? parseList(state, parse) : parse(state, "Assignment");
|
|
1986
|
+
// src/services/parser/parseCall.ts
|
|
1987
|
+
const parseCall = (state, name, column, parse) => {
|
|
1988
|
+
const signatures = getMathExpressionSignatures(name);
|
|
1989
|
+
if (signatures === void 0) {
|
|
1990
|
+
failMathExpressionAt(state, `Unknown function "${name}"`, column);
|
|
1991
|
+
return PLACEHOLDER;
|
|
1992
|
+
}
|
|
1993
|
+
getMathExpressionToken(state);
|
|
1994
|
+
const argumentNodes = parseSequence(state, ")", (argument) => parseArgument(argument, parse));
|
|
1995
|
+
if (state.error !== null) return PLACEHOLDER;
|
|
1996
|
+
getMathExpressionToken(state);
|
|
1997
|
+
const parameterTypes = argumentNodes.map((argument) => getMathExpressionParameterType(argument));
|
|
1998
|
+
if (matchMathExpressionSignature(signatures, parameterTypes) === void 0) {
|
|
1999
|
+
failMathExpressionAt(state, getMathExpressionSignatureError(name, signatures, parameterTypes), column);
|
|
2000
|
+
return PLACEHOLDER;
|
|
2001
|
+
}
|
|
2002
|
+
return {
|
|
2003
|
+
arguments: argumentNodes,
|
|
2004
|
+
name,
|
|
2005
|
+
type: "call"
|
|
2006
|
+
};
|
|
2007
|
+
};
|
|
2008
|
+
// src/services/parser/rule/parseSymbol.ts
|
|
2009
|
+
const parseSymbol = (state, parse) => {
|
|
2010
|
+
if (state.tokenType === "number") {
|
|
2011
|
+
const value = computeNumberLiteral(state.token);
|
|
2012
|
+
if (value === null) {
|
|
2013
|
+
failMathExpression(state, `String "${state.token.split("i")[0]}" is out of range`);
|
|
2014
|
+
getMathExpressionToken(state);
|
|
2015
|
+
return PLACEHOLDER;
|
|
2016
|
+
}
|
|
2017
|
+
getMathExpressionToken(state);
|
|
2018
|
+
return {
|
|
2019
|
+
type: "number",
|
|
2020
|
+
value
|
|
2021
|
+
};
|
|
2022
|
+
}
|
|
2023
|
+
if (checkIsNameToken(state)) {
|
|
2024
|
+
const name = state.token;
|
|
2025
|
+
const column = getMathExpressionColumn(state);
|
|
2026
|
+
const isSymbol = state.tokenType === "symbol";
|
|
2027
|
+
getMathExpressionToken(state);
|
|
2028
|
+
if (checkIsToken(state, "(")) return parseCall(state, name, column, parse);
|
|
2029
|
+
if (isSymbol) return {
|
|
2030
|
+
name,
|
|
2031
|
+
type: "name"
|
|
2032
|
+
};
|
|
2033
|
+
}
|
|
2034
|
+
if (checkIsToken(state, "(")) {
|
|
2035
|
+
getMathExpressionToken(state);
|
|
2036
|
+
const operand = parse(state, "Assignment");
|
|
2037
|
+
if (state.error === null && !checkIsToken(state, ")")) failMathExpression(state, "Parenthesis ) expected");
|
|
2038
|
+
if (state.error !== null) return PLACEHOLDER;
|
|
2039
|
+
getMathExpressionToken(state);
|
|
2040
|
+
return {
|
|
2041
|
+
operand,
|
|
2042
|
+
type: "group"
|
|
2043
|
+
};
|
|
2044
|
+
}
|
|
2045
|
+
failMathExpression(state, checkIsToken(state, "") ? "Unexpected end of expression" : "Value expected");
|
|
2046
|
+
return PLACEHOLDER;
|
|
2047
|
+
};
|
|
2048
|
+
// src/services/parser/rule/parseUnary.ts
|
|
2049
|
+
const parseUnary = (state, parse) => {
|
|
2050
|
+
const name = getOwnValue(MathExpressionOperatorMap["Unary"], state.token);
|
|
2051
|
+
if (name === void 0) return parse(state, "Power");
|
|
2052
|
+
if (!checkCanMathExpressionNest(state)) return PLACEHOLDER;
|
|
2053
|
+
getMathExpressionToken(state);
|
|
2054
|
+
state.depth++;
|
|
2055
|
+
const operand = parse(state, "Unary");
|
|
2056
|
+
state.depth--;
|
|
2057
|
+
return {
|
|
2058
|
+
name,
|
|
2059
|
+
operand,
|
|
2060
|
+
type: "unary"
|
|
2061
|
+
};
|
|
2062
|
+
};
|
|
2063
|
+
// src/services/parser/MathExpressionRuleParserMap.ts
|
|
2064
|
+
const MathExpressionRuleParserMap = {
|
|
2065
|
+
["Assignment"]: parseAssignment,
|
|
2066
|
+
["Binary"]: parseBinary,
|
|
2067
|
+
["Conditional"]: parseConditional,
|
|
2068
|
+
["DivisionBeforeProduct"]: parseDivisionBeforeProduct,
|
|
2069
|
+
["ImplicitProduct"]: parseImplicitProduct,
|
|
2070
|
+
["LeftHand"]: parseLeftHand,
|
|
2071
|
+
["Nullish"]: parseNullish,
|
|
2072
|
+
["Percentage"]: parsePercentage,
|
|
2073
|
+
["Power"]: parsePower,
|
|
2074
|
+
["Symbol"]: parseSymbol,
|
|
2075
|
+
["Unary"]: parseUnary
|
|
2076
|
+
};
|
|
2077
|
+
// src/services/parser/parseMathExpressionRule.ts
|
|
2078
|
+
const parseMathExpressionRule = (state, rule) => MathExpressionRuleParserMap[rule](state, parseMathExpressionRule);
|
|
2079
|
+
// src/services/parser/parseMathExpression.ts
|
|
2080
|
+
const parseMathExpression = (formula) => {
|
|
2081
|
+
const state = {
|
|
2082
|
+
depth: 0,
|
|
2083
|
+
error: null,
|
|
2084
|
+
index: 0,
|
|
2085
|
+
nestingLevel: 0,
|
|
2086
|
+
source: formula,
|
|
2087
|
+
token: "",
|
|
2088
|
+
tokenType: "null"
|
|
2089
|
+
};
|
|
2090
|
+
getMathExpressionToken(state);
|
|
2091
|
+
const node = parseMathExpressionRule(state, "Assignment");
|
|
2092
|
+
if (state.error === null && !checkIsToken(state, "")) failMathExpression(state, state.tokenType === "delimiter" ? `Unexpected operator ${state.token}` : `Unexpected part "${state.token}"`);
|
|
2093
|
+
return state.error === null ? ok(node) : err(state.error);
|
|
2094
|
+
};
|
|
2095
|
+
export { ADDITIVE_NAMES, BERNOULLI_CLOSED_FORM_INDEX, DECIMAL_MARK_OPERATOR_FOLLOWERS, DELIMITERS_OF_ONE, DELIMITERS_OF_THREE, DELIMITERS_OF_TWO, EULER_MACLAURIN_COEFFICIENTS, FIRST_CHARACTERS_OF_THREE, FIRST_CHARACTERS_OF_TWO, HIGH_TAIL_COEFFICIENTS, LAST_FACTORIAL_ARGUMENT, LOW_TAIL_COEFFICIENTS, MATH_EXPRESSION_ABSOLUTE_TOLERANCE, MATH_EXPRESSION_RELATIVE_TOLERANCE, MAXIMUM_DIGITS, MAXIMUM_STIRLING_WORK, MAX_MATH_EXPRESSION_DEPTH, MILLER_RABIN_BASES, MathExpressionBinaryOperatorMap, MathExpressionBinaryOperatorSymbol, MathExpressionBitwiseAbsorption, MathExpressionBitwiseAbsorptionMap, MathExpressionConstantMap, MathExpressionError, MathExpressionFunctionMap, MathExpressionFunctionName, MathExpressionGrammarRule, MathExpressionLogicalOperator, MathExpressionNodeCompileMap, MathExpressionNodeType, MathExpressionOperatorMap, MathExpressionParameterType, MathExpressionRadix, MathExpressionRadixMap, MathExpressionRuleParserMap, MathExpressionTokenType, NAMED_DELIMITERS, NO_LOCALS, NO_VALUES, OVERFLOW_ARGUMENT, PLACEHOLDER, REFINEMENT_STEPS, SQRT_PI, STIRLING_TERMS, STIRLING_THRESHOLD, SUMMATION_LIMIT, WORD_CHARACTER_REGEX, WORD_START_REGEX, checkCanMathExpressionNest, checkIsDecimalMark, checkIsDigitCharacter, checkIsLarger, checkIsMatching, checkIsMatchingKind, checkIsMillerRabinPassing, checkIsNameToken, checkIsNearlyEqual, checkIsOperatorOperand, checkIsPercentageExpression, checkIsPrime, checkIsSignedNumber, checkIsSmaller, checkIsToken, checkIsTruthy, checkIsUnaryStart, checkIsWordCharacter, checkIsWordStartCharacter, collectMathExpressionAssignedNames, compareNumbers, compileArgument, compileAssignment, compileBinary, compileCall, compileComparison, compileConditional, compileGroup, compileList, compileListNode, compileLogical, compileMathExpression, compileMathExpressionNode, compileName, compileNumber, compilePercentage, compileUnary, computeBellNumber, computeBernoulliNumber, computeBitwiseAnd, computeBitwiseOr, computeCatalan, computeCombinations, computeComparison, computeCompositions, computeCorrelation, computeDeterminant, computeDistance, computeDotProduct, computeErf, computeErfInverse, computeExtreme, computeFactorial, computeGamma, computeGreatestCommonDivisor, computeHypotenuse, computeInitialGuess, computeIntegerGreatestCommonDivisor, computeInverseCoth, computeKullbackLeibler, computeLeastCommonMultiple, computeListComparison, computeLogGamma, computeLogGammaOfPositive, computeLogarithm, computeMean, computeMedian, computeMedianAbsoluteDeviation, computeModularInverse, computeModularPower, computeModulo, computeMultinomial, computeNorm, computeNthRoot, computeNumberLiteral, computePartitionSelect, computePermutations, computePointToLine2D, computePointToLine3D, computePolynomial, computeProduct, computeProductOfRange, computeQuantile, computeStirlingColumn, computeStirlingDiagonal, computeStirlingLogGamma, computeStirlingSecondKind, computeSum, computeSummedZeta, computeTruthyCount, computeVariance, computeVectorNorm, computeZeta, createBinaryNode, createBinaryOperation, createBinarySignatures, createBitwiseFold, createBitwisePair, createDigitSignatures, createExtremeSignatures, createIdentitySignatures, createIntegerBinarySignatures, createIntegerUnarySignatures, createListFirstCompute, createListPairCompute, createListPairSignature, createListSignature, createLogicalNode, createNonEmptyCompute, createNumberSignature, createNumericCompute, createOrderedListSignatures, createRangedSignatures, createSignature, createSingletonListSignature, createSpreadSignatures, createUnarySignatures, createVariadicSignatures, failExponent, failMathExpression, failMathExpressionAt, getArgumentCountText, getMathExpressionArityMessage, getMathExpressionCharacter, getMathExpressionChildren, getMathExpressionColumn, getMathExpressionConstant, getMathExpressionNames, getMathExpressionOuterValue, getMathExpressionParameterType, getMathExpressionScalarCompute, getMathExpressionSignatureError, getMathExpressionSignatures, getMathExpressionToken, getOwnValue, getParameterType, matchMathExpressionSignature, multiplyPair, parseArgument, parseAssignment, parseBinary, parseBinaryOperators, parseCall, parseConditional, parseDivisionBeforeProduct, parseImplicitProduct, parseLeftHand, parseList, parseMathExpression, parseMathExpressionRule, parseNullish, parsePercentage, parsePower, parseSequence, parseSymbol, parseUnary, readMathExpressionDelimiter, readMathExpressionNumber, readMathExpressionWord, readRadixNumber, roundToDigits, scaleList, shiftDecimalPlaces, skipMathExpressionSpaces, takeExponent, takeMathExpressionMatching, toNumber, walkNode };
|