@mojir/lits 2.1.31 → 2.1.33
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 +34 -69
- package/dist/cli/cli.js +221 -1753
- package/dist/cli/src/builtin/index.d.ts +1 -1
- package/dist/cli/src/builtin/specialExpressionTypes.d.ts +13 -15
- package/dist/cli/src/builtin/specialExpressions/functions.d.ts +3 -7
- package/dist/cli/src/builtin/specialExpressions/loop.d.ts +1 -1
- package/dist/cli/src/parser/Parser.d.ts +5 -4
- package/dist/cli/src/tokenizer/reservedNames.d.ts +2 -0
- package/dist/index.esm.js +220 -342
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +220 -342
- package/dist/index.js.map +1 -1
- package/dist/lits.iife.js +220 -342
- package/dist/lits.iife.js.map +1 -1
- package/dist/src/builtin/index.d.ts +1 -1
- package/dist/src/builtin/specialExpressionTypes.d.ts +13 -15
- package/dist/src/builtin/specialExpressions/functions.d.ts +3 -7
- package/dist/src/builtin/specialExpressions/loop.d.ts +1 -1
- package/dist/src/parser/Parser.d.ts +5 -4
- package/dist/src/tokenizer/reservedNames.d.ts +2 -0
- package/dist/testFramework.esm.js +210 -332
- package/dist/testFramework.esm.js.map +1 -1
- package/dist/testFramework.js +210 -332
- package/dist/testFramework.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/cli.js
CHANGED
|
@@ -92,7 +92,7 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
|
|
|
92
92
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
93
93
|
};
|
|
94
94
|
|
|
95
|
-
var version = "2.1.
|
|
95
|
+
var version = "2.1.33";
|
|
96
96
|
|
|
97
97
|
function getCodeMarker(sourceCodeInfo) {
|
|
98
98
|
if (!sourceCodeInfo.position || !sourceCodeInfo.code)
|
|
@@ -187,21 +187,21 @@ var specialExpressionTypes = {
|
|
|
187
187
|
'cond': 4,
|
|
188
188
|
'0_def': 5,
|
|
189
189
|
'defined?': 6,
|
|
190
|
-
'0_defn': 7,
|
|
191
|
-
'block':
|
|
192
|
-
'doseq':
|
|
193
|
-
'
|
|
194
|
-
'for':
|
|
195
|
-
'function':
|
|
196
|
-
'if':
|
|
197
|
-
'let':
|
|
198
|
-
'loop':
|
|
199
|
-
'object':
|
|
200
|
-
'recur':
|
|
201
|
-
'switch':
|
|
202
|
-
'throw':
|
|
203
|
-
'try':
|
|
204
|
-
'unless':
|
|
190
|
+
// '0_defn': 7,
|
|
191
|
+
'block': 7,
|
|
192
|
+
'doseq': 8,
|
|
193
|
+
'0_lambda': 9,
|
|
194
|
+
'for': 10,
|
|
195
|
+
// 'function': 10,
|
|
196
|
+
'if': 11,
|
|
197
|
+
'let': 12,
|
|
198
|
+
'loop': 13,
|
|
199
|
+
'object': 14,
|
|
200
|
+
'recur': 15,
|
|
201
|
+
'switch': 16,
|
|
202
|
+
'throw': 17,
|
|
203
|
+
'try': 18,
|
|
204
|
+
'unless': 19,
|
|
205
205
|
};
|
|
206
206
|
|
|
207
207
|
var NodeTypes = {
|
|
@@ -9239,7 +9239,7 @@ var collatzSequence = {
|
|
|
9239
9239
|
'noNth': true,
|
|
9240
9240
|
};
|
|
9241
9241
|
|
|
9242
|
-
function isPrime
|
|
9242
|
+
function isPrime(num) {
|
|
9243
9243
|
if (num <= 1) {
|
|
9244
9244
|
return false;
|
|
9245
9245
|
}
|
|
@@ -9261,18 +9261,18 @@ var primeSequence = {
|
|
|
9261
9261
|
var primes = [];
|
|
9262
9262
|
var num = 2;
|
|
9263
9263
|
while (primes.length < length) {
|
|
9264
|
-
if (isPrime
|
|
9264
|
+
if (isPrime(num)) {
|
|
9265
9265
|
primes.push(num);
|
|
9266
9266
|
}
|
|
9267
9267
|
num += 1;
|
|
9268
9268
|
}
|
|
9269
9269
|
return primes;
|
|
9270
9270
|
},
|
|
9271
|
-
'nth:prime?': function (n) { return isPrime
|
|
9271
|
+
'nth:prime?': function (n) { return isPrime(n); },
|
|
9272
9272
|
'nth:prime-take-while': function (takeWhile) {
|
|
9273
9273
|
var primes = [];
|
|
9274
9274
|
for (var i = 2;; i += 1) {
|
|
9275
|
-
if (!isPrime
|
|
9275
|
+
if (!isPrime(i)) {
|
|
9276
9276
|
continue;
|
|
9277
9277
|
}
|
|
9278
9278
|
if (!takeWhile(i, primes.length)) {
|
|
@@ -9288,7 +9288,7 @@ function isComposite(num) {
|
|
|
9288
9288
|
if (num <= 1) {
|
|
9289
9289
|
return false;
|
|
9290
9290
|
}
|
|
9291
|
-
return !isPrime
|
|
9291
|
+
return !isPrime(num);
|
|
9292
9292
|
}
|
|
9293
9293
|
var compositeSequence = {
|
|
9294
9294
|
'nth:composite-seq': function (length) {
|
|
@@ -11785,140 +11785,13 @@ var doSpecialExpression = {
|
|
|
11785
11785
|
},
|
|
11786
11786
|
};
|
|
11787
11787
|
|
|
11788
|
-
var
|
|
11789
|
-
true: true,
|
|
11790
|
-
false: false,
|
|
11791
|
-
null: null,
|
|
11792
|
-
else: null,
|
|
11793
|
-
case: null,
|
|
11794
|
-
each: null,
|
|
11795
|
-
in: null,
|
|
11796
|
-
when: null,
|
|
11797
|
-
while: null,
|
|
11798
|
-
catch: null,
|
|
11799
|
-
function: null,
|
|
11800
|
-
export: null,
|
|
11801
|
-
as: null,
|
|
11802
|
-
_: null,
|
|
11803
|
-
};
|
|
11804
|
-
var phi = (1 + Math.sqrt(5)) / 2;
|
|
11805
|
-
var numberReservedSymbolRecord = {
|
|
11806
|
-
'E': Math.E,
|
|
11807
|
-
'-E': -Math.E,
|
|
11808
|
-
'ε': Math.E,
|
|
11809
|
-
'-ε': -Math.E,
|
|
11810
|
-
'PI': Math.PI,
|
|
11811
|
-
'-PI': -Math.PI,
|
|
11812
|
-
'π': Math.PI,
|
|
11813
|
-
'-π': -Math.PI,
|
|
11814
|
-
'PHI': phi,
|
|
11815
|
-
'-PHI': -phi,
|
|
11816
|
-
'φ': phi,
|
|
11817
|
-
'-φ': -phi,
|
|
11818
|
-
'POSITIVE_INFINITY': Number.POSITIVE_INFINITY,
|
|
11819
|
-
'∞': Number.POSITIVE_INFINITY,
|
|
11820
|
-
'NEGATIVE_INFINITY': Number.NEGATIVE_INFINITY,
|
|
11821
|
-
'-∞': Number.NEGATIVE_INFINITY,
|
|
11822
|
-
'MAX_SAFE_INTEGER': Number.MAX_SAFE_INTEGER,
|
|
11823
|
-
'MIN_SAFE_INTEGER': Number.MIN_SAFE_INTEGER,
|
|
11824
|
-
'MAX_VALUE': Number.MAX_VALUE,
|
|
11825
|
-
'MIN_VALUE': Number.MIN_VALUE,
|
|
11826
|
-
'NaN': Number.NaN,
|
|
11827
|
-
};
|
|
11828
|
-
var reservedSymbolRecord = __assign(__assign({}, nonNumberReservedSymbolRecord), numberReservedSymbolRecord);
|
|
11829
|
-
function isReservedSymbol(symbol) {
|
|
11830
|
-
return symbol in reservedSymbolRecord;
|
|
11831
|
-
}
|
|
11832
|
-
function isNumberReservedSymbol(symbol) {
|
|
11833
|
-
return symbol in numberReservedSymbolRecord;
|
|
11834
|
-
}
|
|
11835
|
-
|
|
11836
|
-
function assertNameNotDefined(name, contextStack, builtin, sourceCodeInfo) {
|
|
11837
|
-
if (typeof name !== 'string')
|
|
11838
|
-
return;
|
|
11839
|
-
// TODO only subset of special expressions are necessary to check (CommonSpecialExpressionType)
|
|
11840
|
-
if (specialExpressionTypes[name])
|
|
11841
|
-
throw new LitsError("Cannot define variable ".concat(name, ", it's a special expression."), sourceCodeInfo);
|
|
11842
|
-
if (builtin.normalExpressions[name])
|
|
11843
|
-
throw new LitsError("Cannot define variable ".concat(name, ", it's a builtin function."), sourceCodeInfo);
|
|
11844
|
-
if (isReservedSymbol(name))
|
|
11845
|
-
throw new LitsError("Cannot define variable ".concat(name, ", it's a reserved name."), sourceCodeInfo);
|
|
11846
|
-
if (contextStack.globalContext[name])
|
|
11847
|
-
throw new LitsError("Name already defined \"".concat(name, "\"."), sourceCodeInfo);
|
|
11848
|
-
}
|
|
11849
|
-
|
|
11850
|
-
var functionSpecialExpression = {
|
|
11851
|
-
arity: {},
|
|
11852
|
-
evaluate: function (node, contextStack, _a) {
|
|
11853
|
-
var _b, _c;
|
|
11854
|
-
var builtin = _a.builtin, getUndefinedSymbols = _a.getUndefinedSymbols, evaluateNode = _a.evaluateNode;
|
|
11855
|
-
var _d = __read(node[1], 4), functionSymbol = _d[1], fn = _d[2], docString = _d[3];
|
|
11856
|
-
assertUserDefinedSymbolNode(functionSymbol, node[2]);
|
|
11857
|
-
assertNameNotDefined(functionSymbol[1], contextStack, builtin, node[2]);
|
|
11858
|
-
var evaluatedFunction = evaluateFunction(fn, contextStack, builtin, getUndefinedSymbols, evaluateNode);
|
|
11859
|
-
var min = evaluatedFunction[0].filter(function (arg) { return arg[0] !== bindingTargetTypes.rest && arg[1][1] === undefined; }).length;
|
|
11860
|
-
var max = evaluatedFunction[0].some(function (arg) { return arg[0] === bindingTargetTypes.rest; }) ? undefined : evaluatedFunction[0].length;
|
|
11861
|
-
var arity = { min: min > 0 ? min : undefined, max: max };
|
|
11862
|
-
var litsFunction = (_b = {},
|
|
11863
|
-
_b[FUNCTION_SYMBOL] = true,
|
|
11864
|
-
_b.sourceCodeInfo = node[2],
|
|
11865
|
-
_b.functionType = 'UserDefined',
|
|
11866
|
-
_b.name = functionSymbol[1],
|
|
11867
|
-
_b.evaluatedfunction = evaluatedFunction,
|
|
11868
|
-
_b.arity = arity,
|
|
11869
|
-
_b.docString = docString,
|
|
11870
|
-
_b);
|
|
11871
|
-
contextStack.addValues((_c = {}, _c[functionSymbol[1]] = litsFunction, _c), functionSymbol[2]);
|
|
11872
|
-
return litsFunction;
|
|
11873
|
-
},
|
|
11874
|
-
getUndefinedSymbols: function (node, contextStack, _a) {
|
|
11875
|
-
var _b, _c;
|
|
11876
|
-
var getUndefinedSymbols = _a.getUndefinedSymbols, builtin = _a.builtin, evaluateNode = _a.evaluateNode;
|
|
11877
|
-
var functionName = node[1][1][1];
|
|
11878
|
-
contextStack.addValues((_b = {}, _b[functionName] = true, _b), node[1][1][2]);
|
|
11879
|
-
var newContext = (_c = {}, _c[functionName] = { value: true }, _c);
|
|
11880
|
-
return getFunctionUnresolvedSymbols(node[1][2], contextStack, getUndefinedSymbols, builtin, evaluateNode, newContext);
|
|
11881
|
-
},
|
|
11882
|
-
};
|
|
11883
|
-
var defnSpecialExpression = {
|
|
11884
|
-
arity: {},
|
|
11885
|
-
evaluate: function (node, contextStack, _a) {
|
|
11886
|
-
var _b, _c;
|
|
11887
|
-
var builtin = _a.builtin, getUndefinedSymbols = _a.getUndefinedSymbols, evaluateNode = _a.evaluateNode;
|
|
11888
|
-
var _d = __read(node[1], 4), functionSymbol = _d[1], fn = _d[2], docString = _d[3];
|
|
11889
|
-
assertUserDefinedSymbolNode(functionSymbol, node[2]);
|
|
11890
|
-
assertNameNotDefined(functionSymbol[1], contextStack, builtin, node[2]);
|
|
11891
|
-
var evaluatedFunction = evaluateFunction(fn, contextStack, builtin, getUndefinedSymbols, evaluateNode);
|
|
11892
|
-
var min = evaluatedFunction[0].filter(function (arg) { return arg[0] !== bindingTargetTypes.rest && arg[1][1] === undefined; }).length;
|
|
11893
|
-
var arity = { min: min };
|
|
11894
|
-
var litsFunction = (_b = {},
|
|
11895
|
-
_b[FUNCTION_SYMBOL] = true,
|
|
11896
|
-
_b.sourceCodeInfo = node[2],
|
|
11897
|
-
_b.functionType = 'UserDefined',
|
|
11898
|
-
_b.name = functionSymbol[1],
|
|
11899
|
-
_b.evaluatedfunction = evaluatedFunction,
|
|
11900
|
-
_b.arity = arity,
|
|
11901
|
-
_b.docString = docString,
|
|
11902
|
-
_b);
|
|
11903
|
-
contextStack.exportValues((_c = {}, _c[functionSymbol[1]] = litsFunction, _c), functionSymbol[2]);
|
|
11904
|
-
return litsFunction;
|
|
11905
|
-
},
|
|
11906
|
-
getUndefinedSymbols: function (node, contextStack, _a) {
|
|
11907
|
-
var _b, _c;
|
|
11908
|
-
var getUndefinedSymbols = _a.getUndefinedSymbols, builtin = _a.builtin, evaluateNode = _a.evaluateNode;
|
|
11909
|
-
var functionName = node[1][1][1];
|
|
11910
|
-
var fn = node[1][2];
|
|
11911
|
-
contextStack.exportValues((_b = {}, _b[functionName] = true, _b), node[1][1][2]);
|
|
11912
|
-
var newContext = (_c = {}, _c[functionName] = { value: true }, _c);
|
|
11913
|
-
return getFunctionUnresolvedSymbols(fn, contextStack, getUndefinedSymbols, builtin, evaluateNode, newContext);
|
|
11914
|
-
},
|
|
11915
|
-
};
|
|
11916
|
-
var fnSpecialExpression = {
|
|
11788
|
+
var lambdaSpecialExpression = {
|
|
11917
11789
|
arity: {},
|
|
11918
11790
|
evaluate: function (node, contextStack, _a) {
|
|
11919
11791
|
var _b;
|
|
11920
11792
|
var builtin = _a.builtin, getUndefinedSymbols = _a.getUndefinedSymbols, evaluateNode = _a.evaluateNode;
|
|
11921
11793
|
var fn = node[1][1];
|
|
11794
|
+
var docString = node[1][2];
|
|
11922
11795
|
var evaluatedFunction = evaluateFunction(fn, contextStack, builtin, getUndefinedSymbols, evaluateNode);
|
|
11923
11796
|
var min = evaluatedFunction[0].filter(function (arg) { return arg[0] !== bindingTargetTypes.rest && arg[1][1] === undefined; }).length;
|
|
11924
11797
|
var max = evaluatedFunction[0].some(function (arg) { return arg[0] === bindingTargetTypes.rest; }) ? undefined : evaluatedFunction[0].length;
|
|
@@ -11930,8 +11803,9 @@ var fnSpecialExpression = {
|
|
|
11930
11803
|
_b.name = undefined,
|
|
11931
11804
|
_b.evaluatedfunction = evaluatedFunction,
|
|
11932
11805
|
_b.arity = arity,
|
|
11933
|
-
_b.docString =
|
|
11806
|
+
_b.docString = docString,
|
|
11934
11807
|
_b);
|
|
11808
|
+
evaluatedFunction[2].self = { value: litsFunction };
|
|
11935
11809
|
return litsFunction;
|
|
11936
11810
|
},
|
|
11937
11811
|
getUndefinedSymbols: function (node, contextStack, _a) {
|
|
@@ -11962,17 +11836,16 @@ function evaluateFunction(fn, contextStack, builtin, getUndefinedSymbols, evalua
|
|
|
11962
11836
|
];
|
|
11963
11837
|
return evaluatedFunction;
|
|
11964
11838
|
}
|
|
11965
|
-
function getFunctionUnresolvedSymbols(fn, contextStack, getUndefinedSymbols, builtin, evaluateNode
|
|
11839
|
+
function getFunctionUnresolvedSymbols(fn, contextStack, getUndefinedSymbols, builtin, evaluateNode) {
|
|
11966
11840
|
var result = new Set();
|
|
11967
|
-
var
|
|
11968
|
-
var newContext = {};
|
|
11841
|
+
var newContext = { self: { value: null } };
|
|
11969
11842
|
fn[0].forEach(function (arg) {
|
|
11970
11843
|
Object.assign(newContext, getAllBindingTargetNames(arg));
|
|
11971
11844
|
walkDefaults(arg, function (defaultNode) {
|
|
11972
11845
|
addToSet(result, getUndefinedSymbols([defaultNode], contextStack, builtin, evaluateNode));
|
|
11973
11846
|
});
|
|
11974
11847
|
});
|
|
11975
|
-
var newContextStack =
|
|
11848
|
+
var newContextStack = contextStack.create(newContext);
|
|
11976
11849
|
var overloadResult = getUndefinedSymbols(fn[1], newContextStack, builtin, evaluateNode);
|
|
11977
11850
|
addToSet(result, overloadResult);
|
|
11978
11851
|
return result;
|
|
@@ -12059,22 +11932,9 @@ var loopSpecialExpression = {
|
|
|
12059
11932
|
var newContextStack = contextStack.create(bindingContext);
|
|
12060
11933
|
var body = node[1][2];
|
|
12061
11934
|
var _loop_1 = function () {
|
|
12062
|
-
var e_1, _b;
|
|
12063
11935
|
var result = null;
|
|
12064
11936
|
try {
|
|
12065
|
-
|
|
12066
|
-
for (var body_1 = (e_1 = void 0, __values(body)), body_1_1 = body_1.next(); !body_1_1.done; body_1_1 = body_1.next()) {
|
|
12067
|
-
var form = body_1_1.value;
|
|
12068
|
-
result = evaluateNode(form, newContextStack);
|
|
12069
|
-
}
|
|
12070
|
-
}
|
|
12071
|
-
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
12072
|
-
finally {
|
|
12073
|
-
try {
|
|
12074
|
-
if (body_1_1 && !body_1_1.done && (_b = body_1.return)) _b.call(body_1);
|
|
12075
|
-
}
|
|
12076
|
-
finally { if (e_1) throw e_1.error; }
|
|
12077
|
-
}
|
|
11937
|
+
result = evaluateNode(body, newContextStack);
|
|
12078
11938
|
}
|
|
12079
11939
|
catch (error) {
|
|
12080
11940
|
if (error instanceof RecurSignal) {
|
|
@@ -12083,20 +11943,20 @@ var loopSpecialExpression = {
|
|
|
12083
11943
|
throw new LitsError("recur expected ".concat(bindingNodes.length, " parameters, got ").concat(valueToString(params_1.length)), node[2]);
|
|
12084
11944
|
}
|
|
12085
11945
|
bindingNodes.forEach(function (bindingNode, index) {
|
|
12086
|
-
var
|
|
11946
|
+
var e_1, _a;
|
|
12087
11947
|
var valueRecord = evalueateBindingNodeValues(bindingNode[1][0], asAny(params_1[index]), function (Node) { return evaluateNode(Node, contextStack); });
|
|
12088
11948
|
try {
|
|
12089
|
-
for (var _b = (
|
|
11949
|
+
for (var _b = (e_1 = void 0, __values(Object.entries(valueRecord))), _c = _b.next(); !_c.done; _c = _b.next()) {
|
|
12090
11950
|
var _d = __read(_c.value, 2), name_1 = _d[0], value = _d[1];
|
|
12091
11951
|
bindingContext[name_1].value = value;
|
|
12092
11952
|
}
|
|
12093
11953
|
}
|
|
12094
|
-
catch (
|
|
11954
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
12095
11955
|
finally {
|
|
12096
11956
|
try {
|
|
12097
11957
|
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
|
12098
11958
|
}
|
|
12099
|
-
finally { if (
|
|
11959
|
+
finally { if (e_1) throw e_1.error; }
|
|
12100
11960
|
}
|
|
12101
11961
|
});
|
|
12102
11962
|
return "continue";
|
|
@@ -12124,7 +11984,7 @@ var loopSpecialExpression = {
|
|
|
12124
11984
|
}, {});
|
|
12125
11985
|
var bindingValueNodes = bindingNodes.map(function (bindingNode) { return bindingNode[1][1]; });
|
|
12126
11986
|
var bindingsResult = getUndefinedSymbols(bindingValueNodes, contextStack, builtin, evaluateNode);
|
|
12127
|
-
var paramsResult = getUndefinedSymbols(node[1][2], contextStack.create(newContext), builtin, evaluateNode);
|
|
11987
|
+
var paramsResult = getUndefinedSymbols([node[1][2]], contextStack.create(newContext), builtin, evaluateNode);
|
|
12128
11988
|
return joinSets(bindingsResult, paramsResult);
|
|
12129
11989
|
},
|
|
12130
11990
|
};
|
|
@@ -12519,12 +12379,12 @@ var specialExpressions = [
|
|
|
12519
12379
|
condSpecialExpression,
|
|
12520
12380
|
defSpecialExpression,
|
|
12521
12381
|
definedSpecialExpression,
|
|
12522
|
-
defnSpecialExpression,
|
|
12382
|
+
// defnSpecialExpression,
|
|
12523
12383
|
doSpecialExpression,
|
|
12524
12384
|
doseqSpecialExpression,
|
|
12525
|
-
|
|
12385
|
+
lambdaSpecialExpression,
|
|
12526
12386
|
forSpecialExpression,
|
|
12527
|
-
functionSpecialExpression,
|
|
12387
|
+
// functionSpecialExpression,
|
|
12528
12388
|
ifSpecialExpression,
|
|
12529
12389
|
letSpecialExpression,
|
|
12530
12390
|
loopSpecialExpression,
|
|
@@ -12546,6 +12406,53 @@ new Set(specialExpressionKeys);
|
|
|
12546
12406
|
// TODO, remove
|
|
12547
12407
|
// console.log('builtin', [...specialExpressionKeys, ...normalExpressionKeys].length)
|
|
12548
12408
|
|
|
12409
|
+
var nonNumberReservedSymbolRecord = {
|
|
12410
|
+
true: true,
|
|
12411
|
+
false: false,
|
|
12412
|
+
null: null,
|
|
12413
|
+
else: null,
|
|
12414
|
+
case: null,
|
|
12415
|
+
each: null,
|
|
12416
|
+
in: null,
|
|
12417
|
+
when: null,
|
|
12418
|
+
while: null,
|
|
12419
|
+
catch: null,
|
|
12420
|
+
function: null,
|
|
12421
|
+
export: null,
|
|
12422
|
+
as: null,
|
|
12423
|
+
then: null,
|
|
12424
|
+
end: null,
|
|
12425
|
+
_: null,
|
|
12426
|
+
};
|
|
12427
|
+
var phi = (1 + Math.sqrt(5)) / 2;
|
|
12428
|
+
var numberReservedSymbolRecord = {
|
|
12429
|
+
'E': Math.E,
|
|
12430
|
+
'-E': -Math.E,
|
|
12431
|
+
'ε': Math.E,
|
|
12432
|
+
'-ε': -Math.E,
|
|
12433
|
+
'PI': Math.PI,
|
|
12434
|
+
'-PI': -Math.PI,
|
|
12435
|
+
'π': Math.PI,
|
|
12436
|
+
'-π': -Math.PI,
|
|
12437
|
+
'PHI': phi,
|
|
12438
|
+
'-PHI': -phi,
|
|
12439
|
+
'φ': phi,
|
|
12440
|
+
'-φ': -phi,
|
|
12441
|
+
'POSITIVE_INFINITY': Number.POSITIVE_INFINITY,
|
|
12442
|
+
'∞': Number.POSITIVE_INFINITY,
|
|
12443
|
+
'NEGATIVE_INFINITY': Number.NEGATIVE_INFINITY,
|
|
12444
|
+
'-∞': Number.NEGATIVE_INFINITY,
|
|
12445
|
+
'MAX_SAFE_INTEGER': Number.MAX_SAFE_INTEGER,
|
|
12446
|
+
'MIN_SAFE_INTEGER': Number.MIN_SAFE_INTEGER,
|
|
12447
|
+
'MAX_VALUE': Number.MAX_VALUE,
|
|
12448
|
+
'MIN_VALUE': Number.MIN_VALUE,
|
|
12449
|
+
'NaN': Number.NaN,
|
|
12450
|
+
};
|
|
12451
|
+
var reservedSymbolRecord = __assign(__assign({}, nonNumberReservedSymbolRecord), numberReservedSymbolRecord);
|
|
12452
|
+
function isNumberReservedSymbol(symbol) {
|
|
12453
|
+
return symbol in numberReservedSymbolRecord;
|
|
12454
|
+
}
|
|
12455
|
+
|
|
12549
12456
|
var functionExecutors = {
|
|
12550
12457
|
NativeJsFunction: function (fn, params, sourceCodeInfo) {
|
|
12551
12458
|
var _a;
|
|
@@ -12994,6 +12901,9 @@ var ContextStackImpl = /** @class */ (function () {
|
|
|
12994
12901
|
if (normalExpressionKeys.includes(name_1)) {
|
|
12995
12902
|
throw new LitsError("Cannot shadow builtin function \"".concat(name_1, "\""), sourceCodeInfo);
|
|
12996
12903
|
}
|
|
12904
|
+
if (name_1 === 'self') {
|
|
12905
|
+
throw new LitsError("Cannot shadow builtin value \"".concat(name_1, "\""), sourceCodeInfo);
|
|
12906
|
+
}
|
|
12997
12907
|
this.globalContext[name_1] = { value: value };
|
|
12998
12908
|
}
|
|
12999
12909
|
}
|
|
@@ -13023,6 +12933,9 @@ var ContextStackImpl = /** @class */ (function () {
|
|
|
13023
12933
|
if (normalExpressionKeys.includes(name_2)) {
|
|
13024
12934
|
throw new LitsError("Cannot shadow builtin function \"".concat(name_2, "\""), sourceCodeInfo);
|
|
13025
12935
|
}
|
|
12936
|
+
if (name_2 === 'self') {
|
|
12937
|
+
throw new LitsError("Cannot shadow builtin value \"".concat(name_2, "\""), sourceCodeInfo);
|
|
12938
|
+
}
|
|
13026
12939
|
currentContext[name_2] = { value: toAny(value) };
|
|
13027
12940
|
}
|
|
13028
12941
|
}
|
|
@@ -14018,9 +13931,6 @@ var Parser = /** @class */ (function () {
|
|
|
14018
13931
|
break;
|
|
14019
13932
|
}
|
|
14020
13933
|
}
|
|
14021
|
-
else if (isReservedSymbolToken(firstToken, 'function')) {
|
|
14022
|
-
return this.parseFunction(firstToken);
|
|
14023
|
-
}
|
|
14024
13934
|
else if (isReservedSymbolToken(firstToken, 'export')) {
|
|
14025
13935
|
if (!moduleScope) {
|
|
14026
13936
|
throw new LitsError('export is only allowed in module scope', firstToken[2]);
|
|
@@ -14313,9 +14223,8 @@ var Parser = /** @class */ (function () {
|
|
|
14313
14223
|
var _c = __read(params, 1), param = _c[0];
|
|
14314
14224
|
return withSourceCodeInfo([NodeTypes.SpecialExpression, [type, param]], symbol[2]);
|
|
14315
14225
|
}
|
|
14316
|
-
case specialExpressionTypes['
|
|
14226
|
+
case specialExpressionTypes['0_lambda']:
|
|
14317
14227
|
case specialExpressionTypes['0_def']:
|
|
14318
|
-
case specialExpressionTypes['0_defn']:
|
|
14319
14228
|
throw new LitsError("".concat(type, " is not allowed"), symbol[2]);
|
|
14320
14229
|
/* v8 ignore next 2 */
|
|
14321
14230
|
default:
|
|
@@ -14342,11 +14251,27 @@ var Parser = /** @class */ (function () {
|
|
|
14342
14251
|
return null;
|
|
14343
14252
|
}
|
|
14344
14253
|
this.advance();
|
|
14345
|
-
var
|
|
14346
|
-
|
|
14254
|
+
var nodes = void 0;
|
|
14255
|
+
var docString = '';
|
|
14256
|
+
if (isLBraceToken(this.peek())) {
|
|
14257
|
+
var parsedBlock = this.parseBlock(true);
|
|
14258
|
+
docString = parsedBlock[1];
|
|
14259
|
+
nodes = parsedBlock[0][1][1];
|
|
14260
|
+
}
|
|
14261
|
+
else {
|
|
14262
|
+
nodes = [this.parseExpression()];
|
|
14263
|
+
}
|
|
14264
|
+
return withSourceCodeInfo([
|
|
14265
|
+
NodeTypes.SpecialExpression,
|
|
14266
|
+
[
|
|
14267
|
+
specialExpressionTypes['0_lambda'],
|
|
14268
|
+
[
|
|
14347
14269
|
functionArguments,
|
|
14348
|
-
|
|
14349
|
-
]
|
|
14270
|
+
nodes,
|
|
14271
|
+
],
|
|
14272
|
+
docString,
|
|
14273
|
+
],
|
|
14274
|
+
], firstToken[2]);
|
|
14350
14275
|
}
|
|
14351
14276
|
catch (_a) {
|
|
14352
14277
|
return null;
|
|
@@ -14395,7 +14320,16 @@ var Parser = /** @class */ (function () {
|
|
|
14395
14320
|
var firstToken = this.asToken(this.peek());
|
|
14396
14321
|
this.advance();
|
|
14397
14322
|
var startPos = this.parseState.position;
|
|
14398
|
-
var
|
|
14323
|
+
var nodes;
|
|
14324
|
+
var docString = '';
|
|
14325
|
+
if (isLBraceToken(this.peek())) {
|
|
14326
|
+
var parsedBlock = this.parseBlock(true);
|
|
14327
|
+
docString = parsedBlock[1];
|
|
14328
|
+
nodes = parsedBlock[0][1][1];
|
|
14329
|
+
}
|
|
14330
|
+
else {
|
|
14331
|
+
nodes = [this.parseExpression()];
|
|
14332
|
+
}
|
|
14399
14333
|
var endPos = this.parseState.position - 1;
|
|
14400
14334
|
var arity = 0;
|
|
14401
14335
|
var dollar1 = 'NOT_SET'; // referring to argument bindings. $ = NAKED, $1, $2, $3, etc = WITH_1
|
|
@@ -14426,10 +14360,10 @@ var Parser = /** @class */ (function () {
|
|
|
14426
14360
|
functionArguments.push(withSourceCodeInfo([bindingTargetTypes.symbol, [[NodeTypes.UserDefinedSymbol, "$".concat(i)], undefined]], firstToken[2]));
|
|
14427
14361
|
}
|
|
14428
14362
|
}
|
|
14429
|
-
var node = withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes['
|
|
14363
|
+
var node = withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes['0_lambda'], [
|
|
14430
14364
|
functionArguments,
|
|
14431
|
-
|
|
14432
|
-
]]], firstToken[2]);
|
|
14365
|
+
nodes,
|
|
14366
|
+
], docString]], firstToken[2]);
|
|
14433
14367
|
return node;
|
|
14434
14368
|
};
|
|
14435
14369
|
Parser.prototype.parseOptionalDefaulValue = function () {
|
|
@@ -14620,21 +14554,10 @@ var Parser = /** @class */ (function () {
|
|
|
14620
14554
|
}
|
|
14621
14555
|
assertRParenToken(token);
|
|
14622
14556
|
this.advance();
|
|
14623
|
-
|
|
14557
|
+
assertOperatorToken(this.peek(), '->');
|
|
14624
14558
|
this.advance();
|
|
14625
|
-
var
|
|
14626
|
-
|
|
14627
|
-
params.push(this.parseExpression());
|
|
14628
|
-
if (isOperatorToken(this.peek(), ';')) {
|
|
14629
|
-
this.advance();
|
|
14630
|
-
}
|
|
14631
|
-
else if (!isRBraceToken(this.peek())) {
|
|
14632
|
-
throw new LitsError('Expected ;', this.peekSourceCodeInfo());
|
|
14633
|
-
}
|
|
14634
|
-
}
|
|
14635
|
-
assertRBraceToken(this.peek());
|
|
14636
|
-
this.advance();
|
|
14637
|
-
return withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.loop, bindingNodes, params]], firstToken[2]);
|
|
14559
|
+
var expression = this.parseExpression();
|
|
14560
|
+
return withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.loop, bindingNodes, expression]], firstToken[2]);
|
|
14638
14561
|
};
|
|
14639
14562
|
Parser.prototype.parseTry = function (token) {
|
|
14640
14563
|
this.advance();
|
|
@@ -14665,7 +14588,7 @@ var Parser = /** @class */ (function () {
|
|
|
14665
14588
|
throw new LitsError('Duplicate binding', loopBinding[0][2]);
|
|
14666
14589
|
}
|
|
14667
14590
|
forLoopBindings.push(loopBinding);
|
|
14668
|
-
if (isOperatorToken(this_1.peek(), '
|
|
14591
|
+
if (isOperatorToken(this_1.peek(), ',')) {
|
|
14669
14592
|
this_1.advance();
|
|
14670
14593
|
}
|
|
14671
14594
|
};
|
|
@@ -14675,6 +14598,8 @@ var Parser = /** @class */ (function () {
|
|
|
14675
14598
|
}
|
|
14676
14599
|
assertRParenToken(this.peek());
|
|
14677
14600
|
this.advance();
|
|
14601
|
+
assertOperatorToken(this.peek(), '->');
|
|
14602
|
+
this.advance();
|
|
14678
14603
|
var expression = this.parseExpression();
|
|
14679
14604
|
return isDoseq
|
|
14680
14605
|
? withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.doseq, forLoopBindings, expression]], firstToken[2])
|
|
@@ -14684,20 +14609,7 @@ var Parser = /** @class */ (function () {
|
|
|
14684
14609
|
var bindingNode = this.parseBinding();
|
|
14685
14610
|
var modifiers = [];
|
|
14686
14611
|
var token = this.asToken(this.peek());
|
|
14687
|
-
|
|
14688
|
-
throw new LitsError('Expected ")", ";" or ","', token[2]);
|
|
14689
|
-
}
|
|
14690
|
-
if (isOperatorToken(token, ',')) {
|
|
14691
|
-
this.advance();
|
|
14692
|
-
token = this.asToken(this.peek());
|
|
14693
|
-
}
|
|
14694
|
-
if (!isSymbolToken(token, 'let')
|
|
14695
|
-
&& !isReservedSymbolToken(token, 'when')
|
|
14696
|
-
&& !isReservedSymbolToken(token, 'while')
|
|
14697
|
-
&& !isRParenToken(token)
|
|
14698
|
-
&& !isOperatorToken(token, ';')) {
|
|
14699
|
-
throw new LitsError('Expected symbol ";", ")", let, when or while', token[2]);
|
|
14700
|
-
}
|
|
14612
|
+
this.assertInternalLoopBindingDelimiter(token, ['let', 'when', 'while']);
|
|
14701
14613
|
var letBindings = [];
|
|
14702
14614
|
if (token[1] === 'let') {
|
|
14703
14615
|
modifiers.push('&let');
|
|
@@ -14710,12 +14622,7 @@ var Parser = /** @class */ (function () {
|
|
|
14710
14622
|
}
|
|
14711
14623
|
letBindings.push(letNode[1][1]);
|
|
14712
14624
|
token = this_2.asToken(this_2.peek());
|
|
14713
|
-
|
|
14714
|
-
throw new LitsError('Expected ")", ";" or ","', token[2]);
|
|
14715
|
-
}
|
|
14716
|
-
if (isOperatorToken(token, ',')) {
|
|
14717
|
-
this_2.advance();
|
|
14718
|
-
}
|
|
14625
|
+
this_2.assertInternalLoopBindingDelimiter(token, ['let', 'when', 'while']);
|
|
14719
14626
|
token = this_2.asToken(this_2.peek());
|
|
14720
14627
|
};
|
|
14721
14628
|
var this_2 = this;
|
|
@@ -14729,33 +14636,57 @@ var Parser = /** @class */ (function () {
|
|
|
14729
14636
|
|| isReservedSymbolToken(token, 'while')) {
|
|
14730
14637
|
this.advance();
|
|
14731
14638
|
if (token[1] === 'when') {
|
|
14732
|
-
if (modifiers.includes('&when')) {
|
|
14733
|
-
throw new LitsError('Multiple when modifiers in for loop', token[2]);
|
|
14734
|
-
}
|
|
14735
14639
|
modifiers.push('&when');
|
|
14736
14640
|
whenNode = this.parseExpression();
|
|
14737
14641
|
}
|
|
14738
14642
|
else {
|
|
14739
|
-
if (modifiers.includes('&while')) {
|
|
14740
|
-
throw new LitsError('Multiple while modifiers in for loop', token[2]);
|
|
14741
|
-
}
|
|
14742
14643
|
modifiers.push('&while');
|
|
14743
14644
|
whileNode = this.parseExpression();
|
|
14744
14645
|
}
|
|
14745
14646
|
token = this.asToken(this.peek());
|
|
14746
|
-
|
|
14747
|
-
|
|
14748
|
-
|
|
14749
|
-
|
|
14750
|
-
|
|
14751
|
-
|
|
14647
|
+
var symbols = modifiers.includes('&when') && modifiers.includes('&while')
|
|
14648
|
+
? []
|
|
14649
|
+
: modifiers.includes('&when')
|
|
14650
|
+
? ['while']
|
|
14651
|
+
: ['when'];
|
|
14652
|
+
this.assertInternalLoopBindingDelimiter(token, symbols);
|
|
14752
14653
|
token = this.asToken(this.peek());
|
|
14753
14654
|
}
|
|
14754
|
-
|
|
14755
|
-
throw new LitsError('Expected "{" or ";"', token[2]);
|
|
14756
|
-
}
|
|
14655
|
+
this.assertInternalLoopBindingDelimiter(token, []);
|
|
14757
14656
|
return [bindingNode, letBindings, whenNode, whileNode];
|
|
14758
14657
|
};
|
|
14658
|
+
Parser.prototype.assertInternalLoopBindingDelimiter = function (token, symbols) {
|
|
14659
|
+
if (!this.isInternalLoopBindingDelimiter(token, symbols)) {
|
|
14660
|
+
var symbolsString = "".concat(__spreadArray(__spreadArray([], __read(symbols), false), [','], false).map(function (symbol) { return "\"".concat(symbol, "\""); }).join(', '), " or \")\"");
|
|
14661
|
+
throw new LitsError("Expected symbol ".concat(symbolsString), token[2]);
|
|
14662
|
+
}
|
|
14663
|
+
};
|
|
14664
|
+
Parser.prototype.isInternalLoopBindingDelimiter = function (token, symbols) {
|
|
14665
|
+
var e_1, _a;
|
|
14666
|
+
// end of loop binding
|
|
14667
|
+
if (isOperatorToken(token, ',') || isRParenToken(token)) {
|
|
14668
|
+
return true;
|
|
14669
|
+
}
|
|
14670
|
+
try {
|
|
14671
|
+
for (var symbols_1 = __values(symbols), symbols_1_1 = symbols_1.next(); !symbols_1_1.done; symbols_1_1 = symbols_1.next()) {
|
|
14672
|
+
var symbol = symbols_1_1.value;
|
|
14673
|
+
if (symbol === 'let' && isSymbolToken(token, 'let')) {
|
|
14674
|
+
return true;
|
|
14675
|
+
}
|
|
14676
|
+
if (['when', 'while'].includes(symbol) && isReservedSymbolToken(token, symbol)) {
|
|
14677
|
+
return true;
|
|
14678
|
+
}
|
|
14679
|
+
}
|
|
14680
|
+
}
|
|
14681
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
14682
|
+
finally {
|
|
14683
|
+
try {
|
|
14684
|
+
if (symbols_1_1 && !symbols_1_1.done && (_a = symbols_1.return)) _a.call(symbols_1);
|
|
14685
|
+
}
|
|
14686
|
+
finally { if (e_1) throw e_1.error; }
|
|
14687
|
+
}
|
|
14688
|
+
return false;
|
|
14689
|
+
};
|
|
14759
14690
|
Parser.prototype.parseBinding = function () {
|
|
14760
14691
|
var firstToken = asSymbolToken(this.peek());
|
|
14761
14692
|
var name = asUserDefinedSymbolNode(this.parseSymbol());
|
|
@@ -14774,10 +14705,8 @@ var Parser = /** @class */ (function () {
|
|
|
14774
14705
|
Parser.prototype.parseIfOrUnless = function (token) {
|
|
14775
14706
|
var isUnless = token[1] === 'unless';
|
|
14776
14707
|
this.advance();
|
|
14777
|
-
assertLParenToken(this.peek());
|
|
14778
|
-
this.advance();
|
|
14779
14708
|
var condition = this.parseExpression();
|
|
14780
|
-
|
|
14709
|
+
assertReservedSymbolToken(this.peek(), 'then');
|
|
14781
14710
|
this.advance();
|
|
14782
14711
|
var thenExpression = this.parseExpression();
|
|
14783
14712
|
var elseExpression;
|
|
@@ -14790,100 +14719,74 @@ var Parser = /** @class */ (function () {
|
|
|
14790
14719
|
: withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.if, [condition, thenExpression, elseExpression]]], token[2]);
|
|
14791
14720
|
};
|
|
14792
14721
|
Parser.prototype.parseCond = function (token) {
|
|
14793
|
-
this.advance();
|
|
14794
|
-
assertLBraceToken(this.peek());
|
|
14795
14722
|
this.advance();
|
|
14796
14723
|
var params = [];
|
|
14797
|
-
while (!this.isAtEnd() && !
|
|
14724
|
+
while (!this.isAtEnd() && !isReservedSymbolToken(this.peek(), 'end')) {
|
|
14798
14725
|
assertReservedSymbolToken(this.peek(), 'case');
|
|
14799
14726
|
this.advance();
|
|
14800
14727
|
var caseExpression = this.parseExpression();
|
|
14801
|
-
|
|
14728
|
+
assertReservedSymbolToken(this.peek(), 'then');
|
|
14802
14729
|
this.advance();
|
|
14803
14730
|
var expressions = [];
|
|
14804
14731
|
while (!this.isAtEnd()
|
|
14805
14732
|
&& !isReservedSymbolToken(this.peek(), 'case')
|
|
14806
|
-
&& !
|
|
14733
|
+
&& !isReservedSymbolToken(this.peek(), 'end')) {
|
|
14807
14734
|
expressions.push(this.parseExpression());
|
|
14808
14735
|
if (isOperatorToken(this.peek(), ';')) {
|
|
14809
14736
|
this.advance();
|
|
14810
14737
|
}
|
|
14811
|
-
else if (!isReservedSymbolToken(this.peek(), 'case') && !
|
|
14812
|
-
throw new LitsError('Expected
|
|
14738
|
+
else if (!isReservedSymbolToken(this.peek(), 'case') && !isReservedSymbolToken(this.peek(), 'end')) {
|
|
14739
|
+
throw new LitsError('Expected case or end', this.peekSourceCodeInfo());
|
|
14813
14740
|
}
|
|
14814
14741
|
}
|
|
14815
14742
|
var thenExpression = expressions.length === 1
|
|
14816
14743
|
? expressions[0]
|
|
14817
14744
|
: withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.block, expressions]], token[2]);
|
|
14818
14745
|
params.push([caseExpression, thenExpression]);
|
|
14819
|
-
if (
|
|
14746
|
+
if (isReservedSymbolToken(this.peek(), 'end')) {
|
|
14820
14747
|
break;
|
|
14821
14748
|
}
|
|
14822
14749
|
assertReservedSymbolToken(this.peek(), 'case');
|
|
14823
14750
|
}
|
|
14824
|
-
|
|
14751
|
+
assertReservedSymbolToken(this.peek());
|
|
14825
14752
|
this.advance();
|
|
14826
14753
|
return withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.cond, params]], token[2]);
|
|
14827
14754
|
};
|
|
14828
14755
|
Parser.prototype.parseSwitch = function (token) {
|
|
14829
|
-
this.advance();
|
|
14830
|
-
assertLParenToken(this.peek());
|
|
14831
14756
|
this.advance();
|
|
14832
14757
|
var valueExpression = this.parseExpression();
|
|
14833
|
-
assertRParenToken(this.peek());
|
|
14834
|
-
this.advance();
|
|
14835
|
-
assertLBraceToken(this.peek());
|
|
14836
|
-
this.advance();
|
|
14837
14758
|
var params = [];
|
|
14838
|
-
while (!this.isAtEnd() && !
|
|
14759
|
+
while (!this.isAtEnd() && !isReservedSymbolToken(this.peek(), 'end')) {
|
|
14839
14760
|
assertReservedSymbolToken(this.peek(), 'case');
|
|
14840
14761
|
this.advance();
|
|
14841
14762
|
var caseExpression = this.parseExpression();
|
|
14842
|
-
|
|
14763
|
+
assertReservedSymbolToken(this.peek(), 'then');
|
|
14843
14764
|
this.advance();
|
|
14844
14765
|
var expressions = [];
|
|
14845
14766
|
while (!this.isAtEnd()
|
|
14846
14767
|
&& !isReservedSymbolToken(this.peek(), 'case')
|
|
14847
|
-
&& !
|
|
14768
|
+
&& !isReservedSymbolToken(this.peek(), 'end')) {
|
|
14848
14769
|
expressions.push(this.parseExpression());
|
|
14849
14770
|
if (isOperatorToken(this.peek(), ';')) {
|
|
14850
14771
|
this.advance();
|
|
14851
14772
|
}
|
|
14852
|
-
else if (!isReservedSymbolToken(this.peek(), 'case') && !
|
|
14853
|
-
throw new LitsError('Expected
|
|
14773
|
+
else if (!isReservedSymbolToken(this.peek(), 'case') && !isReservedSymbolToken(this.peek(), 'end')) {
|
|
14774
|
+
throw new LitsError('Expected case or end', this.peekSourceCodeInfo());
|
|
14854
14775
|
}
|
|
14855
14776
|
}
|
|
14856
14777
|
var thenExpression = expressions.length === 1
|
|
14857
14778
|
? expressions[0]
|
|
14858
14779
|
: withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.block, expressions]], token[2]);
|
|
14859
14780
|
params.push([caseExpression, thenExpression]);
|
|
14860
|
-
if (
|
|
14781
|
+
if (isReservedSymbolToken(this.peek(), 'end')) {
|
|
14861
14782
|
break;
|
|
14862
14783
|
}
|
|
14863
14784
|
assertReservedSymbolToken(this.peek(), 'case');
|
|
14864
14785
|
}
|
|
14865
|
-
|
|
14786
|
+
assertReservedSymbolToken(this.peek(), 'end');
|
|
14866
14787
|
this.advance();
|
|
14867
14788
|
return withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes.switch, valueExpression, params]], token[2]);
|
|
14868
14789
|
};
|
|
14869
|
-
Parser.prototype.parseFunction = function (token) {
|
|
14870
|
-
this.advance();
|
|
14871
|
-
var symbol = this.parseSymbol();
|
|
14872
|
-
var functionArguments = this.parseFunctionArguments();
|
|
14873
|
-
var _a = __read(this.parseBlock(true), 2), block = _a[0], docString = _a[1];
|
|
14874
|
-
return withSourceCodeInfo([
|
|
14875
|
-
NodeTypes.SpecialExpression,
|
|
14876
|
-
[
|
|
14877
|
-
specialExpressionTypes.function,
|
|
14878
|
-
symbol,
|
|
14879
|
-
[
|
|
14880
|
-
functionArguments,
|
|
14881
|
-
block[1][1],
|
|
14882
|
-
],
|
|
14883
|
-
docString,
|
|
14884
|
-
],
|
|
14885
|
-
], token[2]);
|
|
14886
|
-
};
|
|
14887
14790
|
Parser.prototype.isAtEnd = function () {
|
|
14888
14791
|
return this.parseState.position >= this.tokenStream.tokens.length;
|
|
14889
14792
|
};
|
|
@@ -14896,7 +14799,7 @@ var Parser = /** @class */ (function () {
|
|
|
14896
14799
|
return [';', ',', ':'].includes(token[1]);
|
|
14897
14800
|
}
|
|
14898
14801
|
if (isReservedSymbolToken(token)) {
|
|
14899
|
-
return ['else', 'when', 'while', 'case', 'catch'].includes(token[1]);
|
|
14802
|
+
return ['else', 'when', 'while', 'case', 'catch', 'let', 'then'].includes(token[1]);
|
|
14900
14803
|
}
|
|
14901
14804
|
return false;
|
|
14902
14805
|
};
|
|
@@ -14907,13 +14810,8 @@ var Parser = /** @class */ (function () {
|
|
|
14907
14810
|
var letNode = this.parseLet(asSymbolToken(token));
|
|
14908
14811
|
return withSourceCodeInfo([NodeTypes.SpecialExpression, [specialExpressionTypes['0_def'], letNode[1][1]]], exportToken[2]);
|
|
14909
14812
|
}
|
|
14910
|
-
else if (isReservedSymbolToken(token, 'function')) {
|
|
14911
|
-
var functionNode = this.parseFunction(token);
|
|
14912
|
-
functionNode[1][0] = specialExpressionTypes['0_defn'];
|
|
14913
|
-
return functionNode;
|
|
14914
|
-
}
|
|
14915
14813
|
else {
|
|
14916
|
-
throw new LitsError('Expected let
|
|
14814
|
+
throw new LitsError('Expected let', this.peekSourceCodeInfo());
|
|
14917
14815
|
}
|
|
14918
14816
|
};
|
|
14919
14817
|
Parser.prototype.stringToSymbolNode = function (value, sourceCodeInfo) {
|
|
@@ -15103,7 +15001,7 @@ var AutoCompleter = /** @class */ (function () {
|
|
|
15103
15001
|
};
|
|
15104
15002
|
AutoCompleter.prototype.generateSuggestions = function (params) {
|
|
15105
15003
|
var _this = this;
|
|
15106
|
-
var blacklist = new Set(['0_def', '0_defn', '
|
|
15004
|
+
var blacklist = new Set(['0_def', '0_defn', '0_lambda']);
|
|
15107
15005
|
var startsWithCaseSensitive = this.generateWithPredicate(params, function (suggestion) {
|
|
15108
15006
|
return !blacklist.has(suggestion) && suggestion.startsWith(_this.searchString);
|
|
15109
15007
|
});
|
|
@@ -15732,7 +15630,7 @@ var arrayReference = {
|
|
|
15732
15630
|
'[[3, 2, 1, 0], [6, 5, 4], [9, 8, 7]] mapcat reverse',
|
|
15733
15631
|
'mapcat([[3, 2, 1, 0], [6, 5, 4], [9, 8, 7]], reverse)',
|
|
15734
15632
|
'[[3, 2, 1, 0,], [6, 5, 4,], [9, 8, 7]] mapcat reverse',
|
|
15735
|
-
"\
|
|
15633
|
+
"\nlet foo = (n) -> {\n [n - 1, n, n + 1]\n};\n[1, 2, 3] mapcat foo",
|
|
15736
15634
|
"\nmapcat(\n [[1, 2], [2, 2], [2, 3]],\n -> $ remove even?\n)",
|
|
15737
15635
|
],
|
|
15738
15636
|
},
|
|
@@ -20463,7 +20361,7 @@ var metaReference = {
|
|
|
20463
20361
|
description: 'Returns documentation string of the $fun.',
|
|
20464
20362
|
examples: [
|
|
20465
20363
|
'doc(+)',
|
|
20466
|
-
"\
|
|
20364
|
+
"\nlet add = (x, y) -> {\n \"\"\"\n Adds two numbers.\n Args:\n x: First number.\n y: Second number.\n Returns:\n Sum of x and y.\n \"\"\"\n x + y;\n};\n\ndoc(add)",
|
|
20467
20365
|
],
|
|
20468
20366
|
},
|
|
20469
20367
|
arity: {
|
|
@@ -20484,8 +20382,8 @@ var metaReference = {
|
|
|
20484
20382
|
examples: [
|
|
20485
20383
|
'arity(+)',
|
|
20486
20384
|
'arity(defined?)',
|
|
20487
|
-
"\
|
|
20488
|
-
"\
|
|
20385
|
+
"\nlet add = (x, y = 0) -> {\n x + y;\n};\n\narity(add)",
|
|
20386
|
+
"\nlet foo = (k, ...x) -> {\n k + x;\n};\n arity(foo)",
|
|
20489
20387
|
],
|
|
20490
20388
|
},
|
|
20491
20389
|
};
|
|
@@ -26365,8 +26263,8 @@ var sequenceReference = {
|
|
|
26365
26263
|
examples: [
|
|
26366
26264
|
'[3, 1, 2] sort (a, b) -> b - a',
|
|
26367
26265
|
'sort([3, 1, 2])',
|
|
26368
|
-
"\nsort(\n [3, 1, 2],\n (a, b) -> cond
|
|
26369
|
-
"\nsort(\n [3, 1, 2],\n (a, b) -> cond
|
|
26266
|
+
"\nsort(\n [3, 1, 2],\n (a, b) -> cond case a < b then -1 case a > b then 1 case true then -1 end\n)",
|
|
26267
|
+
"\nsort(\n [3, 1, 2],\n (a, b) -> cond case a > b then -1 case a < b then 1 case true then -1 end\n)",
|
|
26370
26268
|
],
|
|
26371
26269
|
},
|
|
26372
26270
|
'sort-by': {
|
|
@@ -26843,26 +26741,6 @@ var specialExpressionsReference = {
|
|
|
26843
26741
|
description: "\n Binds local variables s to `value`. `value` can be any expression. The scope of the variables is the body of the let expression.",
|
|
26844
26742
|
examples: ["\nlet a = 1 + 2 + 3 + 4;\nlet b = -> $ * ( $ + 1 );\nwrite!(\"a\", a, \"b\", b)"],
|
|
26845
26743
|
},
|
|
26846
|
-
'function': {
|
|
26847
|
-
title: 'function',
|
|
26848
|
-
category: 'Special expression',
|
|
26849
|
-
customVariants: ['function name(...arg) { body }'],
|
|
26850
|
-
details: [
|
|
26851
|
-
['name', 'symbol', 'The name of the function.'],
|
|
26852
|
-
['arg', '[...]arg-name [= value]', 'Arguments of the function.'],
|
|
26853
|
-
['...', 'rest-symbol', 'Optional. The rest argument of the function.'],
|
|
26854
|
-
['arg-name', 'symbol', 'The name of the argument.'],
|
|
26855
|
-
['value', 'any', 'Optional. The default value of the argument.'],
|
|
26856
|
-
['let-binding', 'symbol', 'Optional. The let bindings of the function.'],
|
|
26857
|
-
['body', 'one or more expressions', 'The body of the function.'],
|
|
26858
|
-
],
|
|
26859
|
-
description: 'Creates a named function. When called, evaluation of the last expression in the body is returned.',
|
|
26860
|
-
examples: [
|
|
26861
|
-
"\nfunction hyp (a, b) {\n sqrt(a * a + b * b)\n};\n\nhyp(3, 4)",
|
|
26862
|
-
"\nfunction sumOfSquares(...s) {\n apply(\n +,\n map(s, -> $ ^ 2)\n )\n};\n\nsumOfSquares(1, 2, 3, 4, 5)",
|
|
26863
|
-
"\nfunction withOptional(a, b = 42) {\n a + b\n};\n\nwrite!(withOptional(1), withOptional(1, 2))",
|
|
26864
|
-
],
|
|
26865
|
-
},
|
|
26866
26744
|
'try': {
|
|
26867
26745
|
title: 'try',
|
|
26868
26746
|
category: 'Special expression',
|
|
@@ -26902,7 +26780,7 @@ var specialExpressionsReference = {
|
|
|
26902
26780
|
'if': {
|
|
26903
26781
|
title: 'if',
|
|
26904
26782
|
category: 'Special expression',
|
|
26905
|
-
customVariants: ['if
|
|
26783
|
+
customVariants: ['if test then true-expr else false-expr', 'if test then true-expr'],
|
|
26906
26784
|
details: [
|
|
26907
26785
|
['test', 'expression', 'The condition to test.'],
|
|
26908
26786
|
['true-expr', 'expression', 'The expression to evaluate if the test is truthy.'],
|
|
@@ -26910,16 +26788,16 @@ var specialExpressionsReference = {
|
|
|
26910
26788
|
],
|
|
26911
26789
|
description: 'Either `true-expr` or `false-expr` branch is taken. `true-expr` is selected when $test is truthy. If $test is falsy `false-expr` is executed, if no `false-expr` exists, `null` is returned.',
|
|
26912
26790
|
examples: [
|
|
26913
|
-
"\nif
|
|
26914
|
-
'if
|
|
26915
|
-
'if
|
|
26916
|
-
'if
|
|
26791
|
+
"\nif true then {\n write!(\"TRUE\")\n} else {\n write!(\"FALSE\")\n}",
|
|
26792
|
+
'if false then write!("TRUE") else write!("FALSE")',
|
|
26793
|
+
'if true then write!("TRUE")',
|
|
26794
|
+
'if false then write!("TRUE")',
|
|
26917
26795
|
],
|
|
26918
26796
|
},
|
|
26919
26797
|
'unless': {
|
|
26920
26798
|
title: 'unless',
|
|
26921
26799
|
category: 'Special expression',
|
|
26922
|
-
customVariants: ['unless
|
|
26800
|
+
customVariants: ['unless test then true-expr else false-expr end', 'unless test true-expr end'],
|
|
26923
26801
|
details: [
|
|
26924
26802
|
['test', 'expression', 'The condition to test.'],
|
|
26925
26803
|
['true-expr', 'expression', 'The expressions to evaluate if the test is falsy.'],
|
|
@@ -26927,16 +26805,16 @@ var specialExpressionsReference = {
|
|
|
26927
26805
|
],
|
|
26928
26806
|
description: 'Either `true-expr` or `false-expr` branch is taken. `true-expr` is selected when $test is falsy. If $test is truthy `false-expr` is executed, if no `false-expr` exists, `null` is returned.',
|
|
26929
26807
|
examples: [
|
|
26930
|
-
"\nunless
|
|
26931
|
-
'unless
|
|
26932
|
-
'unless
|
|
26933
|
-
'unless
|
|
26808
|
+
"\nunless true then {\n write!(\"TRUE\")\n} else {\n write!(\"FALSE\")\n}",
|
|
26809
|
+
'unless false then write!("TRUE") else write!("FALSE")',
|
|
26810
|
+
'unless true then write!("TRUE")',
|
|
26811
|
+
'unless false then write!("TRUE")',
|
|
26934
26812
|
],
|
|
26935
26813
|
},
|
|
26936
26814
|
'cond': {
|
|
26937
26815
|
title: 'cond',
|
|
26938
26816
|
category: 'Special expression',
|
|
26939
|
-
customVariants: ['cond
|
|
26817
|
+
customVariants: ['cond cond-branch cond-branch ... end'],
|
|
26940
26818
|
details: [
|
|
26941
26819
|
['cond-branch', 'case test then body', 'A branch of the cond expression.'],
|
|
26942
26820
|
['test', 'expression', 'The condition to test.'],
|
|
@@ -26944,15 +26822,15 @@ var specialExpressionsReference = {
|
|
|
26944
26822
|
],
|
|
26945
26823
|
description: 'Used for branching. `cond-branches` are tested sequentially from the top. If no branch is tested truthy, `null` is returned.',
|
|
26946
26824
|
examples: [
|
|
26947
|
-
"\ncond
|
|
26948
|
-
"\ncond
|
|
26949
|
-
"\ncond
|
|
26825
|
+
"\ncond\n case false then write!(\"FALSE\")\n case true then write!(\"TRUE\")\nend",
|
|
26826
|
+
"\ncond\n case false then write!(\"FALSE\")\n case null then write!(\"null\")\nend ?? write!(\"TRUE\")",
|
|
26827
|
+
"\ncond\n case false then write!(\"FALSE\")\n case null then write!(\"null\")\nend ?? write!(\"TRUE\")",
|
|
26950
26828
|
],
|
|
26951
26829
|
},
|
|
26952
26830
|
'switch': {
|
|
26953
26831
|
title: 'switch',
|
|
26954
26832
|
category: 'Special expression',
|
|
26955
|
-
customVariants: ['switch
|
|
26833
|
+
customVariants: ['switch value switch-branch switch-branch ... end'],
|
|
26956
26834
|
details: [
|
|
26957
26835
|
['value', 'any', 'The value to test.'],
|
|
26958
26836
|
['switch-branch', 'case test then body', 'A branch of the switch expression.'],
|
|
@@ -26961,9 +26839,9 @@ var specialExpressionsReference = {
|
|
|
26961
26839
|
],
|
|
26962
26840
|
description: 'Used for branching. `switch-branches` are tested sequentially from the top against `value`. If no branch is tested truthy, `null` is returned.',
|
|
26963
26841
|
examples: [
|
|
26964
|
-
"\nswitch
|
|
26965
|
-
"\nswitch
|
|
26966
|
-
"\nswitch
|
|
26842
|
+
"\nswitch 1\n case 1 then write!(\"One\")\n case 2 then write!(\"Two\")\nend",
|
|
26843
|
+
"\nswitch 2\n case 1 then write!(\"One\")\n case 2 then write!(\"Two\")\nend",
|
|
26844
|
+
"\nswitch 3\n case 1 then write!(\"One\")\n case 2 then write!(\"Two\")\nend",
|
|
26967
26845
|
],
|
|
26968
26846
|
},
|
|
26969
26847
|
'block': {
|
|
@@ -26984,9 +26862,9 @@ var specialExpressionsReference = {
|
|
|
26984
26862
|
customVariants: ['recur(...recur-args)'],
|
|
26985
26863
|
description: 'Recursevly calls enclosing function or loop with its evaluated `recur-args`.',
|
|
26986
26864
|
examples: [
|
|
26987
|
-
"\
|
|
26988
|
-
"\n(n -> {\n write!(n);\n if
|
|
26989
|
-
"\nloop (n = 3) {\n write!(n);\n if
|
|
26865
|
+
"\nlet foo = (n) -> {\n write!(n);\n if !(zero?(n)) then {\n recur(n - 1)\n }\n};\nfoo(3)",
|
|
26866
|
+
"\n(n -> {\n write!(n);\n if !(zero?(n)) then {\n recur(n - 1)\n }\n})(3)",
|
|
26867
|
+
"\nloop (n = 3) -> {\n write!(n);\n if !(zero?(n)) then {\n recur(n - 1)\n }\n}",
|
|
26990
26868
|
],
|
|
26991
26869
|
},
|
|
26992
26870
|
};
|
|
@@ -31444,1382 +31322,6 @@ function isAbstractLitsError(error) {
|
|
|
31444
31322
|
return error instanceof LitsError;
|
|
31445
31323
|
}
|
|
31446
31324
|
|
|
31447
|
-
const defaultConfig = {
|
|
31448
|
-
spaceSeparation: false,
|
|
31449
|
-
precision: 8,
|
|
31450
|
-
epsilon: 1e-10,
|
|
31451
|
-
};
|
|
31452
|
-
const CONFIG = {
|
|
31453
|
-
spaceSeparation: false,
|
|
31454
|
-
precision: 8,
|
|
31455
|
-
epsilon: 1e-10,
|
|
31456
|
-
};
|
|
31457
|
-
function setConfig(newConfig) {
|
|
31458
|
-
CONFIG.spaceSeparation = newConfig.spaceSeparation ?? defaultConfig.spaceSeparation;
|
|
31459
|
-
CONFIG.precision = newConfig.precision ?? defaultConfig.precision;
|
|
31460
|
-
CONFIG.epsilon = newConfig.epsilon ?? defaultConfig.epsilon;
|
|
31461
|
-
}
|
|
31462
|
-
/**
|
|
31463
|
-
* Mathematical constants used for symbolic representation
|
|
31464
|
-
*/
|
|
31465
|
-
const CONSTANTS = [
|
|
31466
|
-
{ getSymbol: () => 'π', value: Math.PI },
|
|
31467
|
-
{ getSymbol: () => 'e', value: Math.E },
|
|
31468
|
-
{ getSymbol: () => 'φ', value: (1 + Math.sqrt(5)) / 2 },
|
|
31469
|
-
{ getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? '√(2)' : '√2', value: Math.sqrt(2) },
|
|
31470
|
-
{ getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? '√(3)' : '√3', value: Math.sqrt(3) },
|
|
31471
|
-
{ getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? '√(5)' : '√5', value: Math.sqrt(5) },
|
|
31472
|
-
{ getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? '√(7)' : '√7', value: Math.sqrt(7) },
|
|
31473
|
-
{ getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? '√(11)' : '√11', value: Math.sqrt(11) },
|
|
31474
|
-
{ getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? '√(13)' : '√13', value: Math.sqrt(13) },
|
|
31475
|
-
{ getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? '√(17)' : '√17', value: Math.sqrt(17) },
|
|
31476
|
-
{ getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? '√(19)' : '√19', value: Math.sqrt(19) },
|
|
31477
|
-
{ getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? '√(π)' : '√π', value: Math.sqrt(Math.PI) },
|
|
31478
|
-
{ getSymbol: () => 'ln(2)', value: Math.LN2 },
|
|
31479
|
-
{ getSymbol: () => 'ln(10)', value: Math.LN10 },
|
|
31480
|
-
{ getSymbol: () => 'log₂(e)', value: Math.LOG2E },
|
|
31481
|
-
{ getSymbol: () => 'log₁₀(e)', value: Math.LOG10E },
|
|
31482
|
-
];
|
|
31483
|
-
/**
|
|
31484
|
-
* Trigonometric values with their exact symbolic representations
|
|
31485
|
-
*/
|
|
31486
|
-
const TRIG_VALUES = [
|
|
31487
|
-
// sin values
|
|
31488
|
-
{ value: Math.sin(Math.PI / 6), getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? 'sin(π / 6)' : 'sin(π/6)', getExactForm: () => CONFIG.spaceSeparation ? '1 / 2' : '1/2' },
|
|
31489
|
-
{ value: Math.sin(Math.PI / 4), getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? 'sin(π / 4)' : 'sin(π/4)', getExactForm: () => CONFIG.spaceSeparation ? '√2 / 2' : '√2/2' },
|
|
31490
|
-
{ value: Math.sin(Math.PI / 3), getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? 'sin(π / 3)' : 'sin(π/3)', getExactForm: () => CONFIG.spaceSeparation ? '√3 / 2' : '√3/2' },
|
|
31491
|
-
{ value: Math.sin(Math.PI / 2), getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? 'sin(π / 2)' : 'sin(π/2)', getExactForm: () => '1' },
|
|
31492
|
-
// cos values
|
|
31493
|
-
{ value: Math.cos(0), getSymbol: () => 'cos(0)', getExactForm: () => '1' },
|
|
31494
|
-
{ value: Math.cos(Math.PI / 6), getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? 'cos(π / 6)' : 'cos(π/6)', getExactForm: () => CONFIG.spaceSeparation ? '√(3) / 2' : '√3/2' },
|
|
31495
|
-
{ value: Math.cos(Math.PI / 4), getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? 'cos(π / 4)' : 'cos(π/4)', getExactForm: () => CONFIG.spaceSeparation ? '√(2) / 2' : '√2/2' },
|
|
31496
|
-
{ value: Math.cos(Math.PI / 3), getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? 'cos(π / 3)' : 'cos(π/3)', getExactForm: () => CONFIG.spaceSeparation ? '1 / 2' : '1/2' },
|
|
31497
|
-
{ value: Math.cos(Math.PI / 2), getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? 'cos(π / 2)' : 'cos(π/2)', getExactForm: () => '0' },
|
|
31498
|
-
// tan values
|
|
31499
|
-
{ value: Math.tan(Math.PI / 6), getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? 'tan(π / 6)' : 'tan(π/6)', getExactForm: () => CONFIG.spaceSeparation ? '1 / √(3)' : '1/√3' },
|
|
31500
|
-
{ value: Math.tan(Math.PI / 4), getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? 'tan(π / 4)' : 'tan(π/4)', getExactForm: () => '1' },
|
|
31501
|
-
{ value: Math.tan(Math.PI / 3), getSymbol: noSpaceSeparation => CONFIG.spaceSeparation && !noSpaceSeparation ? 'tan(π / 3)' : 'tan(π/3)', getExactForm: () => CONFIG.spaceSeparation ? '√(3)' : '√3' },
|
|
31502
|
-
];
|
|
31503
|
-
|
|
31504
|
-
/**
|
|
31505
|
-
* Factorizes a number under a radical to extract perfect square factors
|
|
31506
|
-
* @param n The number to factorize
|
|
31507
|
-
* @returns Object containing the coefficient and remaining radicand
|
|
31508
|
-
*/
|
|
31509
|
-
function factorizeRadicand(n) {
|
|
31510
|
-
// Find largest perfect square factor
|
|
31511
|
-
let coefficient = 1;
|
|
31512
|
-
let i = 2;
|
|
31513
|
-
while (i * i <= n) {
|
|
31514
|
-
// Check if i² is a factor of n
|
|
31515
|
-
if (n % (i * i) === 0) {
|
|
31516
|
-
coefficient *= i;
|
|
31517
|
-
n /= (i * i);
|
|
31518
|
-
// Continue with the same i to check for multiple powers
|
|
31519
|
-
continue;
|
|
31520
|
-
}
|
|
31521
|
-
i++;
|
|
31522
|
-
}
|
|
31523
|
-
return { coefficient, radicand: n };
|
|
31524
|
-
}
|
|
31525
|
-
/**
|
|
31526
|
-
* Finds the greatest common divisor of two numbers
|
|
31527
|
-
* @param a First number
|
|
31528
|
-
* @param b Second number
|
|
31529
|
-
* @returns The GCD of a and b
|
|
31530
|
-
*/
|
|
31531
|
-
function findGCD(a, b) {
|
|
31532
|
-
a = Math.abs(a);
|
|
31533
|
-
b = Math.abs(b);
|
|
31534
|
-
while (b !== 0) {
|
|
31535
|
-
const temp = b;
|
|
31536
|
-
b = a % b;
|
|
31537
|
-
a = temp;
|
|
31538
|
-
}
|
|
31539
|
-
return a;
|
|
31540
|
-
}
|
|
31541
|
-
/**
|
|
31542
|
-
* Checks if a number is prime
|
|
31543
|
-
* @param n The number to check
|
|
31544
|
-
* @returns True if n is prime, false otherwise
|
|
31545
|
-
*/
|
|
31546
|
-
function isPrime(n) {
|
|
31547
|
-
if (n <= 1)
|
|
31548
|
-
return false;
|
|
31549
|
-
if (n <= 3)
|
|
31550
|
-
return true;
|
|
31551
|
-
if (n % 2 === 0 || n % 3 === 0)
|
|
31552
|
-
return false;
|
|
31553
|
-
let i = 5;
|
|
31554
|
-
while (i * i <= n) {
|
|
31555
|
-
if (n % i === 0 || n % (i + 2) === 0)
|
|
31556
|
-
return false;
|
|
31557
|
-
i += 6;
|
|
31558
|
-
}
|
|
31559
|
-
return true;
|
|
31560
|
-
}
|
|
31561
|
-
/**
|
|
31562
|
-
* Determines if a number's square root should be preserved in direct form
|
|
31563
|
-
* @param n The number to check
|
|
31564
|
-
* @returns True if the square root should be kept in direct form
|
|
31565
|
-
*/
|
|
31566
|
-
function shouldPreserveDirectRadical(n) {
|
|
31567
|
-
// Always preserve direct form for prime numbers
|
|
31568
|
-
if (isPrime(n))
|
|
31569
|
-
return true;
|
|
31570
|
-
// Always preserve direct form for small integers
|
|
31571
|
-
if (n <= 30)
|
|
31572
|
-
return true;
|
|
31573
|
-
// Check if n has any perfect square factors
|
|
31574
|
-
const factorized = factorizeRadicand(n);
|
|
31575
|
-
// If no perfect square factors were found, preserve direct form
|
|
31576
|
-
return factorized.coefficient === 1;
|
|
31577
|
-
}
|
|
31578
|
-
|
|
31579
|
-
function isNumberNode(node) {
|
|
31580
|
-
return node.type === 'Number';
|
|
31581
|
-
}
|
|
31582
|
-
function isUnaryOpNode(node) {
|
|
31583
|
-
return node.type === 'UnaryOp';
|
|
31584
|
-
}
|
|
31585
|
-
function isBinaryOpNode(node) {
|
|
31586
|
-
return node.type === 'BinaryOp';
|
|
31587
|
-
}
|
|
31588
|
-
function isRootNode(node) {
|
|
31589
|
-
return node.type === 'Root';
|
|
31590
|
-
}
|
|
31591
|
-
function isConstantNode(node) {
|
|
31592
|
-
return node.type === 'Constant';
|
|
31593
|
-
}
|
|
31594
|
-
function isPowerNode(node) {
|
|
31595
|
-
return node.type === 'Power';
|
|
31596
|
-
}
|
|
31597
|
-
|
|
31598
|
-
/**
|
|
31599
|
-
* Helper function to determine expression type priority for ordering
|
|
31600
|
-
* @returns A priority number (lower number = higher priority)
|
|
31601
|
-
*/
|
|
31602
|
-
function getExpressionPriority(node) {
|
|
31603
|
-
// Priority order: Number < Root/Power < Constant < Other
|
|
31604
|
-
if (isNumberNode(node))
|
|
31605
|
-
return 1;
|
|
31606
|
-
if (isRootNode(node))
|
|
31607
|
-
return 2;
|
|
31608
|
-
// Handle other node types here
|
|
31609
|
-
return 6; // Default for other types
|
|
31610
|
-
}
|
|
31611
|
-
/**
|
|
31612
|
-
* Determines if two expressions should be swapped in multiplication
|
|
31613
|
-
* @returns true if expressions should be swapped
|
|
31614
|
-
*/
|
|
31615
|
-
function shouldSwapInMultiplication(left, right) {
|
|
31616
|
-
const leftPriority = getExpressionPriority(left);
|
|
31617
|
-
const rightPriority = getExpressionPriority(right);
|
|
31618
|
-
// Lower priority number should come first
|
|
31619
|
-
return leftPriority > rightPriority;
|
|
31620
|
-
}
|
|
31621
|
-
|
|
31622
|
-
/**
|
|
31623
|
-
* Base abstract class for all expression nodes
|
|
31624
|
-
*/
|
|
31625
|
-
class ExprNode {
|
|
31626
|
-
}
|
|
31627
|
-
|
|
31628
|
-
/**
|
|
31629
|
-
* Checks if a node represents exactly zero within epsilon tolerance
|
|
31630
|
-
*/
|
|
31631
|
-
function isZero(node) {
|
|
31632
|
-
return isNumberNode(node) && Math.abs(node.evaluate()) < 1e-10;
|
|
31633
|
-
}
|
|
31634
|
-
/**
|
|
31635
|
-
* Checks if a node represents exactly one within epsilon tolerance
|
|
31636
|
-
*/
|
|
31637
|
-
function isOne(node) {
|
|
31638
|
-
return isNumberNode(node) && Math.abs(node.evaluate() - 1) < 1e-10;
|
|
31639
|
-
}
|
|
31640
|
-
/**
|
|
31641
|
-
* Node for numeric constants (integers, simple fractions, etc.)
|
|
31642
|
-
*/
|
|
31643
|
-
class NumberNode extends ExprNode {
|
|
31644
|
-
constructor(value) {
|
|
31645
|
-
super();
|
|
31646
|
-
Object.defineProperty(this, "value", {
|
|
31647
|
-
enumerable: true,
|
|
31648
|
-
configurable: true,
|
|
31649
|
-
writable: true,
|
|
31650
|
-
value: value
|
|
31651
|
-
});
|
|
31652
|
-
Object.defineProperty(this, "type", {
|
|
31653
|
-
enumerable: true,
|
|
31654
|
-
configurable: true,
|
|
31655
|
-
writable: true,
|
|
31656
|
-
value: 'Number'
|
|
31657
|
-
});
|
|
31658
|
-
}
|
|
31659
|
-
evaluate() {
|
|
31660
|
-
return this.value;
|
|
31661
|
-
}
|
|
31662
|
-
toString() {
|
|
31663
|
-
// Round values extremely close to zero to exactly zero
|
|
31664
|
-
if (Math.abs(this.value) < CONFIG.epsilon) {
|
|
31665
|
-
return "0";
|
|
31666
|
-
}
|
|
31667
|
-
if (this.value === Number.POSITIVE_INFINITY) {
|
|
31668
|
-
return "∞";
|
|
31669
|
-
}
|
|
31670
|
-
if (this.value === Number.NEGATIVE_INFINITY) {
|
|
31671
|
-
return "-∞";
|
|
31672
|
-
}
|
|
31673
|
-
if (Number.isInteger(this.value)) {
|
|
31674
|
-
return this.value.toString();
|
|
31675
|
-
}
|
|
31676
|
-
// Handle fractions if it's a "nice" fraction
|
|
31677
|
-
const MAX_DENOMINATOR = 1000;
|
|
31678
|
-
for (let denominator = 2; denominator <= MAX_DENOMINATOR; denominator++) {
|
|
31679
|
-
const numerator = Math.round(this.value * denominator);
|
|
31680
|
-
if (Math.abs(this.value - numerator / denominator) < CONFIG.epsilon) {
|
|
31681
|
-
const gcd = findGCD(Math.abs(numerator), denominator);
|
|
31682
|
-
return CONFIG.spaceSeparation ? `${numerator / gcd} / ${denominator / gcd}` : `${numerator / gcd}/${denominator / gcd}`;
|
|
31683
|
-
}
|
|
31684
|
-
}
|
|
31685
|
-
// Just return the decimal representation
|
|
31686
|
-
const valueStr = this.value.toString();
|
|
31687
|
-
if (valueStr.includes('.')) {
|
|
31688
|
-
const [, decimalPart] = valueStr.split('.');
|
|
31689
|
-
if (decimalPart.length > 8) {
|
|
31690
|
-
return this.value.toFixed(CONFIG.precision);
|
|
31691
|
-
}
|
|
31692
|
-
}
|
|
31693
|
-
return this.value.toString();
|
|
31694
|
-
}
|
|
31695
|
-
simplify() {
|
|
31696
|
-
return this; // Numbers are already in simplest form
|
|
31697
|
-
}
|
|
31698
|
-
equals(other) {
|
|
31699
|
-
if (!(isNumberNode(other)))
|
|
31700
|
-
return false;
|
|
31701
|
-
return Math.abs(this.value - other.evaluate()) < CONFIG.epsilon;
|
|
31702
|
-
}
|
|
31703
|
-
}
|
|
31704
|
-
|
|
31705
|
-
/**
|
|
31706
|
-
* Node for binary operations (+, -, *, /)
|
|
31707
|
-
*/
|
|
31708
|
-
class BinaryOpNode extends ExprNode {
|
|
31709
|
-
constructor(op, left, right) {
|
|
31710
|
-
super();
|
|
31711
|
-
Object.defineProperty(this, "op", {
|
|
31712
|
-
enumerable: true,
|
|
31713
|
-
configurable: true,
|
|
31714
|
-
writable: true,
|
|
31715
|
-
value: op
|
|
31716
|
-
});
|
|
31717
|
-
Object.defineProperty(this, "left", {
|
|
31718
|
-
enumerable: true,
|
|
31719
|
-
configurable: true,
|
|
31720
|
-
writable: true,
|
|
31721
|
-
value: left
|
|
31722
|
-
});
|
|
31723
|
-
Object.defineProperty(this, "right", {
|
|
31724
|
-
enumerable: true,
|
|
31725
|
-
configurable: true,
|
|
31726
|
-
writable: true,
|
|
31727
|
-
value: right
|
|
31728
|
-
});
|
|
31729
|
-
Object.defineProperty(this, "type", {
|
|
31730
|
-
enumerable: true,
|
|
31731
|
-
configurable: true,
|
|
31732
|
-
writable: true,
|
|
31733
|
-
value: 'BinaryOp'
|
|
31734
|
-
});
|
|
31735
|
-
// Apply automatic ordering for multiplication operations
|
|
31736
|
-
if (this.op === '*' && shouldSwapInMultiplication(left, right)) {
|
|
31737
|
-
// Swap the operands if they're in the wrong order
|
|
31738
|
-
this.left = right;
|
|
31739
|
-
this.right = left;
|
|
31740
|
-
}
|
|
31741
|
-
}
|
|
31742
|
-
evaluate() {
|
|
31743
|
-
const leftVal = this.left.evaluate();
|
|
31744
|
-
const rightVal = this.right.evaluate();
|
|
31745
|
-
switch (this.op) {
|
|
31746
|
-
case '+': return leftVal + rightVal;
|
|
31747
|
-
case '-': return leftVal - rightVal;
|
|
31748
|
-
case '*': return leftVal * rightVal;
|
|
31749
|
-
case '/': return leftVal / rightVal;
|
|
31750
|
-
}
|
|
31751
|
-
}
|
|
31752
|
-
toString() {
|
|
31753
|
-
let leftStr = this.left.toString();
|
|
31754
|
-
let rightStr = this.right.toString();
|
|
31755
|
-
// Add parentheses if needed
|
|
31756
|
-
if ((this.op === '+' || this.op === '-')
|
|
31757
|
-
&& (isBinaryOpNode(this.left) && (this.left.getOp() === '+' || this.left.getOp() === '-'))) {
|
|
31758
|
-
leftStr = `(${leftStr})`;
|
|
31759
|
-
}
|
|
31760
|
-
else if ((this.op === '+' || this.op === '-')
|
|
31761
|
-
&& (isBinaryOpNode(this.right) && (this.right.getOp() === '+' || this.right.getOp() === '-'))) {
|
|
31762
|
-
rightStr = `(${rightStr})`;
|
|
31763
|
-
}
|
|
31764
|
-
if (this.op === '*') {
|
|
31765
|
-
// Double-check order at display time
|
|
31766
|
-
if (shouldSwapInMultiplication(this.left, this.right)) {
|
|
31767
|
-
return CONFIG.spaceSeparation
|
|
31768
|
-
? `${this.right.toString()} · ${this.left.toString()}`
|
|
31769
|
-
: `${this.right.toString()}·${this.left.toString()}`;
|
|
31770
|
-
}
|
|
31771
|
-
return CONFIG.spaceSeparation
|
|
31772
|
-
? `${this.left.toString()} · ${this.right.toString()}`
|
|
31773
|
-
: `${this.left.toString()}·${this.right.toString()}`;
|
|
31774
|
-
}
|
|
31775
|
-
else {
|
|
31776
|
-
return CONFIG.spaceSeparation
|
|
31777
|
-
? `${leftStr} ${this.op} ${rightStr}`
|
|
31778
|
-
: `${leftStr}${this.op}${rightStr}`;
|
|
31779
|
-
}
|
|
31780
|
-
}
|
|
31781
|
-
getOp() {
|
|
31782
|
-
return this.op;
|
|
31783
|
-
}
|
|
31784
|
-
getLeft() {
|
|
31785
|
-
return this.left;
|
|
31786
|
-
}
|
|
31787
|
-
getRight() {
|
|
31788
|
-
return this.right;
|
|
31789
|
-
}
|
|
31790
|
-
simplify() {
|
|
31791
|
-
// First simplify both children
|
|
31792
|
-
const left = this.left.simplify();
|
|
31793
|
-
const right = this.right.simplify();
|
|
31794
|
-
// If both are numbers, perform the operation
|
|
31795
|
-
if (isNumberNode(left) && isNumberNode(right)) {
|
|
31796
|
-
return new NumberNode(this.evaluate());
|
|
31797
|
-
}
|
|
31798
|
-
// Simplification rules for addition
|
|
31799
|
-
if (this.op === '+') {
|
|
31800
|
-
// a + 0 = a
|
|
31801
|
-
if (isNumberNode(right) && right.evaluate() === 0) {
|
|
31802
|
-
return left;
|
|
31803
|
-
}
|
|
31804
|
-
// 0 + a = a
|
|
31805
|
-
if (isNumberNode(left) && left.evaluate() === 0) {
|
|
31806
|
-
return right;
|
|
31807
|
-
}
|
|
31808
|
-
}
|
|
31809
|
-
// Simplification rules for subtraction
|
|
31810
|
-
if (this.op === '-') {
|
|
31811
|
-
// a - 0 = a
|
|
31812
|
-
if (isNumberNode(right) && right.evaluate() === 0) {
|
|
31813
|
-
return left;
|
|
31814
|
-
}
|
|
31815
|
-
// 0 - a = -a
|
|
31816
|
-
if (isNumberNode(left) && left.evaluate() === 0) {
|
|
31817
|
-
return new UnaryOpNode('-', right);
|
|
31818
|
-
}
|
|
31819
|
-
// a - a = 0
|
|
31820
|
-
if (left.equals(right)) {
|
|
31821
|
-
return new NumberNode(0);
|
|
31822
|
-
}
|
|
31823
|
-
}
|
|
31824
|
-
// Simplification rules for multiplication
|
|
31825
|
-
if (this.op === '*') {
|
|
31826
|
-
// a * 0 = 0
|
|
31827
|
-
if (isZero(left) || isZero(right)) {
|
|
31828
|
-
return new NumberNode(0);
|
|
31829
|
-
}
|
|
31830
|
-
// a * 1 = a
|
|
31831
|
-
if (isOne(right)) {
|
|
31832
|
-
return left;
|
|
31833
|
-
}
|
|
31834
|
-
// 1 * a = a
|
|
31835
|
-
if (isOne(left)) {
|
|
31836
|
-
return right;
|
|
31837
|
-
}
|
|
31838
|
-
// Special case for n·√m/2 (12·√3/2 case)
|
|
31839
|
-
if (isNumberNode(left)
|
|
31840
|
-
&& isBinaryOpNode(right)
|
|
31841
|
-
&& right.getOp() === '/'
|
|
31842
|
-
&& isNumberNode(right.getRight())
|
|
31843
|
-
&& right.getRight().evaluate() === 2
|
|
31844
|
-
&& isRootNode(right.getLeft())) {
|
|
31845
|
-
const n = left.evaluate();
|
|
31846
|
-
const rootNode = right.getLeft();
|
|
31847
|
-
return new BinaryOpNode('*', new NumberNode(n / 2), rootNode);
|
|
31848
|
-
}
|
|
31849
|
-
// Handle multiplications with fractions
|
|
31850
|
-
if (isBinaryOpNode(left) && left.getOp() === '/') {
|
|
31851
|
-
// (a/b) * c = (a*c)/b
|
|
31852
|
-
const fraction = left;
|
|
31853
|
-
const numerator = fraction.getLeft();
|
|
31854
|
-
const denominator = fraction.getRight();
|
|
31855
|
-
return new BinaryOpNode('/', new BinaryOpNode('*', numerator, right), denominator).simplify();
|
|
31856
|
-
}
|
|
31857
|
-
if (isBinaryOpNode(right) && right.getOp() === '/') {
|
|
31858
|
-
// c * (a/b) = (c*a)/b
|
|
31859
|
-
const fraction = right;
|
|
31860
|
-
const numerator = fraction.getLeft();
|
|
31861
|
-
const denominator = fraction.getRight();
|
|
31862
|
-
return new BinaryOpNode('/', new BinaryOpNode('*', left, numerator), denominator).simplify();
|
|
31863
|
-
}
|
|
31864
|
-
// Multiple numeric factors: (2·3)·π = 6·π
|
|
31865
|
-
if (isNumberNode(left)
|
|
31866
|
-
&& isBinaryOpNode(right)
|
|
31867
|
-
&& right.getOp() === '*'
|
|
31868
|
-
&& isNumberNode(right.getLeft())) {
|
|
31869
|
-
const n1 = left.evaluate();
|
|
31870
|
-
const n2 = (right).getLeft().evaluate();
|
|
31871
|
-
const restTerm = right.getRight();
|
|
31872
|
-
return new BinaryOpNode('*', new NumberNode(n1 * n2), restTerm).simplify();
|
|
31873
|
-
}
|
|
31874
|
-
// Consolidate products of square roots: √a·√b = √(a·b)
|
|
31875
|
-
if (isRootNode(left) && isRootNode(right)) {
|
|
31876
|
-
const leftOperand = getRootOperand(left);
|
|
31877
|
-
const rightOperand = getRootOperand(right);
|
|
31878
|
-
// Create √(a·b) instead of √a·√b
|
|
31879
|
-
if (isNumberNode(leftOperand) && isNumberNode(rightOperand)) {
|
|
31880
|
-
const newRadicand = leftOperand.evaluate() * rightOperand.evaluate();
|
|
31881
|
-
return new RootNode(new NumberNode(newRadicand)).simplify();
|
|
31882
|
-
}
|
|
31883
|
-
}
|
|
31884
|
-
}
|
|
31885
|
-
// Simplification rules for division
|
|
31886
|
-
if (this.op === '/') {
|
|
31887
|
-
// 0 / a = 0
|
|
31888
|
-
if (isZero(left)) {
|
|
31889
|
-
return new NumberNode(0);
|
|
31890
|
-
}
|
|
31891
|
-
// a / 1 = a
|
|
31892
|
-
if (isOne(right)) {
|
|
31893
|
-
return left;
|
|
31894
|
-
}
|
|
31895
|
-
// a / a = 1
|
|
31896
|
-
if (left.equals(right)) {
|
|
31897
|
-
return new NumberNode(1);
|
|
31898
|
-
}
|
|
31899
|
-
// Simplify numeric fractions
|
|
31900
|
-
if (isNumberNode(left) && isNumberNode(right)) {
|
|
31901
|
-
const num = left.evaluate();
|
|
31902
|
-
const denom = right.evaluate();
|
|
31903
|
-
if (Number.isInteger(num) && Number.isInteger(denom)) {
|
|
31904
|
-
const gcd = findGCD(Math.abs(num), Math.abs(denom));
|
|
31905
|
-
if (gcd > 1) {
|
|
31906
|
-
return new BinaryOpNode('/', new NumberNode(num / gcd), new NumberNode(denom / gcd));
|
|
31907
|
-
}
|
|
31908
|
-
}
|
|
31909
|
-
}
|
|
31910
|
-
// (a*b) / b = a
|
|
31911
|
-
if (isBinaryOpNode(left) && left.getOp() === '*') {
|
|
31912
|
-
const product = left;
|
|
31913
|
-
// Case for n·something / n = something (6·√3/6 = √3)
|
|
31914
|
-
if (isNumberNode(product.getLeft())
|
|
31915
|
-
&& isNumberNode(right)
|
|
31916
|
-
&& Math.abs(product.getLeft().evaluate() - right.evaluate()) < 1e-10) {
|
|
31917
|
-
return product.getRight();
|
|
31918
|
-
}
|
|
31919
|
-
if (isNumberNode(product.getRight())
|
|
31920
|
-
&& isNumberNode(right)
|
|
31921
|
-
&& Math.abs(product.getRight().evaluate() - right.evaluate()) < 1e-10) {
|
|
31922
|
-
return product.getLeft();
|
|
31923
|
-
}
|
|
31924
|
-
// General case
|
|
31925
|
-
if (product.getLeft().equals(right)) {
|
|
31926
|
-
return product.getRight();
|
|
31927
|
-
}
|
|
31928
|
-
if (product.getRight().equals(right)) {
|
|
31929
|
-
return product.getLeft();
|
|
31930
|
-
}
|
|
31931
|
-
}
|
|
31932
|
-
}
|
|
31933
|
-
// If no simplification rules apply, return a new node with simplified children
|
|
31934
|
-
return new BinaryOpNode(this.op, left, right);
|
|
31935
|
-
}
|
|
31936
|
-
equals(other) {
|
|
31937
|
-
if (!(isBinaryOpNode(other)))
|
|
31938
|
-
return false;
|
|
31939
|
-
const otherOp = other;
|
|
31940
|
-
return this.op === otherOp.getOp()
|
|
31941
|
-
&& this.left.equals(otherOp.getLeft())
|
|
31942
|
-
&& this.right.equals(otherOp.getRight());
|
|
31943
|
-
}
|
|
31944
|
-
}
|
|
31945
|
-
/**
|
|
31946
|
-
* Extracts the radicand from a square root expression
|
|
31947
|
-
*/
|
|
31948
|
-
function getRootOperand(node) {
|
|
31949
|
-
if (isRootNode(node)) {
|
|
31950
|
-
return node.getOperand();
|
|
31951
|
-
}
|
|
31952
|
-
if (isUnaryOpNode(node) && node.getOp() === 'sqrt') {
|
|
31953
|
-
return node.getOperand();
|
|
31954
|
-
}
|
|
31955
|
-
throw new Error('Not a root node');
|
|
31956
|
-
}
|
|
31957
|
-
/**
|
|
31958
|
-
* Checks if a node represents a square root expression
|
|
31959
|
-
*/
|
|
31960
|
-
function isRootLikeNode(node) {
|
|
31961
|
-
return isRootNode(node)
|
|
31962
|
-
|| (isUnaryOpNode(node) && node.getOp() === 'sqrt');
|
|
31963
|
-
}
|
|
31964
|
-
/**
|
|
31965
|
-
* Specialized node for square roots for nicer formatting
|
|
31966
|
-
*/
|
|
31967
|
-
class RootNode extends ExprNode {
|
|
31968
|
-
constructor(operand) {
|
|
31969
|
-
super();
|
|
31970
|
-
Object.defineProperty(this, "operand", {
|
|
31971
|
-
enumerable: true,
|
|
31972
|
-
configurable: true,
|
|
31973
|
-
writable: true,
|
|
31974
|
-
value: operand
|
|
31975
|
-
});
|
|
31976
|
-
Object.defineProperty(this, "type", {
|
|
31977
|
-
enumerable: true,
|
|
31978
|
-
configurable: true,
|
|
31979
|
-
writable: true,
|
|
31980
|
-
value: 'Root'
|
|
31981
|
-
});
|
|
31982
|
-
}
|
|
31983
|
-
evaluate() {
|
|
31984
|
-
return Math.sqrt(this.operand.evaluate());
|
|
31985
|
-
}
|
|
31986
|
-
toString() {
|
|
31987
|
-
if (CONFIG.spaceSeparation) {
|
|
31988
|
-
const operandStr = this.operand.toString();
|
|
31989
|
-
if (!isNumberNode(this.operand) || operandStr.includes('/')) {
|
|
31990
|
-
return `√(${operandStr})`;
|
|
31991
|
-
}
|
|
31992
|
-
else {
|
|
31993
|
-
return `√${operandStr}`;
|
|
31994
|
-
}
|
|
31995
|
-
}
|
|
31996
|
-
else {
|
|
31997
|
-
return `√${this.operand.toString()}`;
|
|
31998
|
-
}
|
|
31999
|
-
}
|
|
32000
|
-
simplify() {
|
|
32001
|
-
const operand = this.operand.simplify();
|
|
32002
|
-
// Simplify sqrt of perfect squares
|
|
32003
|
-
if (isNumberNode(operand)) {
|
|
32004
|
-
const val = operand.evaluate();
|
|
32005
|
-
const sqrtVal = Math.sqrt(val);
|
|
32006
|
-
// If it's a perfect square, return the number
|
|
32007
|
-
if (Number.isInteger(sqrtVal)) {
|
|
32008
|
-
return new NumberNode(sqrtVal);
|
|
32009
|
-
}
|
|
32010
|
-
// Otherwise, check if we can simplify the radicand
|
|
32011
|
-
const factorized = factorizeRadicand(val);
|
|
32012
|
-
if (factorized.coefficient > 1) {
|
|
32013
|
-
// If there's a perfect square factor, extract it
|
|
32014
|
-
return new BinaryOpNode('*', new NumberNode(factorized.coefficient), new RootNode(new NumberNode(factorized.radicand)));
|
|
32015
|
-
}
|
|
32016
|
-
}
|
|
32017
|
-
// Handle product of radicands under a root
|
|
32018
|
-
if (isBinaryOpNode(operand) && operand.getOp() === '*') {
|
|
32019
|
-
const left = operand.getLeft();
|
|
32020
|
-
const right = operand.getRight();
|
|
32021
|
-
// We can't simplify if they aren't both numbers
|
|
32022
|
-
if (isNumberNode(left) && isNumberNode(right)) {
|
|
32023
|
-
const leftVal = left.evaluate();
|
|
32024
|
-
const rightVal = right.evaluate();
|
|
32025
|
-
// Try to factorize the product
|
|
32026
|
-
const factorized = factorizeRadicand(leftVal * rightVal);
|
|
32027
|
-
if (factorized.coefficient > 1) {
|
|
32028
|
-
// If there's a perfect square factor, extract it
|
|
32029
|
-
return new BinaryOpNode('*', new NumberNode(factorized.coefficient), new RootNode(new NumberNode(factorized.radicand))).simplify();
|
|
32030
|
-
}
|
|
32031
|
-
}
|
|
32032
|
-
}
|
|
32033
|
-
return new RootNode(operand);
|
|
32034
|
-
}
|
|
32035
|
-
getOperand() {
|
|
32036
|
-
return this.operand;
|
|
32037
|
-
}
|
|
32038
|
-
equals(other) {
|
|
32039
|
-
if (!(isRootLikeNode(other)))
|
|
32040
|
-
return false;
|
|
32041
|
-
return this.operand.equals(other.getOperand());
|
|
32042
|
-
}
|
|
32043
|
-
}
|
|
32044
|
-
/**
|
|
32045
|
-
* Specialized node for power expressions
|
|
32046
|
-
*/
|
|
32047
|
-
class PowerNode extends ExprNode {
|
|
32048
|
-
constructor(base, exponent) {
|
|
32049
|
-
super();
|
|
32050
|
-
Object.defineProperty(this, "base", {
|
|
32051
|
-
enumerable: true,
|
|
32052
|
-
configurable: true,
|
|
32053
|
-
writable: true,
|
|
32054
|
-
value: base
|
|
32055
|
-
});
|
|
32056
|
-
Object.defineProperty(this, "exponent", {
|
|
32057
|
-
enumerable: true,
|
|
32058
|
-
configurable: true,
|
|
32059
|
-
writable: true,
|
|
32060
|
-
value: exponent
|
|
32061
|
-
});
|
|
32062
|
-
Object.defineProperty(this, "type", {
|
|
32063
|
-
enumerable: true,
|
|
32064
|
-
configurable: true,
|
|
32065
|
-
writable: true,
|
|
32066
|
-
value: 'Power'
|
|
32067
|
-
});
|
|
32068
|
-
}
|
|
32069
|
-
evaluate() {
|
|
32070
|
-
return this.base.evaluate() ** this.exponent.evaluate();
|
|
32071
|
-
}
|
|
32072
|
-
toString() {
|
|
32073
|
-
const baseStr = this.base.toString();
|
|
32074
|
-
const expVal = this.exponent.evaluate();
|
|
32075
|
-
// Use superscript for powers 2 and 3
|
|
32076
|
-
if (expVal === 2)
|
|
32077
|
-
return `${baseStr}²`;
|
|
32078
|
-
if (expVal === 3)
|
|
32079
|
-
return `${baseStr}³`;
|
|
32080
|
-
return CONFIG.spaceSeparation ? `${baseStr} ^ ${this.exponent.toString()}` : `${baseStr}^${this.exponent.toString()}`;
|
|
32081
|
-
}
|
|
32082
|
-
simplify() {
|
|
32083
|
-
const base = this.base.simplify();
|
|
32084
|
-
const exponent = this.exponent.simplify();
|
|
32085
|
-
// Anything to the power of 0 is 1
|
|
32086
|
-
if (isNumberNode(exponent) && exponent.evaluate() === 0) {
|
|
32087
|
-
return new NumberNode(1);
|
|
32088
|
-
}
|
|
32089
|
-
// Anything to the power of 1 is itself
|
|
32090
|
-
if (isNumberNode(exponent) && exponent.evaluate() === 1) {
|
|
32091
|
-
return base;
|
|
32092
|
-
}
|
|
32093
|
-
// 0 to any positive power is 0
|
|
32094
|
-
if (isNumberNode(base) && base.evaluate() === 0
|
|
32095
|
-
&& isNumberNode(exponent) && exponent.evaluate() > 0) {
|
|
32096
|
-
return new NumberNode(0);
|
|
32097
|
-
}
|
|
32098
|
-
// 1 to any power is 1
|
|
32099
|
-
if (isNumberNode(base) && base.evaluate() === 1) {
|
|
32100
|
-
return new NumberNode(1);
|
|
32101
|
-
}
|
|
32102
|
-
// If both are numbers, compute the power
|
|
32103
|
-
if (isNumberNode(base) && isNumberNode(exponent)) {
|
|
32104
|
-
return new NumberNode(base.evaluate() ** exponent.evaluate());
|
|
32105
|
-
}
|
|
32106
|
-
return new PowerNode(base, exponent);
|
|
32107
|
-
}
|
|
32108
|
-
equals(other) {
|
|
32109
|
-
if (!(isPowerNode(other)))
|
|
32110
|
-
return false;
|
|
32111
|
-
const otherPower = other;
|
|
32112
|
-
return this.base.equals(otherPower.base)
|
|
32113
|
-
&& this.exponent.equals(otherPower.exponent);
|
|
32114
|
-
}
|
|
32115
|
-
getBase() {
|
|
32116
|
-
return this.base;
|
|
32117
|
-
}
|
|
32118
|
-
getExponent() {
|
|
32119
|
-
return this.exponent;
|
|
32120
|
-
}
|
|
32121
|
-
}
|
|
32122
|
-
/**
|
|
32123
|
-
* Node for unary operations (-, sqrt, cbrt, etc.)
|
|
32124
|
-
*/
|
|
32125
|
-
class UnaryOpNode extends ExprNode {
|
|
32126
|
-
constructor(op, operand) {
|
|
32127
|
-
super();
|
|
32128
|
-
Object.defineProperty(this, "op", {
|
|
32129
|
-
enumerable: true,
|
|
32130
|
-
configurable: true,
|
|
32131
|
-
writable: true,
|
|
32132
|
-
value: op
|
|
32133
|
-
});
|
|
32134
|
-
Object.defineProperty(this, "operand", {
|
|
32135
|
-
enumerable: true,
|
|
32136
|
-
configurable: true,
|
|
32137
|
-
writable: true,
|
|
32138
|
-
value: operand
|
|
32139
|
-
});
|
|
32140
|
-
Object.defineProperty(this, "type", {
|
|
32141
|
-
enumerable: true,
|
|
32142
|
-
configurable: true,
|
|
32143
|
-
writable: true,
|
|
32144
|
-
value: 'UnaryOp'
|
|
32145
|
-
});
|
|
32146
|
-
}
|
|
32147
|
-
evaluate() {
|
|
32148
|
-
const val = this.operand.evaluate();
|
|
32149
|
-
switch (this.op) {
|
|
32150
|
-
case '-': return -val;
|
|
32151
|
-
case 'sqrt': return Math.sqrt(val);
|
|
32152
|
-
case 'cbrt': return Math.cbrt(val);
|
|
32153
|
-
}
|
|
32154
|
-
}
|
|
32155
|
-
toString() {
|
|
32156
|
-
const operandStr = this.operand.toString();
|
|
32157
|
-
if (operandStr === '0') {
|
|
32158
|
-
return '0';
|
|
32159
|
-
}
|
|
32160
|
-
switch (this.op) {
|
|
32161
|
-
case '-': return CONFIG.spaceSeparation && (!isNumberNode(this.operand) || operandStr.includes('/')) ? `-(${operandStr})` : `-${operandStr}`;
|
|
32162
|
-
case 'sqrt': return CONFIG.spaceSeparation ? `√(${operandStr})` : `√${operandStr}`;
|
|
32163
|
-
case 'cbrt': return CONFIG.spaceSeparation ? `∛(${operandStr})` : `∛${operandStr}`;
|
|
32164
|
-
}
|
|
32165
|
-
}
|
|
32166
|
-
simplify() {
|
|
32167
|
-
const operand = this.operand.simplify();
|
|
32168
|
-
// Double negation: --a = a
|
|
32169
|
-
if (this.op === '-' && isUnaryOpNode(operand)
|
|
32170
|
-
&& operand.getOp() === '-') {
|
|
32171
|
-
return operand.getOperand();
|
|
32172
|
-
}
|
|
32173
|
-
// Simplify sqrt of perfect squares
|
|
32174
|
-
if (this.op === 'sqrt' && isNumberNode(operand)) {
|
|
32175
|
-
const val = operand.evaluate();
|
|
32176
|
-
const sqrtVal = Math.sqrt(val);
|
|
32177
|
-
if (Number.isInteger(sqrtVal)) {
|
|
32178
|
-
return new NumberNode(sqrtVal);
|
|
32179
|
-
}
|
|
32180
|
-
}
|
|
32181
|
-
// Simplify cbrt of perfect cubes
|
|
32182
|
-
if (this.op === 'cbrt' && isNumberNode(operand)) {
|
|
32183
|
-
const val = operand.evaluate();
|
|
32184
|
-
const cbrtVal = Math.cbrt(val);
|
|
32185
|
-
if (Number.isInteger(cbrtVal)) {
|
|
32186
|
-
return new NumberNode(cbrtVal);
|
|
32187
|
-
}
|
|
32188
|
-
}
|
|
32189
|
-
return new UnaryOpNode(this.op, operand);
|
|
32190
|
-
}
|
|
32191
|
-
getOp() {
|
|
32192
|
-
return this.op;
|
|
32193
|
-
}
|
|
32194
|
-
getOperand() {
|
|
32195
|
-
return this.operand;
|
|
32196
|
-
}
|
|
32197
|
-
equals(other) {
|
|
32198
|
-
if (!(isUnaryOpNode(other)))
|
|
32199
|
-
return false;
|
|
32200
|
-
const otherOp = other;
|
|
32201
|
-
return this.op === otherOp.getOp()
|
|
32202
|
-
&& this.operand.equals(otherOp.getOperand());
|
|
32203
|
-
}
|
|
32204
|
-
}
|
|
32205
|
-
|
|
32206
|
-
/**
|
|
32207
|
-
* Node for mathematical constants (π, e, etc.)
|
|
32208
|
-
*/
|
|
32209
|
-
class ConstantNode extends ExprNode {
|
|
32210
|
-
constructor(symbol, value) {
|
|
32211
|
-
super();
|
|
32212
|
-
Object.defineProperty(this, "symbol", {
|
|
32213
|
-
enumerable: true,
|
|
32214
|
-
configurable: true,
|
|
32215
|
-
writable: true,
|
|
32216
|
-
value: symbol
|
|
32217
|
-
});
|
|
32218
|
-
Object.defineProperty(this, "value", {
|
|
32219
|
-
enumerable: true,
|
|
32220
|
-
configurable: true,
|
|
32221
|
-
writable: true,
|
|
32222
|
-
value: value
|
|
32223
|
-
});
|
|
32224
|
-
Object.defineProperty(this, "type", {
|
|
32225
|
-
enumerable: true,
|
|
32226
|
-
configurable: true,
|
|
32227
|
-
writable: true,
|
|
32228
|
-
value: 'Constant'
|
|
32229
|
-
});
|
|
32230
|
-
}
|
|
32231
|
-
evaluate() {
|
|
32232
|
-
return this.value;
|
|
32233
|
-
}
|
|
32234
|
-
toString() {
|
|
32235
|
-
return this.symbol;
|
|
32236
|
-
}
|
|
32237
|
-
simplify() {
|
|
32238
|
-
return this; // Constants are already in simplest form
|
|
32239
|
-
}
|
|
32240
|
-
equals(other) {
|
|
32241
|
-
if (!(isConstantNode(other)))
|
|
32242
|
-
return false;
|
|
32243
|
-
return this.symbol === other.symbol;
|
|
32244
|
-
}
|
|
32245
|
-
getSymbol() {
|
|
32246
|
-
return this.symbol;
|
|
32247
|
-
}
|
|
32248
|
-
}
|
|
32249
|
-
|
|
32250
|
-
/**
|
|
32251
|
-
* Converts a number to its continued fraction representation
|
|
32252
|
-
* @param x The number to convert
|
|
32253
|
-
* @param maxTerms Maximum number of terms to compute
|
|
32254
|
-
* @returns Array of continued fraction terms [a0, a1, a2, ...]
|
|
32255
|
-
*/
|
|
32256
|
-
function toContinuedFraction(x, maxTerms = 20) {
|
|
32257
|
-
const terms = [];
|
|
32258
|
-
for (let i = 0; i < maxTerms; i++) {
|
|
32259
|
-
const a = Math.floor(x);
|
|
32260
|
-
terms.push(a);
|
|
32261
|
-
// If we've reached a very small remainder, we're done
|
|
32262
|
-
if (Math.abs(x - a) < CONFIG.epsilon) {
|
|
32263
|
-
break;
|
|
32264
|
-
}
|
|
32265
|
-
// Prevent division by zero and avoid floating point issues
|
|
32266
|
-
const remainder = x - a;
|
|
32267
|
-
if (Math.abs(remainder) < CONFIG.epsilon) {
|
|
32268
|
-
break;
|
|
32269
|
-
}
|
|
32270
|
-
x = 1 / remainder;
|
|
32271
|
-
}
|
|
32272
|
-
return terms;
|
|
32273
|
-
}
|
|
32274
|
-
/**
|
|
32275
|
-
* Converts continued fraction terms to a fraction [numerator, denominator]
|
|
32276
|
-
* @param terms Array of continued fraction terms
|
|
32277
|
-
* @returns [numerator, denominator]
|
|
32278
|
-
*/
|
|
32279
|
-
function fromContinuedFraction(terms) {
|
|
32280
|
-
if (terms.length === 0)
|
|
32281
|
-
return [0, 1];
|
|
32282
|
-
if (terms.length === 1)
|
|
32283
|
-
return [terms[0], 1];
|
|
32284
|
-
// Start with the last term
|
|
32285
|
-
let numerator = terms[terms.length - 1];
|
|
32286
|
-
let denominator = 1;
|
|
32287
|
-
// Work backwards through the terms
|
|
32288
|
-
for (let i = terms.length - 2; i >= 0; i--) {
|
|
32289
|
-
// For each term, compute the new fraction
|
|
32290
|
-
// a_i + 1/previous
|
|
32291
|
-
const newNumerator = terms[i] * numerator + denominator;
|
|
32292
|
-
const newDenominator = numerator;
|
|
32293
|
-
// Update for next iteration
|
|
32294
|
-
numerator = newNumerator;
|
|
32295
|
-
denominator = newDenominator;
|
|
32296
|
-
}
|
|
32297
|
-
return [numerator, denominator];
|
|
32298
|
-
}
|
|
32299
|
-
/**
|
|
32300
|
-
* Generates all convergents (approximations) from continued fraction terms
|
|
32301
|
-
* @param terms Array of continued fraction terms
|
|
32302
|
-
* @returns Array of [numerator, denominator] pairs
|
|
32303
|
-
*/
|
|
32304
|
-
function getConvergents(terms) {
|
|
32305
|
-
const convergents = [];
|
|
32306
|
-
for (let i = 1; i <= terms.length; i++) {
|
|
32307
|
-
convergents.push(fromContinuedFraction(terms.slice(0, i)));
|
|
32308
|
-
}
|
|
32309
|
-
return convergents;
|
|
32310
|
-
}
|
|
32311
|
-
/**
|
|
32312
|
-
* Tries to identify if a number is a simple quadratic irrational
|
|
32313
|
-
* @param x The number to check
|
|
32314
|
-
* @returns The identified form or null if not recognized
|
|
32315
|
-
*/
|
|
32316
|
-
function identifyQuadraticIrrational(x) {
|
|
32317
|
-
// Direct checks for common values
|
|
32318
|
-
// Check for square roots of integers
|
|
32319
|
-
for (let n = 2; n <= 20; n++) {
|
|
32320
|
-
if (Math.abs(x - Math.sqrt(n)) < CONFIG.epsilon) {
|
|
32321
|
-
return `√${n}`;
|
|
32322
|
-
}
|
|
32323
|
-
}
|
|
32324
|
-
// Check for golden ratio and its conjugate
|
|
32325
|
-
if (Math.abs(x - (1 + Math.sqrt(5)) / 2) < CONFIG.epsilon) {
|
|
32326
|
-
return "φ"; // Already defined as a constant
|
|
32327
|
-
}
|
|
32328
|
-
if (Math.abs(x - (Math.sqrt(5) - 1) / 2) < CONFIG.epsilon) {
|
|
32329
|
-
return "(√5-1)/2";
|
|
32330
|
-
}
|
|
32331
|
-
// Check for expressions of form (√n ± m)/k where n, m, k are small integers
|
|
32332
|
-
for (let n = 2; n <= 30; n++) {
|
|
32333
|
-
const sqrtN = Math.sqrt(n);
|
|
32334
|
-
for (let m = 1; m <= 10; m++) {
|
|
32335
|
-
for (let k = 2; k <= 10; k++) {
|
|
32336
|
-
// Check (√n + m)/k
|
|
32337
|
-
if (Math.abs(x - (sqrtN + m) / k) < CONFIG.epsilon) {
|
|
32338
|
-
if (m === 1 && k === 2) {
|
|
32339
|
-
return `(√${n}+1)/2`;
|
|
32340
|
-
}
|
|
32341
|
-
else {
|
|
32342
|
-
return `(√${n}+${m})/${k}`;
|
|
32343
|
-
}
|
|
32344
|
-
}
|
|
32345
|
-
// Check (√n - m)/k
|
|
32346
|
-
if (Math.abs(x - (sqrtN - m) / k) < CONFIG.epsilon) {
|
|
32347
|
-
if (m === 1 && k === 2) {
|
|
32348
|
-
return `(√${n}-1)/2`;
|
|
32349
|
-
}
|
|
32350
|
-
else {
|
|
32351
|
-
return `(√${n}-${m})/${k}`;
|
|
32352
|
-
}
|
|
32353
|
-
}
|
|
32354
|
-
}
|
|
32355
|
-
}
|
|
32356
|
-
}
|
|
32357
|
-
// Get continued fraction for pattern analysis
|
|
32358
|
-
const terms = toContinuedFraction(x, 30);
|
|
32359
|
-
// Analyze the continued fraction for periodic patterns
|
|
32360
|
-
const period = detectPeriod(terms);
|
|
32361
|
-
if (period) {
|
|
32362
|
-
// For purely periodic CFs (√D): [a0; a1, a2, ..., aN, a1, a2, ...]
|
|
32363
|
-
// where a0 = floor(√D) and the sequence after a0 is periodic
|
|
32364
|
-
// For √2: [1; 2, 2, 2, ...]
|
|
32365
|
-
if (period.length === 1 && period[0] === 2 && terms[0] === 1) {
|
|
32366
|
-
return "√2";
|
|
32367
|
-
}
|
|
32368
|
-
// For √3: [1; 1, 2, 1, 2, ...]
|
|
32369
|
-
if (period.length === 2 && period[0] === 1 && period[1] === 2 && terms[0] === 1) {
|
|
32370
|
-
return "√3";
|
|
32371
|
-
}
|
|
32372
|
-
// For √5: [2; 4, 4, 4, ...]
|
|
32373
|
-
if (period.length === 1 && period[0] === 4 && terms[0] === 2) {
|
|
32374
|
-
return "√5";
|
|
32375
|
-
}
|
|
32376
|
-
// For √6: [2; 2, 4, 2, 4, ...]
|
|
32377
|
-
if (period.length === 2 && period[0] === 2 && period[1] === 4 && terms[0] === 2) {
|
|
32378
|
-
return "√6";
|
|
32379
|
-
}
|
|
32380
|
-
// For √7: [2; 1, 1, 1, 4, 1, 1, 1, 4, ...]
|
|
32381
|
-
if (period.length === 4 &&
|
|
32382
|
-
period[0] === 1 && period[1] === 1 &&
|
|
32383
|
-
period[2] === 1 && period[3] === 4 &&
|
|
32384
|
-
terms[0] === 2) {
|
|
32385
|
-
return "√7";
|
|
32386
|
-
}
|
|
32387
|
-
// For √8: [2; 1, 4, 1, 4, ...]
|
|
32388
|
-
if (period.length === 2 && period[0] === 1 && period[1] === 4 && terms[0] === 2) {
|
|
32389
|
-
return "√8";
|
|
32390
|
-
}
|
|
32391
|
-
// Try to reconstruct the quadratic form from the pattern
|
|
32392
|
-
// (Complex algorithm that would determine D from periodic CF)
|
|
32393
|
-
const D = reconstructD(terms[0], period);
|
|
32394
|
-
if (D > 0 && Math.abs(x - Math.sqrt(D)) < CONFIG.epsilon) {
|
|
32395
|
-
return `√${D}`;
|
|
32396
|
-
}
|
|
32397
|
-
}
|
|
32398
|
-
return null; // Could not identify
|
|
32399
|
-
}
|
|
32400
|
-
// Helper function to detect periodic patterns in continued fractions
|
|
32401
|
-
function detectPeriod(terms) {
|
|
32402
|
-
// Need at least a few terms to detect patterns
|
|
32403
|
-
if (terms.length < 6)
|
|
32404
|
-
return null;
|
|
32405
|
-
// Check for single repeating digit (like √2 = [1; 2, 2, 2, ...])
|
|
32406
|
-
const allSameAfterFirst = terms.slice(1).every(t => t === terms[1]);
|
|
32407
|
-
if (allSameAfterFirst) {
|
|
32408
|
-
return [terms[1]];
|
|
32409
|
-
}
|
|
32410
|
-
// For period of length 2
|
|
32411
|
-
const periodLength2 = terms.slice(1, 3).every((val, idx) => {
|
|
32412
|
-
for (let i = 1; i < Math.floor((terms.length - 1) / 2); i++) {
|
|
32413
|
-
if (terms[1 + idx + 2 * i] !== val)
|
|
32414
|
-
return false;
|
|
32415
|
-
}
|
|
32416
|
-
return true;
|
|
32417
|
-
});
|
|
32418
|
-
if (periodLength2) {
|
|
32419
|
-
return terms.slice(1, 3);
|
|
32420
|
-
}
|
|
32421
|
-
// For period of length 4
|
|
32422
|
-
if (terms.length >= 9) {
|
|
32423
|
-
const periodLength4 = terms.slice(1, 5).every((val, idx) => {
|
|
32424
|
-
return terms[1 + idx + 4] === val && (terms.length >= 13 ? terms[1 + idx + 8] === val : true);
|
|
32425
|
-
});
|
|
32426
|
-
if (periodLength4) {
|
|
32427
|
-
return terms.slice(1, 5);
|
|
32428
|
-
}
|
|
32429
|
-
}
|
|
32430
|
-
// Could add more pattern detection logic here
|
|
32431
|
-
return null;
|
|
32432
|
-
}
|
|
32433
|
-
// Helper function to reconstruct D from continued fraction pattern
|
|
32434
|
-
function reconstructD(a0, period) {
|
|
32435
|
-
// Simple cases
|
|
32436
|
-
if (period.length === 1 && period[0] === 2)
|
|
32437
|
-
return 2;
|
|
32438
|
-
if (period.length === 2 && period[0] === 1 && period[1] === 2)
|
|
32439
|
-
return 3;
|
|
32440
|
-
if (period.length === 1 && period[0] === 4)
|
|
32441
|
-
return 5;
|
|
32442
|
-
if (period.length === 2 && period[0] === 2 && period[1] === 4)
|
|
32443
|
-
return 6;
|
|
32444
|
-
if (period.length === 4 &&
|
|
32445
|
-
period[0] === 1 && period[1] === 1 &&
|
|
32446
|
-
period[2] === 1 && period[3] === 4)
|
|
32447
|
-
return 7;
|
|
32448
|
-
if (period.length === 2 && period[0] === 1 && period[1] === 4)
|
|
32449
|
-
return 8;
|
|
32450
|
-
// For purely periodic CFs, D = a0² + P where P depends on the period
|
|
32451
|
-
// This is a simplified heuristic approach
|
|
32452
|
-
if (period.length === 1) {
|
|
32453
|
-
// If period has form [n], then D ≈ a0² + 1/n
|
|
32454
|
-
return Math.round(a0 * a0 + 1 / period[0]);
|
|
32455
|
-
}
|
|
32456
|
-
// Generalized approach for reconstructing D would be more complex
|
|
32457
|
-
// and would involve solving the Pell equation
|
|
32458
|
-
return -1; // Couldn't determine D
|
|
32459
|
-
}
|
|
32460
|
-
|
|
32461
|
-
/**
|
|
32462
|
-
* Parser to convert numeric values to expression trees
|
|
32463
|
-
*/
|
|
32464
|
-
class ExpressionParser {
|
|
32465
|
-
/**
|
|
32466
|
-
* Main parsing function that converts a number to an expression tree
|
|
32467
|
-
*/
|
|
32468
|
-
parseNumber(num, depth = 0) {
|
|
32469
|
-
const MAX_DEPTH = 3;
|
|
32470
|
-
// Handle zero
|
|
32471
|
-
if (Math.abs(num) < CONFIG.epsilon) {
|
|
32472
|
-
return new NumberNode(0);
|
|
32473
|
-
}
|
|
32474
|
-
// Handle negative numbers
|
|
32475
|
-
if (num < 0) {
|
|
32476
|
-
return new UnaryOpNode('-', this.parseNumber(-num, depth));
|
|
32477
|
-
}
|
|
32478
|
-
// Handle integers
|
|
32479
|
-
if (Math.abs(num - Math.round(num)) < CONFIG.epsilon) {
|
|
32480
|
-
return new NumberNode(Math.round(num));
|
|
32481
|
-
}
|
|
32482
|
-
const cfTerms = toContinuedFraction(num, 20);
|
|
32483
|
-
const convergents = getConvergents(cfTerms);
|
|
32484
|
-
for (const [numerator, denominator] of convergents) {
|
|
32485
|
-
// Only consider fractions with reasonably-sized components
|
|
32486
|
-
if (denominator <= 1000 && Math.abs(numerator) <= 10000) {
|
|
32487
|
-
// Verify the approximation is within our epsilon
|
|
32488
|
-
if (Math.abs(num - numerator / denominator) < CONFIG.epsilon) {
|
|
32489
|
-
const gcd = this.findGCD(Math.abs(numerator), denominator);
|
|
32490
|
-
return new BinaryOpNode('/', new NumberNode(numerator / gcd), new NumberNode(denominator / gcd));
|
|
32491
|
-
}
|
|
32492
|
-
}
|
|
32493
|
-
}
|
|
32494
|
-
// Try direct matches to constants
|
|
32495
|
-
for (const constant of CONSTANTS) {
|
|
32496
|
-
if (Math.abs(num - constant.value) < CONFIG.epsilon) {
|
|
32497
|
-
return new ConstantNode(constant.getSymbol(), constant.value);
|
|
32498
|
-
}
|
|
32499
|
-
}
|
|
32500
|
-
// Try direct matches to trig values
|
|
32501
|
-
for (const trigValue of TRIG_VALUES) {
|
|
32502
|
-
if (Math.abs(num - trigValue.value) < CONFIG.epsilon) {
|
|
32503
|
-
// If it has an exact form, parse that instead
|
|
32504
|
-
if (trigValue.getExactForm) {
|
|
32505
|
-
return this.parseExactForm(trigValue.getExactForm());
|
|
32506
|
-
}
|
|
32507
|
-
return new ConstantNode(trigValue.getSymbol(), trigValue.value);
|
|
32508
|
-
}
|
|
32509
|
-
}
|
|
32510
|
-
const quadraticForm = identifyQuadraticIrrational(num);
|
|
32511
|
-
if (quadraticForm) {
|
|
32512
|
-
// If we identified something like "√7" or "(√13-3)/2"
|
|
32513
|
-
return this.parseExactForm(quadraticForm);
|
|
32514
|
-
}
|
|
32515
|
-
// Direct check for square roots before other decompositions
|
|
32516
|
-
// This ensures square roots of integers are handled directly
|
|
32517
|
-
for (let i = 2; i <= 100; i++) {
|
|
32518
|
-
if (Math.abs(num - Math.sqrt(i)) < CONFIG.epsilon) {
|
|
32519
|
-
// Use our enhanced check to determine if we should keep direct form
|
|
32520
|
-
if (shouldPreserveDirectRadical(i)) {
|
|
32521
|
-
return new RootNode(new NumberNode(i));
|
|
32522
|
-
}
|
|
32523
|
-
// Only factorize if appropriate
|
|
32524
|
-
const factorized = factorizeRadicand(i);
|
|
32525
|
-
if (factorized.coefficient > 1) {
|
|
32526
|
-
return new BinaryOpNode('*', new NumberNode(factorized.coefficient), new RootNode(new NumberNode(factorized.radicand)));
|
|
32527
|
-
}
|
|
32528
|
-
// Default to direct representation
|
|
32529
|
-
return new RootNode(new NumberNode(i));
|
|
32530
|
-
}
|
|
32531
|
-
}
|
|
32532
|
-
// Check for powers
|
|
32533
|
-
for (const constant of [...CONSTANTS, ...TRIG_VALUES]) {
|
|
32534
|
-
for (let power = 2; power <= 3; power++) {
|
|
32535
|
-
if (Math.abs(num - constant.value ** power) < CONFIG.epsilon) {
|
|
32536
|
-
const baseNode = constant.getExactForm
|
|
32537
|
-
? this.parseExactForm(constant.getExactForm())
|
|
32538
|
-
: new ConstantNode(constant.getSymbol(), constant.value);
|
|
32539
|
-
return new PowerNode(baseNode, new NumberNode(power));
|
|
32540
|
-
}
|
|
32541
|
-
}
|
|
32542
|
-
}
|
|
32543
|
-
// Recursive decomposition with extra care for square roots
|
|
32544
|
-
if (depth < MAX_DEPTH) {
|
|
32545
|
-
// Check for π/n pattern
|
|
32546
|
-
for (const constant of CONSTANTS) {
|
|
32547
|
-
for (let denominator = 2; denominator <= 12; denominator++) {
|
|
32548
|
-
if (Math.abs(num - constant.value / denominator) < CONFIG.epsilon) {
|
|
32549
|
-
return new BinaryOpNode('/', constant.getExactForm
|
|
32550
|
-
? this.parseExactForm(constant.getExactForm())
|
|
32551
|
-
: new ConstantNode(constant.getSymbol(), constant.value), new NumberNode(denominator));
|
|
32552
|
-
}
|
|
32553
|
-
}
|
|
32554
|
-
}
|
|
32555
|
-
// Check for products with more careful handling of square roots
|
|
32556
|
-
for (const constant of [...CONSTANTS, ...TRIG_VALUES]) {
|
|
32557
|
-
if (Math.abs(constant.value) > CONFIG.epsilon) {
|
|
32558
|
-
const multiplier = num / constant.value;
|
|
32559
|
-
// Skip square root decomposition attempts for prime square roots
|
|
32560
|
-
if (constant.getSymbol() && constant.getSymbol().startsWith('√')
|
|
32561
|
-
&& isPrime(Number.parseInt(constant.getSymbol(true).substring(1)))) {
|
|
32562
|
-
continue;
|
|
32563
|
-
}
|
|
32564
|
-
// Skip common fractions that might be misidentified as complex expressions
|
|
32565
|
-
if ((Math.abs(multiplier - 0.75) < CONFIG.epsilon)
|
|
32566
|
-
|| (Math.abs(multiplier - 0.5) < CONFIG.epsilon)
|
|
32567
|
-
|| (Math.abs(multiplier - 0.25) < CONFIG.epsilon)
|
|
32568
|
-
|| (Math.abs(multiplier - 0.3333333333333333) < CONFIG.epsilon)
|
|
32569
|
-
|| (Math.abs(multiplier - 0.6666666666666666) < CONFIG.epsilon)) {
|
|
32570
|
-
continue;
|
|
32571
|
-
}
|
|
32572
|
-
if (this.isNiceValue(multiplier)) {
|
|
32573
|
-
const multiplierNode = this.parseNumber(multiplier, depth + 1);
|
|
32574
|
-
const valueNode = constant.getExactForm
|
|
32575
|
-
? this.parseExactForm(constant.getExactForm())
|
|
32576
|
-
: new ConstantNode(constant.getSymbol(), constant.value);
|
|
32577
|
-
// If multiplier is 1, just return the constant
|
|
32578
|
-
if (isNumberNode(multiplierNode)
|
|
32579
|
-
&& Math.abs(multiplierNode.evaluate() - 1) < CONFIG.epsilon) {
|
|
32580
|
-
return valueNode;
|
|
32581
|
-
}
|
|
32582
|
-
// Create the product node with canonical ordering
|
|
32583
|
-
let productNode;
|
|
32584
|
-
// Define a priority order for constants
|
|
32585
|
-
// Lower number = higher priority (should appear first)
|
|
32586
|
-
const getConstantPriority = (node) => {
|
|
32587
|
-
if (!(isConstantNode(node)))
|
|
32588
|
-
return 100;
|
|
32589
|
-
const symbol = node.getSymbol();
|
|
32590
|
-
if (symbol === 'e')
|
|
32591
|
-
return 1;
|
|
32592
|
-
if (symbol === 'π')
|
|
32593
|
-
return 2;
|
|
32594
|
-
if (symbol === 'φ')
|
|
32595
|
-
return 3;
|
|
32596
|
-
if (symbol.startsWith('√'))
|
|
32597
|
-
return 10;
|
|
32598
|
-
return 50; // Other constants
|
|
32599
|
-
};
|
|
32600
|
-
// Apply the priority ordering
|
|
32601
|
-
if (isConstantNode(multiplierNode) && isConstantNode(valueNode)) {
|
|
32602
|
-
// When multiplying two constants, order them by priority
|
|
32603
|
-
const multiplierPriority = getConstantPriority(multiplierNode);
|
|
32604
|
-
const valuePriority = getConstantPriority(valueNode);
|
|
32605
|
-
if (multiplierPriority <= valuePriority) {
|
|
32606
|
-
productNode = new BinaryOpNode('*', multiplierNode, valueNode);
|
|
32607
|
-
}
|
|
32608
|
-
else {
|
|
32609
|
-
productNode = new BinaryOpNode('*', valueNode, multiplierNode);
|
|
32610
|
-
}
|
|
32611
|
-
}
|
|
32612
|
-
else if (isNumberNode(multiplierNode)) {
|
|
32613
|
-
// Number multiplier always goes first
|
|
32614
|
-
productNode = new BinaryOpNode('*', multiplierNode, valueNode);
|
|
32615
|
-
}
|
|
32616
|
-
else if (getConstantPriority(valueNode) < 50) {
|
|
32617
|
-
// Special constants (e, π, φ) go before other expressions
|
|
32618
|
-
productNode = new BinaryOpNode('*', valueNode, multiplierNode);
|
|
32619
|
-
}
|
|
32620
|
-
else {
|
|
32621
|
-
// Default ordering
|
|
32622
|
-
productNode = new BinaryOpNode('*', multiplierNode, valueNode);
|
|
32623
|
-
}
|
|
32624
|
-
// Check if this product is part of a sum
|
|
32625
|
-
// Try to see if the product plus a nice value equals our number
|
|
32626
|
-
for (let addend = 1; addend <= 10; addend++) {
|
|
32627
|
-
const productValue = productNode.evaluate();
|
|
32628
|
-
const sum = productValue + addend;
|
|
32629
|
-
if (Math.abs(num - sum) < CONFIG.epsilon) {
|
|
32630
|
-
return new BinaryOpNode('+', productNode, new NumberNode(addend));
|
|
32631
|
-
}
|
|
32632
|
-
}
|
|
32633
|
-
return productNode;
|
|
32634
|
-
}
|
|
32635
|
-
}
|
|
32636
|
-
}
|
|
32637
|
-
// Check for sums and differences with base constants
|
|
32638
|
-
for (const constant of [...CONSTANTS, ...TRIG_VALUES]) {
|
|
32639
|
-
const remainder = num - constant.value;
|
|
32640
|
-
if (this.isNiceValue(remainder)) {
|
|
32641
|
-
const remainderNode = this.parseNumber(remainder, depth + 1);
|
|
32642
|
-
const valueNode = constant.getExactForm
|
|
32643
|
-
? this.parseExactForm(constant.getExactForm())
|
|
32644
|
-
: new ConstantNode(constant.getSymbol(), constant.value);
|
|
32645
|
-
// If remainder is 0, just return the constant
|
|
32646
|
-
if (isNumberNode(remainderNode)
|
|
32647
|
-
&& Math.abs(remainderNode.evaluate()) < CONFIG.epsilon) {
|
|
32648
|
-
return valueNode;
|
|
32649
|
-
}
|
|
32650
|
-
// If remainder is negative, use subtraction
|
|
32651
|
-
if (remainder < 0) {
|
|
32652
|
-
return new BinaryOpNode('-', valueNode, this.parseNumber(-remainder, depth + 1));
|
|
32653
|
-
}
|
|
32654
|
-
return new BinaryOpNode('+', valueNode, remainderNode);
|
|
32655
|
-
}
|
|
32656
|
-
}
|
|
32657
|
-
// Check for sums and differences with multiples of constants
|
|
32658
|
-
for (const constant of [...CONSTANTS, ...TRIG_VALUES]) {
|
|
32659
|
-
for (let multiplier = 2; multiplier <= 5; multiplier++) {
|
|
32660
|
-
const multipleValue = multiplier * constant.value;
|
|
32661
|
-
const remainder = num - multipleValue;
|
|
32662
|
-
if (this.isNiceValue(remainder)) {
|
|
32663
|
-
// Create the multiple constant node
|
|
32664
|
-
const constantNode = constant.getExactForm
|
|
32665
|
-
? this.parseExactForm(constant.getExactForm())
|
|
32666
|
-
: new ConstantNode(constant.getSymbol(), constant.value);
|
|
32667
|
-
const multipleNode = new BinaryOpNode('*', new NumberNode(multiplier), constantNode);
|
|
32668
|
-
// Create the remainder node
|
|
32669
|
-
const remainderNode = this.parseNumber(remainder, depth + 1);
|
|
32670
|
-
// If remainder is 0, just return the multiple
|
|
32671
|
-
if (isNumberNode(remainderNode)
|
|
32672
|
-
&& Math.abs(remainderNode.evaluate()) < CONFIG.epsilon) {
|
|
32673
|
-
return multipleNode;
|
|
32674
|
-
}
|
|
32675
|
-
// If remainder is negative, use subtraction
|
|
32676
|
-
if (remainder < 0) {
|
|
32677
|
-
return new BinaryOpNode('-', multipleNode, this.parseNumber(-remainder, depth + 1));
|
|
32678
|
-
}
|
|
32679
|
-
return new BinaryOpNode('+', multipleNode, remainderNode);
|
|
32680
|
-
}
|
|
32681
|
-
}
|
|
32682
|
-
}
|
|
32683
|
-
// Check for cube roots of integers
|
|
32684
|
-
for (let i = 2; i <= 100; i++) {
|
|
32685
|
-
if (Math.abs(num - Math.cbrt(i)) < CONFIG.epsilon) {
|
|
32686
|
-
return new UnaryOpNode('cbrt', new NumberNode(i));
|
|
32687
|
-
}
|
|
32688
|
-
}
|
|
32689
|
-
}
|
|
32690
|
-
// Final direct check for square roots before giving up
|
|
32691
|
-
// This ensures we catch any square roots we might have missed
|
|
32692
|
-
const possibleRadicand = Math.round(num * num);
|
|
32693
|
-
if (Math.abs(num - Math.sqrt(possibleRadicand)) < CONFIG.epsilon) {
|
|
32694
|
-
return new RootNode(new NumberNode(possibleRadicand));
|
|
32695
|
-
}
|
|
32696
|
-
// Fallback to decimal representation
|
|
32697
|
-
return new NumberNode(num);
|
|
32698
|
-
}
|
|
32699
|
-
/**
|
|
32700
|
-
* Helper method to parse exact forms like "√2/2"
|
|
32701
|
-
*/
|
|
32702
|
-
parseExactForm(exactForm) {
|
|
32703
|
-
// Handle fractions
|
|
32704
|
-
if (exactForm.includes('/')) {
|
|
32705
|
-
const [numerator, denominator] = exactForm.split('/');
|
|
32706
|
-
return new BinaryOpNode('/', this.parseExactForm(numerator), this.parseExactForm(denominator));
|
|
32707
|
-
}
|
|
32708
|
-
// Handle square roots
|
|
32709
|
-
if (exactForm.startsWith('√')) {
|
|
32710
|
-
const argument = exactForm.substring(1);
|
|
32711
|
-
return new RootNode(this.parseExactForm(argument));
|
|
32712
|
-
}
|
|
32713
|
-
// Handle simple numbers
|
|
32714
|
-
if (/^\d+$/.test(exactForm)) {
|
|
32715
|
-
return new NumberNode(Number.parseInt(exactForm));
|
|
32716
|
-
}
|
|
32717
|
-
// Handle known constants
|
|
32718
|
-
for (const constant of CONSTANTS) {
|
|
32719
|
-
if (exactForm === constant.getSymbol()) {
|
|
32720
|
-
return new ConstantNode(constant.getSymbol(), constant.value);
|
|
32721
|
-
}
|
|
32722
|
-
}
|
|
32723
|
-
// If we don't recognize it, treat it as a symbol
|
|
32724
|
-
return new ConstantNode(exactForm, Number.NaN);
|
|
32725
|
-
}
|
|
32726
|
-
/**
|
|
32727
|
-
* Helper to determine if a value is "nice"
|
|
32728
|
-
*/
|
|
32729
|
-
isNiceValue(num) {
|
|
32730
|
-
// Check if it's close to an integer
|
|
32731
|
-
if (Math.abs(num - Math.round(num)) < CONFIG.epsilon) {
|
|
32732
|
-
return true;
|
|
32733
|
-
}
|
|
32734
|
-
const cfTerms = toContinuedFraction(num, 10);
|
|
32735
|
-
const convergents = getConvergents(cfTerms);
|
|
32736
|
-
// Check if any convergent is a good approximation and reasonably simple
|
|
32737
|
-
for (const [numerator, denominator] of convergents) {
|
|
32738
|
-
// Only consider "nice" fractions (reasonable size numerator/denominator)
|
|
32739
|
-
if (denominator <= 20 && Math.abs(numerator) <= 50) {
|
|
32740
|
-
if (Math.abs(num - numerator / denominator) < CONFIG.epsilon) {
|
|
32741
|
-
return true;
|
|
32742
|
-
}
|
|
32743
|
-
}
|
|
32744
|
-
}
|
|
32745
|
-
// Check if it's a square root of a small integer (highest priority)
|
|
32746
|
-
// This helps prefer direct √n forms
|
|
32747
|
-
for (let i = 2; i <= 30; i++) {
|
|
32748
|
-
if (Math.abs(num - Math.sqrt(i)) < CONFIG.epsilon) {
|
|
32749
|
-
// Give very high priority to prime square roots
|
|
32750
|
-
if (isPrime(i)) {
|
|
32751
|
-
return true;
|
|
32752
|
-
}
|
|
32753
|
-
// Also prioritize other small square roots
|
|
32754
|
-
if (i <= 20) {
|
|
32755
|
-
return true;
|
|
32756
|
-
}
|
|
32757
|
-
}
|
|
32758
|
-
}
|
|
32759
|
-
// Check if it's close to a common constant
|
|
32760
|
-
for (const constant of CONSTANTS) {
|
|
32761
|
-
if (Math.abs(num - constant.value) < CONFIG.epsilon) {
|
|
32762
|
-
return true;
|
|
32763
|
-
}
|
|
32764
|
-
}
|
|
32765
|
-
// Check trig values
|
|
32766
|
-
for (const value of TRIG_VALUES) {
|
|
32767
|
-
if (Math.abs(num - value.value) < CONFIG.epsilon) {
|
|
32768
|
-
return true;
|
|
32769
|
-
}
|
|
32770
|
-
}
|
|
32771
|
-
// Other common values with lower priority
|
|
32772
|
-
const COMMON_VALUES = [
|
|
32773
|
-
Math.sin(Math.PI / 3),
|
|
32774
|
-
Math.cos(Math.PI / 4),
|
|
32775
|
-
];
|
|
32776
|
-
for (const value of COMMON_VALUES) {
|
|
32777
|
-
if (Math.abs(num - value) < CONFIG.epsilon) {
|
|
32778
|
-
return true;
|
|
32779
|
-
}
|
|
32780
|
-
}
|
|
32781
|
-
return false;
|
|
32782
|
-
}
|
|
32783
|
-
/**
|
|
32784
|
-
* Find greatest common divisor helper method
|
|
32785
|
-
*/
|
|
32786
|
-
findGCD(a, b) {
|
|
32787
|
-
a = Math.abs(a);
|
|
32788
|
-
b = Math.abs(b);
|
|
32789
|
-
while (b !== 0) {
|
|
32790
|
-
const temp = b;
|
|
32791
|
-
b = a % b;
|
|
32792
|
-
a = temp;
|
|
32793
|
-
}
|
|
32794
|
-
return a;
|
|
32795
|
-
}
|
|
32796
|
-
}
|
|
32797
|
-
|
|
32798
|
-
/**
|
|
32799
|
-
* Symbolic Number Printer using Expression Tree Representation
|
|
32800
|
-
*
|
|
32801
|
-
* This implementation uses a proper expression tree to represent mathematical
|
|
32802
|
-
* expressions, enabling more sophisticated simplification and consistent formatting.
|
|
32803
|
-
*/
|
|
32804
|
-
/**
|
|
32805
|
-
* Main function to convert a number to its symbolic representation
|
|
32806
|
-
* @param num The number to convert to symbolic form
|
|
32807
|
-
* @returns A string containing the symbolic representation
|
|
32808
|
-
*/
|
|
32809
|
-
function prettyPi(num, config = {}) {
|
|
32810
|
-
if (!isFinite(num)) {
|
|
32811
|
-
if (isNaN(num)) {
|
|
32812
|
-
return "NaN";
|
|
32813
|
-
}
|
|
32814
|
-
return num > 0 ? "∞" : "-∞";
|
|
32815
|
-
}
|
|
32816
|
-
setConfig(config);
|
|
32817
|
-
const parser = new ExpressionParser();
|
|
32818
|
-
const exprTree = parser.parseNumber(num);
|
|
32819
|
-
const simplified = exprTree.simplify();
|
|
32820
|
-
return simplified.toString();
|
|
32821
|
-
}
|
|
32822
|
-
|
|
32823
31325
|
function stringifyValue(value, html) {
|
|
32824
31326
|
var _a;
|
|
32825
31327
|
var gt = '>';
|
|
@@ -32837,7 +31339,7 @@ function stringifyValue(value, html) {
|
|
|
32837
31339
|
if (typeof value === 'object' && value instanceof RegExp)
|
|
32838
31340
|
return "".concat(value);
|
|
32839
31341
|
if (typeof value === 'number') {
|
|
32840
|
-
return
|
|
31342
|
+
return "".concat(value);
|
|
32841
31343
|
}
|
|
32842
31344
|
if (isRegularExpression(value))
|
|
32843
31345
|
return "/".concat(value.s, "/").concat(value.f);
|
|
@@ -32850,12 +31352,12 @@ function stringifyValue(value, html) {
|
|
|
32850
31352
|
return '[]';
|
|
32851
31353
|
if (value.length > 8) {
|
|
32852
31354
|
return "[\n ".concat(value.map(function (cell) {
|
|
32853
|
-
return
|
|
31355
|
+
return cell;
|
|
32854
31356
|
}).join(',\n '), "\n]");
|
|
32855
31357
|
}
|
|
32856
31358
|
else {
|
|
32857
31359
|
return "[".concat(value.map(function (cell) {
|
|
32858
|
-
return
|
|
31360
|
+
return cell;
|
|
32859
31361
|
}).join(', '), "]");
|
|
32860
31362
|
}
|
|
32861
31363
|
}
|
|
@@ -32891,45 +31393,11 @@ function replaceInfinities(value) {
|
|
|
32891
31393
|
}
|
|
32892
31394
|
return value;
|
|
32893
31395
|
}
|
|
32894
|
-
function prettyIfNumber(value) {
|
|
32895
|
-
if (typeof value === 'number') {
|
|
32896
|
-
return prettyPi(value);
|
|
32897
|
-
}
|
|
32898
|
-
return "".concat(value);
|
|
32899
|
-
}
|
|
32900
31396
|
function stringifyMatrix(matrix) {
|
|
32901
|
-
var padding = matrix.flat().reduce(function (max, cell) { return Math.max(max,
|
|
32902
|
-
var rows = matrix.map(function (row) { return "[".concat(row.map(function (cell) { return
|
|
31397
|
+
var padding = matrix.flat().reduce(function (max, cell) { return Math.max(max, "".concat(cell).length); }, 0) + 1;
|
|
31398
|
+
var rows = matrix.map(function (row) { return "[".concat(row.map(function (cell) { return "".concat(cell).padStart(padding); }).join(' '), " ]"); });
|
|
32903
31399
|
return rows.join('\n');
|
|
32904
31400
|
}
|
|
32905
|
-
// function isMatrix(value: unknown): value is (null | number | string | boolean)[][] {
|
|
32906
|
-
// if (!Array.isArray(value)) {
|
|
32907
|
-
// return false
|
|
32908
|
-
// }
|
|
32909
|
-
// if (!value.every(row => Array.isArray(row))) {
|
|
32910
|
-
// return false
|
|
32911
|
-
// }
|
|
32912
|
-
// let cols = -1
|
|
32913
|
-
// for (const row of value) {
|
|
32914
|
-
// if (cols === -1) {
|
|
32915
|
-
// cols = row.length
|
|
32916
|
-
// if (cols === 0) {
|
|
32917
|
-
// return false
|
|
32918
|
-
// }
|
|
32919
|
-
// }
|
|
32920
|
-
// else {
|
|
32921
|
-
// if (row.length !== cols) {
|
|
32922
|
-
// return false
|
|
32923
|
-
// }
|
|
32924
|
-
// }
|
|
32925
|
-
// for (const cell of row) {
|
|
32926
|
-
// if (typeof cell !== 'number' && typeof cell !== 'string' && typeof cell !== 'boolean' && cell !== null) {
|
|
32927
|
-
// return false
|
|
32928
|
-
// }
|
|
32929
|
-
// }
|
|
32930
|
-
// }
|
|
32931
|
-
// return true
|
|
32932
|
-
// }
|
|
32933
31401
|
function findAllOccurrences(input, pattern) {
|
|
32934
31402
|
var matches = __spreadArray([], __read(input.matchAll(pattern)), false);
|
|
32935
31403
|
return new Set(matches.map(function (match) { return match[0]; }));
|