@danielsimonjr/mathts-expression 0.10.3 → 0.11.1

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.
Files changed (48) hide show
  1. package/README.md +18 -2
  2. package/dist/compiler/compile.d.ts.map +1 -1
  3. package/dist/evaluator/evaluate.d.ts.map +1 -1
  4. package/dist/index.d.ts +7 -0
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +819 -256
  7. package/dist/limits.d.ts +18 -0
  8. package/dist/limits.d.ts.map +1 -0
  9. package/dist/node/ArrayNode.d.ts.map +1 -1
  10. package/dist/node/AssignmentNode.d.ts.map +1 -1
  11. package/dist/node/ConditionalNode.d.ts +124 -124
  12. package/dist/node/ConditionalNode.d.ts.map +1 -1
  13. package/dist/node/FunctionNode.d.ts.map +1 -1
  14. package/dist/node/IndexNode.d.ts.map +1 -1
  15. package/dist/node/Node.d.ts +15 -0
  16. package/dist/node/Node.d.ts.map +1 -1
  17. package/dist/node/ObjectNode.d.ts.map +1 -1
  18. package/dist/node/OperatorNode.d.ts +2 -2
  19. package/dist/node/OperatorNode.d.ts.map +1 -1
  20. package/dist/node/RangeNode.d.ts +70 -70
  21. package/dist/node/RangeNode.d.ts.map +1 -1
  22. package/dist/node/RelationalNode.d.ts +120 -120
  23. package/dist/node/RelationalNode.d.ts.map +1 -1
  24. package/dist/node/utils/stringSubset.d.ts +9 -2
  25. package/dist/node/utils/stringSubset.d.ts.map +1 -1
  26. package/dist/parse.d.ts.map +1 -1
  27. package/dist/scalar-builder.d.ts +4 -4
  28. package/dist/scalar-enumerate.d.ts +48 -0
  29. package/dist/scalar-enumerate.d.ts.map +1 -0
  30. package/dist/scalar-structure.d.ts +167 -0
  31. package/dist/scalar-structure.d.ts.map +1 -0
  32. package/dist/transform/and.transform.d.ts.map +1 -1
  33. package/dist/transform/bitAnd.transform.d.ts.map +1 -1
  34. package/dist/transform/bitOr.transform.d.ts.map +1 -1
  35. package/dist/transform/cumsum.transform.d.ts.map +1 -1
  36. package/dist/transform/filter.transform.d.ts +4 -4
  37. package/dist/transform/filter.transform.d.ts.map +1 -1
  38. package/dist/transform/forEach.transform.d.ts.map +1 -1
  39. package/dist/transform/mapSlices.transform.d.ts +1 -1
  40. package/dist/transform/mapSlices.transform.d.ts.map +1 -1
  41. package/dist/transform/or.transform.d.ts.map +1 -1
  42. package/dist/transform/utils/dimToZeroBase.d.ts +4 -1
  43. package/dist/transform/utils/dimToZeroBase.d.ts.map +1 -1
  44. package/dist/transform/utils/prepareTransformArgs.d.ts +23 -0
  45. package/dist/transform/utils/prepareTransformArgs.d.ts.map +1 -0
  46. package/dist/transform/utils/shortCircuitBinary.d.ts +21 -0
  47. package/dist/transform/utils/shortCircuitBinary.d.ts.map +1 -0
  48. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -465,6 +465,9 @@ const safeNativeMethods = {
465
465
  toLocaleString: true
466
466
  };
467
467
  //#endregion
468
+ //#region src/limits.ts
469
+ const MAX_EXPRESSION_LENGTH = 1e6;
470
+ //#endregion
468
471
  //#region src/parse.ts
469
472
  const name$45 = "parse";
470
473
  const createParse = /* #__PURE__ */ factory(name$45, [
@@ -489,12 +492,14 @@ const createParse = /* #__PURE__ */ factory(name$45, [
489
492
  ], ({ typed, numeric, config, AccessorNode, ArrayNode, AssignmentNode, BlockNode, ConditionalNode, ConstantNode, FunctionAssignmentNode, FunctionNode, IndexNode, ObjectNode, OperatorNode, ParenthesisNode, RangeNode, RelationalNode, SymbolNode }) => {
490
493
  const parse = typed(name$45, {
491
494
  string: function(expression) {
495
+ if (expression.length > 1e6) throw new SyntaxError(`expression exceeds ${MAX_EXPRESSION_LENGTH} characters`);
492
496
  return parseStart(expression, {});
493
497
  },
494
498
  "Array | Matrix": function(expressions) {
495
499
  return parseMultiple(expressions, {});
496
500
  },
497
501
  "string, Object": function(expression, options) {
502
+ if (expression.length > 1e6) throw new SyntaxError(`expression exceeds ${MAX_EXPRESSION_LENGTH} characters`);
498
503
  return parseStart(expression, options.nodes !== void 0 ? options.nodes : {});
499
504
  },
500
505
  "Array | Matrix, Object": parseMultiple
@@ -583,7 +588,8 @@ const createParse = /* #__PURE__ */ factory(name$45, [
583
588
  token: "",
584
589
  tokenType: 0,
585
590
  nestingLevel: 0,
586
- conditionalLevel: null
591
+ conditionalLevel: null,
592
+ recursionDepth: 0
587
593
  };
588
594
  }
589
595
  function currentString(state, length) {
@@ -731,8 +737,12 @@ const createParse = /* #__PURE__ */ factory(name$45, [
731
737
  while (state.token === "\n");
732
738
  }
733
739
  function openParams(state) {
740
+ if (state.nestingLevel >= 124) throw createSyntaxError(state, "expression exceeds nesting limit");
734
741
  state.nestingLevel++;
735
742
  }
743
+ function enterRecursion(state) {
744
+ if (++state.recursionDepth > 500) throw createSyntaxError(state, "expression exceeds nesting limit");
745
+ }
736
746
  function closeParams(state) {
737
747
  state.nestingLevel--;
738
748
  }
@@ -810,6 +820,14 @@ const createParse = /* #__PURE__ */ factory(name$45, [
810
820
  }
811
821
  }
812
822
  function parseAssignment(state) {
823
+ enterRecursion(state);
824
+ try {
825
+ return parseAssignmentRule(state);
826
+ } finally {
827
+ state.recursionDepth--;
828
+ }
829
+ }
830
+ function parseAssignmentRule(state) {
813
831
  let name;
814
832
  let args;
815
833
  let value;
@@ -1092,6 +1110,14 @@ const createParse = /* #__PURE__ */ factory(name$45, [
1092
1110
  return node;
1093
1111
  }
1094
1112
  function parseUnary(state) {
1113
+ enterRecursion(state);
1114
+ try {
1115
+ return parseUnaryRule(state);
1116
+ } finally {
1117
+ state.recursionDepth--;
1118
+ }
1119
+ }
1120
+ function parseUnaryRule(state) {
1095
1121
  let name;
1096
1122
  let params;
1097
1123
  let fn;
@@ -7140,7 +7166,7 @@ const embeddedDocs = {
7140
7166
  //#endregion
7141
7167
  //#region src/compiler/compile.ts
7142
7168
  function compile(node, mathScope) {
7143
- const evalFn = compileNode(node, mathScope, {});
7169
+ const evalFn = compileNode(node, mathScope, {}, 0);
7144
7170
  return { evaluate(userScope) {
7145
7171
  let scope;
7146
7172
  if (!userScope) scope = new ObjectWrappingMap({});
@@ -7149,23 +7175,25 @@ function compile(node, mathScope) {
7149
7175
  return evalFn(scope, {}, void 0);
7150
7176
  } };
7151
7177
  }
7152
- function compileNode(node, math, argNames) {
7178
+ function compileNode(node, math, argNames, depth) {
7179
+ if (depth >= 512) throw new Error(`expression exceeds compile depth 512`);
7180
+ const nextDepth = depth + 1;
7153
7181
  const n = node;
7154
7182
  if (n.isConstantNode) return compileConstantNode(node);
7155
7183
  if (n.isSymbolNode) return compileSymbolNode(node, math, argNames);
7156
- if (n.isOperatorNode) return compileOperatorNode(node, math, argNames);
7157
- if (n.isFunctionNode) return compileFunctionNode(node, math, argNames);
7158
- if (n.isParenthesisNode) return compileNode(n.content, math, argNames);
7159
- if (n.isArrayNode) return compileArrayNode(node, math, argNames);
7160
- if (n.isAssignmentNode) return compileAssignmentNode(node, math, argNames);
7161
- if (n.isBlockNode) return compileBlockNode(node, math, argNames);
7162
- if (n.isConditionalNode) return compileConditionalNode(node, math, argNames);
7163
- if (n.isObjectNode) return compileObjectNode(node, math, argNames);
7164
- if (n.isRangeNode) return compileRangeNode(node, math, argNames);
7165
- if (n.isRelationalNode) return compileRelationalNode(node, math, argNames);
7166
- if (n.isFunctionAssignmentNode) return compileFunctionAssignmentNode(node, math, argNames);
7167
- if (n.isAccessorNode) return compileAccessorNode(node, math, argNames);
7168
- if (n.isIndexNode) return compileIndexNode(node, math, argNames);
7184
+ if (n.isOperatorNode) return compileOperatorNode(node, math, argNames, nextDepth);
7185
+ if (n.isFunctionNode) return compileFunctionNode(node, math, argNames, nextDepth);
7186
+ if (n.isParenthesisNode) return compileNode(n.content, math, argNames, nextDepth);
7187
+ if (n.isArrayNode) return compileArrayNode(node, math, argNames, nextDepth);
7188
+ if (n.isAssignmentNode) return compileAssignmentNode(node, math, argNames, nextDepth);
7189
+ if (n.isBlockNode) return compileBlockNode(node, math, argNames, nextDepth);
7190
+ if (n.isConditionalNode) return compileConditionalNode(node, math, argNames, nextDepth);
7191
+ if (n.isObjectNode) return compileObjectNode(node, math, argNames, nextDepth);
7192
+ if (n.isRangeNode) return compileRangeNode(node, math, argNames, nextDepth);
7193
+ if (n.isRelationalNode) return compileRelationalNode(node, math, argNames, nextDepth);
7194
+ if (n.isFunctionAssignmentNode) return compileFunctionAssignmentNode(node, math, argNames, nextDepth);
7195
+ if (n.isAccessorNode) return compileAccessorNode(node, math, argNames, nextDepth);
7196
+ if (n.isIndexNode) return compileIndexNode(node, math, argNames, nextDepth);
7169
7197
  throw new Error(`Unknown node type: ${node.type || node.constructor?.name}`);
7170
7198
  }
7171
7199
  function compileConstantNode(node) {
@@ -7193,7 +7221,7 @@ function compileSymbolNode(node, math, argNames) {
7193
7221
  throw new Error(`Undefined symbol "${name}"`);
7194
7222
  };
7195
7223
  }
7196
- function compileOperatorNode(node, math, argNames) {
7224
+ function compileOperatorNode(node, math, argNames, depth) {
7197
7225
  const opNode = node;
7198
7226
  const fnName = opNode.fn;
7199
7227
  if (typeof fnName !== "string" || !(fnName in math)) throw new Error(`Function "${fnName}" missing in provided namespace "math"`);
@@ -7204,7 +7232,7 @@ function compileOperatorNode(node, math, argNames) {
7204
7232
  return fn(rawNodes, math, scope);
7205
7233
  };
7206
7234
  }
7207
- const evalArgs = opNode.args.map((arg) => compileNode(arg, math, argNames));
7235
+ const evalArgs = opNode.args.map((arg) => compileNode(arg, math, argNames, depth));
7208
7236
  if (evalArgs.length === 1) {
7209
7237
  const evalArg0 = evalArgs[0];
7210
7238
  return function evalOperatorNode(scope, args, context) {
@@ -7223,7 +7251,7 @@ function compileOperatorNode(node, math, argNames) {
7223
7251
  return fn(...values);
7224
7252
  };
7225
7253
  }
7226
- function compileFunctionNode(node, math, argNames) {
7254
+ function compileFunctionNode(node, math, argNames, depth) {
7227
7255
  const fnNodeOuter = node;
7228
7256
  const fnNode = fnNodeOuter.fn;
7229
7257
  if (fnNode && fnNode.isSymbolNode) {
@@ -7233,11 +7261,11 @@ function compileFunctionNode(node, math, argNames) {
7233
7261
  const rawNodes = fnNodeOuter.args;
7234
7262
  return function evalFunctionNodeRaw(scope) {
7235
7263
  const shadow = scope.has(name) ? scope.get(name) : void 0;
7236
- if (typeof shadow === "function" && shadow.rawArgs !== true) return shadow(...rawNodes.map((rn) => compileNode(rn, math, argNames)(scope, {}, void 0)));
7264
+ if (typeof shadow === "function" && shadow.rawArgs !== true) return shadow(...rawNodes.map((rn) => compileNode(rn, math, argNames, depth)(scope, {}, void 0)));
7237
7265
  return staticFn(rawNodes, math, scope);
7238
7266
  };
7239
7267
  }
7240
- const evalArgs = fnNodeOuter.args.map((arg) => compileNode(arg, math, argNames));
7268
+ const evalArgs = fnNodeOuter.args.map((arg) => compileNode(arg, math, argNames, depth));
7241
7269
  const resolveFn = (scope) => {
7242
7270
  if (scope.has(name)) {
7243
7271
  const value = scope.get(name);
@@ -7275,8 +7303,8 @@ function compileFunctionNode(node, math, argNames) {
7275
7303
  }
7276
7304
  }
7277
7305
  if (fnNode && fnNode.isAccessorNode && fnNode.index && fnNode.index.isObjectProperty && fnNode.index.isObjectProperty()) {
7278
- const evalArgs = fnNodeOuter.args.map((arg) => compileNode(arg, math, argNames));
7279
- const evalObject = compileNode(fnNode.object, math, argNames);
7306
+ const evalArgs = fnNodeOuter.args.map((arg) => compileNode(arg, math, argNames, depth));
7307
+ const evalObject = compileNode(fnNode.object, math, argNames, depth);
7280
7308
  const methodName = fnNode.index.getObjectProperty();
7281
7309
  return function evalFunctionNode(scope, args, context) {
7282
7310
  const obj = evalObject(scope, args, context);
@@ -7286,23 +7314,23 @@ function compileFunctionNode(node, math, argNames) {
7286
7314
  return fn.apply(obj, values);
7287
7315
  };
7288
7316
  }
7289
- const evalArgs = fnNodeOuter.args.map((arg) => compileNode(arg, math, argNames));
7290
- const evalFn = compileNode(fnNode, math, argNames);
7317
+ const evalArgs = fnNodeOuter.args.map((arg) => compileNode(arg, math, argNames, depth));
7318
+ const evalFn = compileNode(fnNode, math, argNames, depth);
7291
7319
  return function evalFunctionNode(scope, args, context) {
7292
7320
  const fn = evalFn(scope, args, context);
7293
7321
  if (typeof fn !== "function") throw new TypeError("Expression does not evaluate to a function");
7294
7322
  return fn(...evalArgs.map((e) => e(scope, args, context)));
7295
7323
  };
7296
7324
  }
7297
- function compileArrayNode(node, math, argNames) {
7298
- const evalItems = node.items.map((item) => compileNode(item, math, argNames));
7325
+ function compileArrayNode(node, math, argNames, depth) {
7326
+ const evalItems = node.items.map((item) => compileNode(item, math, argNames, depth));
7299
7327
  return function evalArrayNode(scope, args, context) {
7300
7328
  return evalItems.map((e) => e(scope, args, context));
7301
7329
  };
7302
7330
  }
7303
- function compileAssignmentNode(node, math, argNames) {
7331
+ function compileAssignmentNode(node, math, argNames, depth) {
7304
7332
  const asgNode = node;
7305
- const evalValue = compileNode(asgNode.value, math, argNames);
7333
+ const evalValue = compileNode(asgNode.value, math, argNames, depth);
7306
7334
  if (asgNode.object && asgNode.object.isSymbolNode && !asgNode.index) {
7307
7335
  const name = asgNode.object.name;
7308
7336
  return function evalAssignmentNode(scope, args, context) {
@@ -7312,7 +7340,7 @@ function compileAssignmentNode(node, math, argNames) {
7312
7340
  };
7313
7341
  }
7314
7342
  if (asgNode.object && asgNode.object.isAccessorNode) {
7315
- const evalObject = compileNode(asgNode.object.object, math, argNames);
7343
+ const evalObject = compileNode(asgNode.object.object, math, argNames, depth);
7316
7344
  if (asgNode.object.index && asgNode.object.index.isObjectProperty()) {
7317
7345
  const prop = asgNode.object.index.getObjectProperty();
7318
7346
  return function evalAssignmentNode(scope, args, context) {
@@ -7330,9 +7358,9 @@ function compileAssignmentNode(node, math, argNames) {
7330
7358
  };
7331
7359
  throw new Error("Unsupported assignment target");
7332
7360
  }
7333
- function compileBlockNode(node, math, argNames) {
7361
+ function compileBlockNode(node, math, argNames, depth) {
7334
7362
  const evalBlocks = node.blocks.map((block) => ({
7335
- evaluate: compileNode(block.node, math, argNames),
7363
+ evaluate: compileNode(block.node, math, argNames, depth),
7336
7364
  visible: block.visible
7337
7365
  }));
7338
7366
  return function evalBlockNode(scope, args, context) {
@@ -7345,20 +7373,20 @@ function compileBlockNode(node, math, argNames) {
7345
7373
  if (results.length > 1) return results;
7346
7374
  };
7347
7375
  }
7348
- function compileConditionalNode(node, math, argNames) {
7376
+ function compileConditionalNode(node, math, argNames, depth) {
7349
7377
  const condNode = node;
7350
- const evalCondition = compileNode(condNode.condition, math, argNames);
7351
- const evalTrueExpr = compileNode(condNode.trueExpr, math, argNames);
7352
- const evalFalseExpr = compileNode(condNode.falseExpr, math, argNames);
7378
+ const evalCondition = compileNode(condNode.condition, math, argNames, depth);
7379
+ const evalTrueExpr = compileNode(condNode.trueExpr, math, argNames, depth);
7380
+ const evalFalseExpr = compileNode(condNode.falseExpr, math, argNames, depth);
7353
7381
  return function evalConditionalNode(scope, args, context) {
7354
7382
  return testCondition(evalCondition(scope, args, context)) ? evalTrueExpr(scope, args, context) : evalFalseExpr(scope, args, context);
7355
7383
  };
7356
7384
  }
7357
- function compileObjectNode(node, math, argNames) {
7385
+ function compileObjectNode(node, math, argNames, depth) {
7358
7386
  const properties = node.properties;
7359
7387
  const evalProps = Object.keys(properties).map((key) => ({
7360
7388
  key,
7361
- evaluate: compileNode(properties[key], math, argNames)
7389
+ evaluate: compileNode(properties[key], math, argNames, depth)
7362
7390
  }));
7363
7391
  return function evalObjectNode(scope, args, context) {
7364
7392
  const result = {};
@@ -7366,14 +7394,14 @@ function compileObjectNode(node, math, argNames) {
7366
7394
  return result;
7367
7395
  };
7368
7396
  }
7369
- function compileRangeNode(node, math, argNames) {
7397
+ function compileRangeNode(node, math, argNames, depth) {
7370
7398
  const rangeFn = math.range;
7371
7399
  if (!rangeFn) throw new Error("Function \"range\" missing in math namespace (required for range expressions)");
7372
7400
  const rngNode = node;
7373
- const evalStart = compileNode(rngNode.start, math, argNames);
7374
- const evalEnd = compileNode(rngNode.end, math, argNames);
7401
+ const evalStart = compileNode(rngNode.start, math, argNames, depth);
7402
+ const evalEnd = compileNode(rngNode.end, math, argNames, depth);
7375
7403
  if (rngNode.step) {
7376
- const evalStep = compileNode(rngNode.step, math, argNames);
7404
+ const evalStep = compileNode(rngNode.step, math, argNames, depth);
7377
7405
  return function evalRangeNode(scope, args, context) {
7378
7406
  return rangeFn(evalStart(scope, args, context), evalEnd(scope, args, context), evalStep(scope, args, context));
7379
7407
  };
@@ -7382,9 +7410,9 @@ function compileRangeNode(node, math, argNames) {
7382
7410
  return rangeFn(evalStart(scope, args, context), evalEnd(scope, args, context));
7383
7411
  };
7384
7412
  }
7385
- function compileRelationalNode(node, math, argNames) {
7413
+ function compileRelationalNode(node, math, argNames, depth) {
7386
7414
  const relNode = node;
7387
- const compiled = relNode.params.map((p) => compileNode(p, math, argNames));
7415
+ const compiled = relNode.params.map((p) => compileNode(p, math, argNames, depth));
7388
7416
  const conditionals = relNode.conditionals;
7389
7417
  return function evalRelationalNode(scope, args, context) {
7390
7418
  let evalLhs;
@@ -7399,11 +7427,11 @@ function compileRelationalNode(node, math, argNames) {
7399
7427
  return true;
7400
7428
  };
7401
7429
  }
7402
- function compileFunctionAssignmentNode(node, math, argNames) {
7430
+ function compileFunctionAssignmentNode(node, math, argNames, depth) {
7403
7431
  const faNode = node;
7404
7432
  const childArgNames = Object.create(argNames);
7405
7433
  for (const param of faNode.params) childArgNames[param] = true;
7406
- const evalExpr = compileNode(faNode.expr, math, childArgNames);
7434
+ const evalExpr = compileNode(faNode.expr, math, childArgNames, depth);
7407
7435
  const name = faNode.name;
7408
7436
  const params = faNode.params;
7409
7437
  return function evalFunctionAssignmentNode(scope, args, _context) {
@@ -7417,9 +7445,9 @@ function compileFunctionAssignmentNode(node, math, argNames) {
7417
7445
  return fn;
7418
7446
  };
7419
7447
  }
7420
- function compileAccessorNode(node, math, argNames) {
7448
+ function compileAccessorNode(node, math, argNames, depth) {
7421
7449
  const accNode = node;
7422
- const evalObject = compileNode(accNode.object, math, argNames);
7450
+ const evalObject = compileNode(accNode.object, math, argNames, depth);
7423
7451
  if (accNode.index && accNode.index.isObjectProperty && accNode.index.isObjectProperty()) {
7424
7452
  const prop = accNode.index.getObjectProperty();
7425
7453
  return function evalAccessorNode(scope, args, context) {
@@ -7428,7 +7456,7 @@ function compileAccessorNode(node, math, argNames) {
7428
7456
  return getSafeProperty(object, prop);
7429
7457
  };
7430
7458
  }
7431
- const evalIndex = compileNode(accNode.index, math, argNames);
7459
+ const evalIndex = compileNode(accNode.index, math, argNames, depth);
7432
7460
  return function evalAccessorNode(scope, args, context) {
7433
7461
  const object = evalObject(scope, args, context);
7434
7462
  if (accNode.optionalChaining && object == null) return void 0;
@@ -7438,16 +7466,16 @@ function compileAccessorNode(node, math, argNames) {
7438
7466
  return getSafeProperty(object, index);
7439
7467
  };
7440
7468
  }
7441
- function compileIndexNode(node, math, argNames) {
7469
+ function compileIndexNode(node, math, argNames, depth) {
7442
7470
  const dimensionNodes = node.dimensions;
7443
7471
  const usesEnd = dimensionNodes.map((dim) => dim.filter?.((n) => n.isSymbolNode === true && n.name === "end")?.length ?? 0);
7444
7472
  const evalDimensions = dimensionNodes.map((dim, i) => {
7445
7473
  if (usesEnd[i]) {
7446
7474
  const childArgNames = Object.create(argNames);
7447
7475
  childArgNames.end = true;
7448
- return compileNode(dim, math, childArgNames);
7476
+ return compileNode(dim, math, childArgNames, depth);
7449
7477
  }
7450
- return compileNode(dim, math, argNames);
7478
+ return compileNode(dim, math, argNames, depth);
7451
7479
  });
7452
7480
  usesEnd.some((n) => n > 0);
7453
7481
  return function evalIndexNode(scope, args, context) {
@@ -7491,8 +7519,9 @@ const FORBIDDEN_FUNCTIONS = /* @__PURE__ */ new Set([
7491
7519
  "help",
7492
7520
  "chain"
7493
7521
  ]);
7494
- function validateAst(node) {
7522
+ function validateAst(node, depth = 0) {
7495
7523
  if (node === null || node === void 0 || typeof node !== "object") return;
7524
+ if (depth >= 512) throw new Error(`expression exceeds compile depth 512`);
7496
7525
  const n = node;
7497
7526
  if (n.isAssignmentNode === true) throw new Error("Security: assignment expressions are disabled. Pass `{ unsafe: true }` to opt out.");
7498
7527
  if (n.isFunctionAssignmentNode === true) throw new Error("Security: function definitions (FunctionAssignmentNode) are disabled. Pass `{ unsafe: true }` to opt out.");
@@ -7502,10 +7531,10 @@ function validateAst(node) {
7502
7531
  if (fnName && FORBIDDEN_FUNCTIONS.has(fnName)) throw new Error(`Security: call to forbidden function "${fnName}" is disabled. Pass \`{ unsafe: true }\` to opt out.`);
7503
7532
  }
7504
7533
  if (typeof n.forEach === "function") try {
7505
- n.forEach((child) => validateAst(child));
7534
+ n.forEach((child) => validateAst(child, depth + 1));
7506
7535
  return;
7507
7536
  } catch (err) {
7508
- if (err instanceof Error && err.message.startsWith("Security:")) throw err;
7537
+ if (err instanceof RangeError || err instanceof Error && (err.message.startsWith("Security:") || err.message.startsWith("expression exceeds"))) throw err;
7509
7538
  }
7510
7539
  for (const key of [
7511
7540
  "object",
@@ -7520,19 +7549,19 @@ function validateAst(node) {
7520
7549
  "start",
7521
7550
  "end",
7522
7551
  "step"
7523
- ]) if (key in n) validateAst(n[key]);
7552
+ ]) if (key in n) validateAst(n[key], depth + 1);
7524
7553
  for (const key of [
7525
7554
  "args",
7526
7555
  "items",
7527
7556
  "params"
7528
7557
  ]) {
7529
7558
  const arr = n[key];
7530
- if (Array.isArray(arr)) for (const child of arr) validateAst(child);
7559
+ if (Array.isArray(arr)) for (const child of arr) validateAst(child, depth + 1);
7531
7560
  }
7532
- if (Array.isArray(n.blocks)) for (const b of n.blocks) validateAst(b && b.node);
7561
+ if (Array.isArray(n.blocks)) for (const b of n.blocks) validateAst(b && b.node, depth + 1);
7533
7562
  if (n.properties && typeof n.properties === "object") {
7534
7563
  const properties = n.properties;
7535
- for (const k of Object.keys(properties)) validateAst(properties[k]);
7564
+ for (const k of Object.keys(properties)) validateAst(properties[k], depth + 1);
7536
7565
  }
7537
7566
  }
7538
7567
  function createEvaluate(parseFn, mathScope) {
@@ -7836,6 +7865,11 @@ const createNode = /* #__PURE__ */ factory("Node", ["mathWithTransform"], ({ mat
7836
7865
  isClass: true,
7837
7866
  isNode: true
7838
7867
  });
7868
+ function mapChildArray(parent, children, pathPrefix, callback) {
7869
+ const out = [];
7870
+ for (let i = 0; i < children.length; i++) out[i] = parent._ifNode(callback(children[i], pathPrefix + "[" + i + "]", parent));
7871
+ return out;
7872
+ }
7839
7873
  //#endregion
7840
7874
  //#region src/transform/utils/errorTransform.ts
7841
7875
  function errorTransform(err) {
@@ -7845,8 +7879,8 @@ function errorTransform(err) {
7845
7879
  }
7846
7880
  //#endregion
7847
7881
  //#region src/node/utils/stringSubset.ts
7848
- function getStringSubset(str, index) {
7849
- if (!isIndex(index)) throw new TypeError("Index expected");
7882
+ function getStringSubset(str, index, invalidIndexMessage = "Index expected") {
7883
+ if (!isIndex(index)) throw new TypeError(invalidIndexMessage);
7850
7884
  if (isEmptyIndex(index)) return "";
7851
7885
  validateIndexSourceSize(Array.from(str), index);
7852
7886
  if (index.size().length !== 1) throw new DimensionError(index.size().length, 1);
@@ -8050,8 +8084,7 @@ const createArrayNode = /* #__PURE__ */ factory(name$39, ["Node"], ({ Node }) =>
8050
8084
  }
8051
8085
  }
8052
8086
  map(callback) {
8053
- const items = [];
8054
- for (let i = 0; i < this.items.length; i++) items[i] = this._ifNode(callback(this.items[i], "items[" + i + "]", this));
8087
+ const items = mapChildArray(this, this.items, "items", callback);
8055
8088
  return new ArrayNode(items);
8056
8089
  }
8057
8090
  clone() {
@@ -8130,6 +8163,13 @@ const createAssignmentNode = /* #__PURE__ */ factory(name$38, ["subset", "Node"]
8130
8163
  const exprPrecedence = getPrecedence(node.value, parenthesis, implicit, void 0);
8131
8164
  return parenthesis === "all" || exprPrecedence !== null && exprPrecedence <= precedence;
8132
8165
  }
8166
+ function renderAssignment(node, options, render, markup) {
8167
+ const object = render(node.object, options);
8168
+ const index = node.index ? render(node.index, options) : "";
8169
+ let value = render(node.value, options);
8170
+ if (needParenthesis(node, options && options.parenthesis, options && options.implicit)) value = markup.paren(value);
8171
+ return object + index + markup.eq + value;
8172
+ }
8133
8173
  class AssignmentNode extends Node {
8134
8174
  object;
8135
8175
  index;
@@ -8224,11 +8264,10 @@ const createAssignmentNode = /* #__PURE__ */ factory(name$38, ["subset", "Node"]
8224
8264
  return new AssignmentNode(this.object, this.index, this.value);
8225
8265
  }
8226
8266
  _toString(options) {
8227
- const object = this.object.toString(options);
8228
- const index = this.index ? this.index.toString(options) : "";
8229
- let value = this.value.toString(options);
8230
- if (needParenthesis(this, options && options.parenthesis, options && options.implicit)) value = "(" + value + ")";
8231
- return object + index + " = " + value;
8267
+ return renderAssignment(this, options, (node, opts) => node.toString(opts), {
8268
+ paren: (value) => "(" + value + ")",
8269
+ eq: " = "
8270
+ });
8232
8271
  }
8233
8272
  toJSON() {
8234
8273
  return {
@@ -8242,11 +8281,10 @@ const createAssignmentNode = /* #__PURE__ */ factory(name$38, ["subset", "Node"]
8242
8281
  return new AssignmentNode(json.object, json.index, json.value);
8243
8282
  }
8244
8283
  _toHTML(options) {
8245
- const object = this.object.toHTML(options);
8246
- const index = this.index ? this.index.toHTML(options) : "";
8247
- let value = this.value.toHTML(options);
8248
- if (needParenthesis(this, options && options.parenthesis, options && options.implicit)) value = "<span class=\"math-paranthesis math-round-parenthesis\">(</span>" + value + "<span class=\"math-paranthesis math-round-parenthesis\">)</span>";
8249
- return object + index + "<span class=\"math-operator math-assignment-operator math-variable-assignment-operator math-binary-operator\">=</span>" + value;
8284
+ return renderAssignment(this, options, (node, opts) => node.toHTML(opts), {
8285
+ paren: (value) => "<span class=\"math-paranthesis math-round-parenthesis\">(</span>" + value + "<span class=\"math-paranthesis math-round-parenthesis\">)</span>",
8286
+ eq: "<span class=\"math-operator math-assignment-operator math-variable-assignment-operator math-binary-operator\">=</span>"
8287
+ });
8250
8288
  }
8251
8289
  _toMathML() {
8252
8290
  const obj = this.object;
@@ -8257,11 +8295,10 @@ const createAssignmentNode = /* #__PURE__ */ factory(name$38, ["subset", "Node"]
8257
8295
  return `<mtext>${escapeMathML(this.toString())}</mtext>`;
8258
8296
  }
8259
8297
  _toTex(options) {
8260
- const object = this.object.toTex(options);
8261
- const index = this.index ? this.index.toTex(options) : "";
8262
- let value = this.value.toTex(options);
8263
- if (needParenthesis(this, options && options.parenthesis, options && options.implicit)) value = `\\left(${value}\\right)`;
8264
- return object + index + "=" + value;
8298
+ return renderAssignment(this, options, (node, opts) => node.toTex(opts), {
8299
+ paren: (value) => `\\left(${value}\\right)`,
8300
+ eq: "="
8301
+ });
8265
8302
  }
8266
8303
  }
8267
8304
  return AssignmentNode;
@@ -8380,6 +8417,17 @@ const createConditionalNode = /* #__PURE__ */ factory(name$36, ["Node"], ({ Node
8380
8417
  if (condition === null || condition === void 0) return false;
8381
8418
  throw new TypeError("Unsupported type of condition \"" + typeOf(condition) + "\"");
8382
8419
  }
8420
+ function formatConditionalBranches(self, options, render, wrap, join) {
8421
+ const parenthesis = options && options.parenthesis ? options.parenthesis : "keep";
8422
+ const precedence = getPrecedence(self, parenthesis, options && options.implicit, void 0);
8423
+ function branchText(node) {
8424
+ let text = render(node);
8425
+ const nodePrecedence = getPrecedence(node, parenthesis, options && options.implicit, void 0);
8426
+ if (parenthesis === "all" || node.type === "OperatorNode" || nodePrecedence !== null && nodePrecedence <= precedence) text = wrap(text);
8427
+ return text;
8428
+ }
8429
+ return join(branchText(self.condition), branchText(self.trueExpr), branchText(self.falseExpr));
8430
+ }
8383
8431
  class ConditionalNode extends Node {
8384
8432
  condition;
8385
8433
  trueExpr;
@@ -8420,18 +8468,7 @@ const createConditionalNode = /* #__PURE__ */ factory(name$36, ["Node"], ({ Node
8420
8468
  return new ConditionalNode(this.condition, this.trueExpr, this.falseExpr);
8421
8469
  }
8422
8470
  _toString(options) {
8423
- const parenthesis = options && options.parenthesis ? options.parenthesis : "keep";
8424
- const precedence = getPrecedence(this, parenthesis, options && options.implicit, void 0);
8425
- let condition = this.condition.toString(options);
8426
- const conditionPrecedence = getPrecedence(this.condition, parenthesis, options && options.implicit, void 0);
8427
- if (parenthesis === "all" || this.condition.type === "OperatorNode" || conditionPrecedence !== null && conditionPrecedence <= precedence) condition = "(" + condition + ")";
8428
- let trueExpr = this.trueExpr.toString(options);
8429
- const truePrecedence = getPrecedence(this.trueExpr, parenthesis, options && options.implicit, void 0);
8430
- if (parenthesis === "all" || this.trueExpr.type === "OperatorNode" || truePrecedence !== null && truePrecedence <= precedence) trueExpr = "(" + trueExpr + ")";
8431
- let falseExpr = this.falseExpr.toString(options);
8432
- const falsePrecedence = getPrecedence(this.falseExpr, parenthesis, options && options.implicit, void 0);
8433
- if (parenthesis === "all" || this.falseExpr.type === "OperatorNode" || falsePrecedence !== null && falsePrecedence <= precedence) falseExpr = "(" + falseExpr + ")";
8434
- return condition + " ? " + trueExpr + " : " + falseExpr;
8471
+ return formatConditionalBranches(this, options, (node) => node.toString(options), (inner) => "(" + inner + ")", (condition, trueExpr, falseExpr) => condition + " ? " + trueExpr + " : " + falseExpr);
8435
8472
  }
8436
8473
  toJSON() {
8437
8474
  return {
@@ -8445,18 +8482,9 @@ const createConditionalNode = /* #__PURE__ */ factory(name$36, ["Node"], ({ Node
8445
8482
  return new ConditionalNode(json.condition, json.trueExpr, json.falseExpr);
8446
8483
  }
8447
8484
  _toHTML(options) {
8448
- const parenthesis = options && options.parenthesis ? options.parenthesis : "keep";
8449
- const precedence = getPrecedence(this, parenthesis, options && options.implicit, void 0);
8450
- let condition = this.condition.toHTML(options);
8451
- const conditionPrecedence = getPrecedence(this.condition, parenthesis, options && options.implicit, void 0);
8452
- if (parenthesis === "all" || this.condition.type === "OperatorNode" || conditionPrecedence !== null && conditionPrecedence <= precedence) condition = "<span class=\"math-parenthesis math-round-parenthesis\">(</span>" + condition + "<span class=\"math-parenthesis math-round-parenthesis\">)</span>";
8453
- let trueExpr = this.trueExpr.toHTML(options);
8454
- const truePrecedence = getPrecedence(this.trueExpr, parenthesis, options && options.implicit, void 0);
8455
- if (parenthesis === "all" || this.trueExpr.type === "OperatorNode" || truePrecedence !== null && truePrecedence <= precedence) trueExpr = "<span class=\"math-parenthesis math-round-parenthesis\">(</span>" + trueExpr + "<span class=\"math-parenthesis math-round-parenthesis\">)</span>";
8456
- let falseExpr = this.falseExpr.toHTML(options);
8457
- const falsePrecedence = getPrecedence(this.falseExpr, parenthesis, options && options.implicit, void 0);
8458
- if (parenthesis === "all" || this.falseExpr.type === "OperatorNode" || falsePrecedence !== null && falsePrecedence <= precedence) falseExpr = "<span class=\"math-parenthesis math-round-parenthesis\">(</span>" + falseExpr + "<span class=\"math-parenthesis math-round-parenthesis\">)</span>";
8459
- return condition + "<span class=\"math-operator math-conditional-operator\">?</span>" + trueExpr + "<span class=\"math-operator math-conditional-operator\">:</span>" + falseExpr;
8485
+ const parenOpen = "<span class=\"math-parenthesis math-round-parenthesis\">(</span>";
8486
+ const parenClose = "<span class=\"math-parenthesis math-round-parenthesis\">)</span>";
8487
+ return formatConditionalBranches(this, options, (node) => node.toHTML(options), (inner) => parenOpen + inner + parenClose, (condition, trueExpr, falseExpr) => condition + "<span class=\"math-operator math-conditional-operator\">?</span>" + trueExpr + "<span class=\"math-operator math-conditional-operator\">:</span>" + falseExpr);
8460
8488
  }
8461
8489
  _toTex(options) {
8462
8490
  return "\\begin{cases} {" + this.trueExpr.toTex(options) + "}, &\\quad{\\text{if }\\;" + this.condition.toTex(options) + "}\\\\{" + this.falseExpr.toTex(options) + "}, &\\quad{\\text{otherwise}}\\end{cases}";
@@ -9173,8 +9201,7 @@ const createFunctionNode = /* #__PURE__ */ factory(name$33, [
9173
9201
  }
9174
9202
  map(callback) {
9175
9203
  const fn = this._ifNode(callback(this.fn, "fn", this));
9176
- const args = [];
9177
- for (let i = 0; i < this.args.length; i++) args[i] = this._ifNode(callback(this.args[i], "args[" + i + "]", this));
9204
+ const args = mapChildArray(this, this.args, "args", callback);
9178
9205
  return new FunctionNode(fn, args);
9179
9206
  }
9180
9207
  clone() {
@@ -9315,8 +9342,7 @@ const createIndexNode = /* #__PURE__ */ factory(name$32, ["Node", "size"], ({ No
9315
9342
  for (let i = 0; i < this.dimensions.length; i++) callback(this.dimensions[i], "dimensions[" + i + "]", this);
9316
9343
  }
9317
9344
  map(callback) {
9318
- const dimensions = [];
9319
- for (let i = 0; i < this.dimensions.length; i++) dimensions[i] = this._ifNode(callback(this.dimensions[i], "dimensions[" + i + "]", this));
9345
+ const dimensions = mapChildArray(this, this.dimensions, "dimensions", callback);
9320
9346
  return new IndexNode(dimensions, this.dotNotation);
9321
9347
  }
9322
9348
  clone() {
@@ -9364,6 +9390,11 @@ const createIndexNode = /* #__PURE__ */ factory(name$32, ["Node", "size"], ({ No
9364
9390
  //#region src/node/ObjectNode.ts
9365
9391
  const name$31 = "ObjectNode";
9366
9392
  const createObjectNode = /* #__PURE__ */ factory(name$31, ["Node"], ({ Node }) => {
9393
+ function renderObjectEntries(properties, entry, separator, wrap) {
9394
+ const entries = [];
9395
+ for (const key in properties) if (hasOwnProperty(properties, key)) entries.push(entry(key, properties[key]));
9396
+ return wrap(entries.join(separator));
9397
+ }
9367
9398
  class ObjectNode extends Node {
9368
9399
  properties;
9369
9400
  constructor(properties) {
@@ -9399,18 +9430,16 @@ const createObjectNode = /* #__PURE__ */ factory(name$31, ["Node"], ({ Node }) =
9399
9430
  }
9400
9431
  map(callback) {
9401
9432
  const properties = {};
9402
- for (const key in this.properties) if (hasOwnProperty(this.properties, key)) properties[key] = this._ifNode(callback(this.properties[key], "properties[" + stringify(key) + "]", this));
9433
+ for (const key in this.properties) if (hasOwnProperty(this.properties, key)) setSafeProperty(properties, key, this._ifNode(callback(this.properties[key], "properties[" + stringify(key) + "]", this)));
9403
9434
  return new ObjectNode(properties);
9404
9435
  }
9405
9436
  clone() {
9406
9437
  const properties = {};
9407
- for (const key in this.properties) if (hasOwnProperty(this.properties, key)) properties[key] = this.properties[key];
9438
+ for (const key in this.properties) if (hasOwnProperty(this.properties, key)) setSafeProperty(properties, key, this.properties[key]);
9408
9439
  return new ObjectNode(properties);
9409
9440
  }
9410
9441
  _toString(options) {
9411
- const entries = [];
9412
- for (const key in this.properties) if (hasOwnProperty(this.properties, key)) entries.push(stringify(key) + ": " + this.properties[key].toString(options));
9413
- return "{" + entries.join(", ") + "}";
9442
+ return renderObjectEntries(this.properties, (key, node) => stringify(key) + ": " + node.toString(options), ", ", (joined) => "{" + joined + "}");
9414
9443
  }
9415
9444
  toJSON() {
9416
9445
  return {
@@ -9422,14 +9451,10 @@ const createObjectNode = /* #__PURE__ */ factory(name$31, ["Node"], ({ Node }) =
9422
9451
  return new ObjectNode(json.properties);
9423
9452
  }
9424
9453
  _toHTML(options) {
9425
- const entries = [];
9426
- for (const key in this.properties) if (hasOwnProperty(this.properties, key)) entries.push("<span class=\"math-symbol math-property\">" + escape(key) + "</span><span class=\"math-operator math-assignment-operator math-property-assignment-operator math-binary-operator\">:</span>" + this.properties[key].toHTML(options));
9427
- return "<span class=\"math-parenthesis math-curly-parenthesis\">{</span>" + entries.join("<span class=\"math-separator\">,</span>") + "<span class=\"math-parenthesis math-curly-parenthesis\">}</span>";
9454
+ return renderObjectEntries(this.properties, (key, node) => "<span class=\"math-symbol math-property\">" + escape(key) + "</span><span class=\"math-operator math-assignment-operator math-property-assignment-operator math-binary-operator\">:</span>" + node.toHTML(options), "<span class=\"math-separator\">,</span>", (joined) => "<span class=\"math-parenthesis math-curly-parenthesis\">{</span>" + joined + "<span class=\"math-parenthesis math-curly-parenthesis\">}</span>");
9428
9455
  }
9429
9456
  _toTex(options) {
9430
- const entries = [];
9431
- for (const key in this.properties) if (hasOwnProperty(this.properties, key)) entries.push("\\mathbf{" + key + ":} & " + this.properties[key].toTex(options) + "\\\\");
9432
- return "\\left\\{\\begin{array}{ll}" + entries.join("\n") + "\\end{array}\\right\\}";
9457
+ return renderObjectEntries(this.properties, (key, node) => "\\mathbf{" + key + ":} & " + node.toTex(options) + "\\\\", "\n", (joined) => "\\left\\{\\begin{array}{ll}" + joined + "\\end{array}\\right\\}");
9433
9458
  }
9434
9459
  }
9435
9460
  return ObjectNode;
@@ -9448,10 +9473,19 @@ const createOperatorNode = /* #__PURE__ */ factory(name$30, ["Node"], ({ Node })
9448
9473
  if (isOperatorNode(curNode)) return startsWithConstant(curNode.args[0], parenthesis);
9449
9474
  return false;
9450
9475
  }
9476
+ function isNegativeNumberConstant(arg) {
9477
+ const content = arg.getContent();
9478
+ if (!isConstantNode(content)) return false;
9479
+ const value = content.value;
9480
+ return typeof value === "number" && value < 0;
9481
+ }
9482
+ function parenthesizeNegativeConstants(args, parens) {
9483
+ return args.map((arg, i) => parens[i] === true || isNegativeNumberConstant(arg));
9484
+ }
9451
9485
  function calculateNecessaryParentheses(root, parenthesis, implicit, args, latex) {
9452
9486
  const precedence = getPrecedence(root, parenthesis, implicit, void 0);
9453
9487
  const associativity = getAssociativity(root, parenthesis);
9454
- if (parenthesis === "all" || args.length > 2 && root.getIdentifier() !== "OperatorNode:add" && root.getIdentifier() !== "OperatorNode:multiply") return args.map(function(arg) {
9488
+ if (parenthesis === "all" || args.length > 2 && root.getIdentifier() !== "OperatorNode:add" && root.getIdentifier() !== "OperatorNode:multiply") return parenthesizeNegativeConstants(args, args.map(function(arg) {
9455
9489
  switch (arg.getContent().type) {
9456
9490
  case "ArrayNode":
9457
9491
  case "ConstantNode":
@@ -9459,7 +9493,7 @@ const createOperatorNode = /* #__PURE__ */ factory(name$30, ["Node"], ({ Node })
9459
9493
  case "ParenthesisNode": return false;
9460
9494
  default: return true;
9461
9495
  }
9462
- });
9496
+ }));
9463
9497
  let result = [];
9464
9498
  switch (args.length) {
9465
9499
  case 0:
@@ -9552,7 +9586,7 @@ const createOperatorNode = /* #__PURE__ */ factory(name$30, ["Node"], ({ Node })
9552
9586
  if (args.length >= 2 && root.getIdentifier() === "OperatorNode:multiply" && root.implicit && parenthesis !== "all" && implicit === "hide") {
9553
9587
  for (let i = 1; i < result.length; ++i) if (startsWithConstant(args[i], parenthesis) && !result[i - 1] && (parenthesis !== "keep" || !isParenthesisNode(args[i - 1]))) result[i] = true;
9554
9588
  }
9555
- return result;
9589
+ return parenthesizeNegativeConstants(args, result);
9556
9590
  }
9557
9591
  class OperatorNode extends Node {
9558
9592
  op;
@@ -9613,8 +9647,7 @@ const createOperatorNode = /* #__PURE__ */ factory(name$30, ["Node"], ({ Node })
9613
9647
  for (let i = 0; i < this.args.length; i++) callback(this.args[i], "args[" + i + "]", this);
9614
9648
  }
9615
9649
  map(callback) {
9616
- const args = [];
9617
- for (let i = 0; i < this.args.length; i++) args[i] = this._ifNode(callback(this.args[i], "args[" + i + "]", this));
9650
+ const args = mapChildArray(this, this.args, "args", callback);
9618
9651
  return new OperatorNode(this.op, this.fn, args, this.implicit, this.isPercentage);
9619
9652
  }
9620
9653
  clone() {
@@ -9857,6 +9890,21 @@ const createRangeNode = /* #__PURE__ */ factory(name$28, ["Node"], ({ Node }) =>
9857
9890
  parens.end = endPrecedence !== null && endPrecedence <= precedence || parenthesis === "all";
9858
9891
  return parens;
9859
9892
  }
9893
+ function formatRangeDisplay(node, options, render, wrap, rangeSep) {
9894
+ const parens = calculateNecessaryParentheses(node, options && options.parenthesis ? options.parenthesis : "keep", options && options.implicit || "hide");
9895
+ let start = render(node.start);
9896
+ if (parens.start) start = wrap(start);
9897
+ let str = start;
9898
+ if (node.step) {
9899
+ let step = render(node.step);
9900
+ if (parens.step) step = wrap(step);
9901
+ str += rangeSep() + step;
9902
+ }
9903
+ let end = render(node.end);
9904
+ if (parens.end) end = wrap(end);
9905
+ str += rangeSep() + end;
9906
+ return str;
9907
+ }
9860
9908
  class RangeNode extends Node {
9861
9909
  start;
9862
9910
  end;
@@ -9908,21 +9956,7 @@ const createRangeNode = /* #__PURE__ */ factory(name$28, ["Node"], ({ Node }) =>
9908
9956
  return new RangeNode(this.start, this.end, this.step || void 0);
9909
9957
  }
9910
9958
  _toString(options) {
9911
- const parenthesis = options && options.parenthesis ? options.parenthesis : "keep";
9912
- const parens = calculateNecessaryParentheses(this, parenthesis, options && options.implicit || "hide");
9913
- let str;
9914
- let start = this.start.toString(options);
9915
- if (parens.start) start = "(" + start + ")";
9916
- str = start;
9917
- if (this.step) {
9918
- let step = this.step.toString(options);
9919
- if (parens.step) step = "(" + step + ")";
9920
- str += ":" + step;
9921
- }
9922
- let end = this.end.toString(options);
9923
- if (parens.end) end = "(" + end + ")";
9924
- str += ":" + end;
9925
- return str;
9959
+ return formatRangeDisplay(this, options, (child) => child.toString(options), (inner) => "(" + inner + ")", () => ":");
9926
9960
  }
9927
9961
  toJSON() {
9928
9962
  return {
@@ -9936,21 +9970,9 @@ const createRangeNode = /* #__PURE__ */ factory(name$28, ["Node"], ({ Node }) =>
9936
9970
  return new RangeNode(json.start, json.end, json.step);
9937
9971
  }
9938
9972
  _toHTML(options) {
9939
- const parenthesis = options && options.parenthesis ? options.parenthesis : "keep";
9940
- const parens = calculateNecessaryParentheses(this, parenthesis, options && options.implicit || "hide");
9941
- let str;
9942
- let start = this.start.toHTML(options);
9943
- if (parens.start) start = "<span class=\"math-parenthesis math-round-parenthesis\">(</span>" + start + "<span class=\"math-parenthesis math-round-parenthesis\">)</span>";
9944
- str = start;
9945
- if (this.step) {
9946
- let step = this.step.toHTML(options);
9947
- if (parens.step) step = "<span class=\"math-parenthesis math-round-parenthesis\">(</span>" + step + "<span class=\"math-parenthesis math-round-parenthesis\">)</span>";
9948
- str += "<span class=\"math-operator math-range-operator\">:</span>" + step;
9949
- }
9950
- let end = this.end.toHTML(options);
9951
- if (parens.end) end = "<span class=\"math-parenthesis math-round-parenthesis\">(</span>" + end + "<span class=\"math-parenthesis math-round-parenthesis\">)</span>";
9952
- str += "<span class=\"math-operator math-range-operator\">:</span>" + end;
9953
- return str;
9973
+ const parenOpen = "<span class=\"math-parenthesis math-round-parenthesis\">(</span>";
9974
+ const parenClose = "<span class=\"math-parenthesis math-round-parenthesis\">)</span>";
9975
+ return formatRangeDisplay(this, options, (child) => child.toHTML(options), (inner) => parenOpen + inner + parenClose, () => "<span class=\"math-operator math-range-operator\">:</span>");
9954
9976
  }
9955
9977
  _toTex(options) {
9956
9978
  const parenthesis = options && options.parenthesis ? options.parenthesis : "keep";
@@ -9985,6 +10007,18 @@ const createRelationalNode = /* #__PURE__ */ factory(name$27, ["Node"], ({ Node
9985
10007
  smallerEq: "<=",
9986
10008
  largerEq: ">="
9987
10009
  };
10010
+ function renderRelational(self, options, renderParam, wrapParam, join) {
10011
+ const parenthesis = options && options.parenthesis ? options.parenthesis : "keep";
10012
+ const precedence = getPrecedence(self, parenthesis, options && options.implicit || "hide", void 0);
10013
+ const paramStrings = self.params.map(function(p) {
10014
+ const paramPrecedence = getPrecedence(p, parenthesis, options && options.implicit || "hide", void 0);
10015
+ const text = renderParam(p);
10016
+ return parenthesis === "all" || paramPrecedence !== null && paramPrecedence <= precedence ? wrapParam(text) : text;
10017
+ });
10018
+ let ret = paramStrings[0];
10019
+ for (let i = 0; i < self.conditionals.length; i++) ret += join(self.conditionals[i], paramStrings[i + 1]);
10020
+ return ret;
10021
+ }
9988
10022
  class RelationalNode extends Node {
9989
10023
  conditionals;
9990
10024
  params;
@@ -10027,18 +10061,7 @@ const createRelationalNode = /* #__PURE__ */ factory(name$27, ["Node"], ({ Node
10027
10061
  return new RelationalNode(this.conditionals, this.params);
10028
10062
  }
10029
10063
  _toString(options) {
10030
- const parenthesis = options && options.parenthesis ? options.parenthesis : "keep";
10031
- const precedence = getPrecedence(this, parenthesis, options && options.implicit || "hide", void 0);
10032
- const paramStrings = this.params.map(function(p, _index) {
10033
- const paramPrecedence = getPrecedence(p, parenthesis, options && options.implicit || "hide", void 0);
10034
- return parenthesis === "all" || paramPrecedence !== null && paramPrecedence <= precedence ? "(" + p.toString(options) + ")" : p.toString(options);
10035
- });
10036
- let ret = paramStrings[0];
10037
- for (let i = 0; i < this.conditionals.length; i++) {
10038
- ret += " " + operatorMap[this.conditionals[i]];
10039
- ret += " " + paramStrings[i + 1];
10040
- }
10041
- return ret;
10064
+ return renderRelational(this, options, (p) => p.toString(options), (inner) => "(" + inner + ")", (cond, rhs) => " " + operatorMap[cond] + " " + rhs);
10042
10065
  }
10043
10066
  toJSON() {
10044
10067
  return {
@@ -10051,15 +10074,7 @@ const createRelationalNode = /* #__PURE__ */ factory(name$27, ["Node"], ({ Node
10051
10074
  return new RelationalNode(json.conditionals, json.params);
10052
10075
  }
10053
10076
  _toHTML(options) {
10054
- const parenthesis = options && options.parenthesis ? options.parenthesis : "keep";
10055
- const precedence = getPrecedence(this, parenthesis, options && options.implicit || "hide", void 0);
10056
- const paramStrings = this.params.map(function(p, _index) {
10057
- const paramPrecedence = getPrecedence(p, parenthesis, options && options.implicit || "hide", void 0);
10058
- return parenthesis === "all" || paramPrecedence !== null && paramPrecedence <= precedence ? "<span class=\"math-parenthesis math-round-parenthesis\">(</span>" + p.toHTML(options) + "<span class=\"math-parenthesis math-round-parenthesis\">)</span>" : p.toHTML(options);
10059
- });
10060
- let ret = paramStrings[0];
10061
- for (let i = 0; i < this.conditionals.length; i++) ret += "<span class=\"math-operator math-binary-operator math-explicit-binary-operator\">" + escape(operatorMap[this.conditionals[i]]) + "</span>" + paramStrings[i + 1];
10062
- return ret;
10077
+ return renderRelational(this, options, (param) => param.toHTML(options), (inner) => "<span class=\"math-parenthesis math-round-parenthesis\">(</span>" + inner + "<span class=\"math-parenthesis math-round-parenthesis\">)</span>", (conditional, rhs) => "<span class=\"math-operator math-binary-operator math-explicit-binary-operator\">" + escape(operatorMap[conditional]) + "</span>" + rhs);
10063
10078
  }
10064
10079
  _toMathML() {
10065
10080
  const params = this.params;
@@ -10072,15 +10087,8 @@ const createRelationalNode = /* #__PURE__ */ factory(name$27, ["Node"], ({ Node
10072
10087
  return `<mtext>${escapeMathML(this.toString())}</mtext>`;
10073
10088
  }
10074
10089
  _toTex(options) {
10075
- const parenthesis = options && options.parenthesis ? options.parenthesis : "keep";
10076
- const precedence = getPrecedence(this, parenthesis, options && options.implicit || "hide", void 0);
10077
- const paramStrings = this.params.map(function(p, _index) {
10078
- const paramPrecedence = getPrecedence(p, parenthesis, options && options.implicit || "hide", void 0);
10079
- return parenthesis === "all" || paramPrecedence !== null && paramPrecedence <= precedence ? "\\left(" + p.toTex(options) + "\\right)" : p.toTex(options);
10080
- });
10081
- let ret = paramStrings[0];
10082
- for (let i = 0; i < this.conditionals.length; i++) ret += latexOperators[this.conditionals[i]] + paramStrings[i + 1];
10083
- return ret;
10090
+ const latex = latexOperators;
10091
+ return renderRelational(this, options, (p) => p.toTex(options), (inner) => "\\left(" + inner + "\\right)", (cond, rhs) => latex[cond] + rhs);
10084
10092
  }
10085
10093
  }
10086
10094
  return RelationalNode;
@@ -10202,7 +10210,7 @@ const SCALAR_FUNCTIONS = [
10202
10210
  "tanh",
10203
10211
  "abs"
10204
10212
  ];
10205
- const LOWERED_FUNCTIONS = new Set(Object.values(SCALAR_FUNCTION_LOWERING));
10213
+ const LOWERED_FUNCTIONS$1 = new Set(Object.values(SCALAR_FUNCTION_LOWERING));
10206
10214
  var ScalarBuildError = class extends Error {
10207
10215
  constructor(message) {
10208
10216
  super(message);
@@ -10222,8 +10230,8 @@ function isOperator(op) {
10222
10230
  function isScalarFunctionName(fn) {
10223
10231
  return Object.prototype.hasOwnProperty.call(SCALAR_FUNCTION_LOWERING, fn);
10224
10232
  }
10225
- function isLoweredScalarFn(fn) {
10226
- return LOWERED_FUNCTIONS.has(fn);
10233
+ function isLoweredScalarFn$1(fn) {
10234
+ return LOWERED_FUNCTIONS$1.has(fn);
10227
10235
  }
10228
10236
  function isCanonicalNode(value) {
10229
10237
  if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
@@ -10290,7 +10298,7 @@ function createScalarBuilder() {
10290
10298
  }
10291
10299
  case "call": {
10292
10300
  const fn = record.fn;
10293
- if (typeof fn !== "string" || !isLoweredScalarFn(fn)) throw new ScalarBuildError(`scalar call '${String(fn)}' must already use a lowered MathTS name`);
10301
+ if (typeof fn !== "string" || !isLoweredScalarFn$1(fn)) throw new ScalarBuildError(`scalar call '${String(fn)}' must already use a lowered MathTS name`);
10294
10302
  return {
10295
10303
  kind: "call",
10296
10304
  fn,
@@ -10314,36 +10322,590 @@ function createScalarBuilder() {
10314
10322
  from
10315
10323
  };
10316
10324
  }
10325
+ //#endregion
10326
+ //#region src/scalar-structure.ts
10327
+ const LOWERED_FUNCTIONS = new Set(Object.values(SCALAR_FUNCTION_LOWERING));
10328
+ function isLoweredScalarFn(fn) {
10329
+ return LOWERED_FUNCTIONS.has(fn);
10330
+ }
10331
+ function spell(value) {
10332
+ if (Number.isNaN(value)) return "NaN";
10333
+ if (Object.is(value, -0)) return "-0";
10334
+ if (value === Infinity) return "Infinity";
10335
+ if (value === -Infinity) return "-Infinity";
10336
+ return value.toString();
10337
+ }
10338
+ function assertPowerArity(args) {
10339
+ if (args.length !== 2) throw new ScalarBuildError(`'^' needs exactly 2 args, got ${args.length}`);
10340
+ }
10341
+ function assertCallFn(fn) {
10342
+ if (!isLoweredScalarFn(fn)) throw new ScalarBuildError(`unknown scalar function '${String(fn)}'`);
10343
+ return fn;
10344
+ }
10345
+ function forEachScalar(node, visit) {
10346
+ function walk(n, depth) {
10347
+ visit(n, depth);
10348
+ if (n.kind === "call") walk(n.arg, depth + 1);
10349
+ else if (n.kind === "op") for (const child of n.args) walk(child, depth + 1);
10350
+ }
10351
+ walk(node, 0);
10352
+ }
10353
+ function mapScalar(node, fn) {
10354
+ let rebuilt;
10355
+ if (node.kind === "number" || node.kind === "symbol") rebuilt = node;
10356
+ else if (node.kind === "call") {
10357
+ const newArg = mapScalar(node.arg, fn);
10358
+ rebuilt = newArg === node.arg ? node : {
10359
+ kind: "call",
10360
+ fn: node.fn,
10361
+ arg: newArg
10362
+ };
10363
+ } else {
10364
+ const newArgs = node.args.map((child) => mapScalar(child, fn));
10365
+ rebuilt = newArgs.every((child, i) => child === node.args[i]) ? node : {
10366
+ kind: "op",
10367
+ op: node.op,
10368
+ args: newArgs
10369
+ };
10370
+ }
10371
+ const result = fn(rebuilt);
10372
+ if (result === rebuilt && rebuilt === node) return node;
10373
+ return result;
10374
+ }
10375
+ function scalarSize(node) {
10376
+ let count = 0;
10377
+ forEachScalar(node, () => {
10378
+ count += 1;
10379
+ });
10380
+ return count;
10381
+ }
10382
+ function scalarDepth(node) {
10383
+ if (node.kind === "number" || node.kind === "symbol") return 1;
10384
+ if (node.kind === "call") return 1 + scalarDepth(node.arg);
10385
+ if (node.args.length === 0) return 1;
10386
+ let maxChild = 0;
10387
+ for (const child of node.args) {
10388
+ const d = scalarDepth(child);
10389
+ if (d > maxChild) maxChild = d;
10390
+ }
10391
+ return 1 + maxChild;
10392
+ }
10393
+ function ownWeight(record, key) {
10394
+ return record !== void 0 && hasOwnProperty(record, key) ? record[key] ?? 1 : 1;
10395
+ }
10396
+ function symbolWeight(name, weights) {
10397
+ const w = weights.symbol;
10398
+ if (w === void 0) return 1;
10399
+ if (typeof w === "number") return w;
10400
+ return ownWeight(w, name);
10401
+ }
10402
+ function nodeWeight(node, weights) {
10403
+ switch (node.kind) {
10404
+ case "number": return weights.number ?? 1;
10405
+ case "symbol": return symbolWeight(node.name, weights);
10406
+ case "call": return ownWeight(weights.call, node.fn);
10407
+ case "op": return ownWeight(weights.op, node.op);
10408
+ }
10409
+ }
10410
+ function scalarComplexity(node, weights = {}) {
10411
+ let sum = 0;
10412
+ forEachScalar(node, (n) => {
10413
+ sum += nodeWeight(n, weights);
10414
+ });
10415
+ return Math.round(sum);
10416
+ }
10417
+ function scalarSymbols(node) {
10418
+ const names = /* @__PURE__ */ new Set();
10419
+ forEachScalar(node, (n) => {
10420
+ if (n.kind === "symbol") names.add(n.name);
10421
+ });
10422
+ return [...names].sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
10423
+ }
10424
+ function factorToPowerNode(f) {
10425
+ if (Object.is(f.exp, 1)) return f.base;
10426
+ return {
10427
+ kind: "op",
10428
+ op: "^",
10429
+ args: [f.base, {
10430
+ kind: "number",
10431
+ value: f.exp
10432
+ }]
10433
+ };
10434
+ }
10435
+ function collectFactors(node) {
10436
+ if (node.kind === "op" && node.op === "*") {
10437
+ const out = [];
10438
+ for (const child of node.args) out.push(...collectFactors(child));
10439
+ return out;
10440
+ }
10441
+ if (node.kind === "op" && node.op === "^") {
10442
+ assertPowerArity(node.args);
10443
+ const [base, expNode] = node.args;
10444
+ if (expNode.kind === "number" && Number.isFinite(expNode.value)) return collectFactors(base).map((f) => ({
10445
+ base: f.base,
10446
+ exp: f.exp * expNode.value
10447
+ }));
10448
+ return [{
10449
+ base: node,
10450
+ exp: 1
10451
+ }];
10452
+ }
10453
+ return [{
10454
+ base: node,
10455
+ exp: 1
10456
+ }];
10457
+ }
10458
+ function fullyFlattenFactors(factors) {
10459
+ const out = [];
10460
+ for (const f of factors) out.push(...collectFactors(factorToPowerNode(f)));
10461
+ return out;
10462
+ }
10463
+ function encodeCanonicalKey(node) {
10464
+ switch (node.kind) {
10465
+ case "number": return JSON.stringify({
10466
+ k: "n",
10467
+ v: spell(node.value)
10468
+ });
10469
+ case "symbol": return JSON.stringify({
10470
+ k: "s",
10471
+ v: node.name
10472
+ });
10473
+ case "call": {
10474
+ assertCallFn(node.fn);
10475
+ const argKey = encodeCanonicalKey(node.arg);
10476
+ const obj = {
10477
+ k: "c",
10478
+ f: node.fn,
10479
+ a: JSON.parse(argKey)
10480
+ };
10481
+ return JSON.stringify(obj);
10482
+ }
10483
+ case "op":
10484
+ if (node.op === "^") {
10485
+ assertPowerArity(node.args);
10486
+ const [base, exp] = node.args;
10487
+ const obj = {
10488
+ k: "^",
10489
+ b: JSON.parse(encodeCanonicalKey(base)),
10490
+ e: JSON.parse(encodeCanonicalKey(exp))
10491
+ };
10492
+ return JSON.stringify(obj);
10493
+ }
10494
+ if (node.op === "+" || node.op === "*") {
10495
+ const keys = node.args.map((arg) => encodeCanonicalKey(arg)).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
10496
+ const obj = {
10497
+ k: node.op,
10498
+ a: keys.map((k) => JSON.parse(k))
10499
+ };
10500
+ return JSON.stringify(obj);
10501
+ }
10502
+ throw new ScalarBuildError(`scalar canonical: unexpected operator '${node.op}'`);
10503
+ }
10504
+ }
10505
+ function baseKey(node) {
10506
+ return encodeCanonicalKey(node);
10507
+ }
10508
+ function fromProduct(factors) {
10509
+ const flat = fullyFlattenFactors(factors);
10510
+ const coefficients = [];
10511
+ const rest = [];
10512
+ for (const f of flat) if (f.base.kind === "number" && Object.is(f.exp, 1)) coefficients.push(f);
10513
+ else rest.push(f);
10514
+ let coeff = 1;
10515
+ for (const c of coefficients) if (c.base.kind === "number") coeff *= c.base.value;
10516
+ const groups = /* @__PURE__ */ new Map();
10517
+ const groupOrder = [];
10518
+ for (const f of rest) {
10519
+ const key = baseKey(f.base);
10520
+ const existing = groups.get(key);
10521
+ if (existing) existing.sum += f.exp;
10522
+ else {
10523
+ groups.set(key, {
10524
+ base: f.base,
10525
+ sum: f.exp
10526
+ });
10527
+ groupOrder.push(key);
10528
+ }
10529
+ }
10530
+ const materialized = [];
10531
+ for (const key of groupOrder) {
10532
+ const { base, sum } = groups.get(key);
10533
+ if (Object.is(sum, 1)) materialized.push(base);
10534
+ else materialized.push({
10535
+ kind: "op",
10536
+ op: "^",
10537
+ args: [base, {
10538
+ kind: "number",
10539
+ value: sum
10540
+ }]
10541
+ });
10542
+ }
10543
+ const hasOther = materialized.length > 0;
10544
+ if (coefficients.length > 0) {
10545
+ if (!Object.is(coeff, 1) || !hasOther) materialized.push({
10546
+ kind: "number",
10547
+ value: coeff
10548
+ });
10549
+ }
10550
+ if (materialized.length === 0) return {
10551
+ kind: "number",
10552
+ value: 1
10553
+ };
10554
+ if (materialized.length === 1) return materialized[0];
10555
+ return {
10556
+ kind: "op",
10557
+ op: "*",
10558
+ args: materialized.map((node) => ({
10559
+ node,
10560
+ key: encodeCanonicalKey(node)
10561
+ })).sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0).map((entry) => entry.node)
10562
+ };
10563
+ }
10564
+ function fromSum(parts) {
10565
+ const inlined = [];
10566
+ for (const part of parts) if (part.kind === "op" && part.op === "+") inlined.push(...part.args);
10567
+ else inlined.push(part);
10568
+ let numericSum;
10569
+ const nonNumbers = [];
10570
+ for (const part of inlined) if (part.kind === "number") numericSum = numericSum === void 0 ? part.value : numericSum + part.value;
10571
+ else nonNumbers.push(part);
10572
+ const addends = [...nonNumbers];
10573
+ if (numericSum !== void 0) addends.push({
10574
+ kind: "number",
10575
+ value: numericSum
10576
+ });
10577
+ if (addends.length === 0) return {
10578
+ kind: "number",
10579
+ value: 0
10580
+ };
10581
+ if (addends.length === 1) return addends[0];
10582
+ return {
10583
+ kind: "op",
10584
+ op: "+",
10585
+ args: addends.map((node) => ({
10586
+ node,
10587
+ key: encodeCanonicalKey(node)
10588
+ })).sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0).map((entry) => entry.node)
10589
+ };
10590
+ }
10591
+ function canonProductByNumber(node, coeff) {
10592
+ return fromProduct([{
10593
+ base: {
10594
+ kind: "number",
10595
+ value: coeff
10596
+ },
10597
+ exp: 1
10598
+ }, ...collectFactors(node)]);
10599
+ }
10600
+ function inversePower(t) {
10601
+ return {
10602
+ kind: "op",
10603
+ op: "^",
10604
+ args: [t, {
10605
+ kind: "number",
10606
+ value: -1
10607
+ }]
10608
+ };
10609
+ }
10610
+ function canon(node) {
10611
+ switch (node.kind) {
10612
+ case "number":
10613
+ case "symbol": return node;
10614
+ case "call": return {
10615
+ kind: "call",
10616
+ fn: assertCallFn(node.fn),
10617
+ arg: canon(node.arg)
10618
+ };
10619
+ case "op": {
10620
+ const op = node.op;
10621
+ const args = node.args;
10622
+ if (op === "+") {
10623
+ if (args.length === 0) return {
10624
+ kind: "number",
10625
+ value: 0
10626
+ };
10627
+ if (args.length === 1) return canon(args[0]);
10628
+ return fromSum(args.map((a) => canon(a)));
10629
+ }
10630
+ if (op === "-") {
10631
+ if (args.length === 0) return {
10632
+ kind: "number",
10633
+ value: 0
10634
+ };
10635
+ if (args.length === 1) return canon(args[0]);
10636
+ const parts = [canon(args[0])];
10637
+ for (let i = 1; i < args.length; i++) parts.push(canonProductByNumber(canon(args[i]), -1));
10638
+ return fromSum(parts);
10639
+ }
10640
+ if (op === "*") {
10641
+ if (args.length === 0) return {
10642
+ kind: "number",
10643
+ value: 1
10644
+ };
10645
+ if (args.length === 1) return canon(args[0]);
10646
+ const factors = [];
10647
+ for (const a of args) factors.push(...collectFactors(canon(a)));
10648
+ return fromProduct(factors);
10649
+ }
10650
+ if (op === "/") {
10651
+ if (args.length === 0) return {
10652
+ kind: "number",
10653
+ value: 1
10654
+ };
10655
+ if (args.length === 1) return canon(args[0]);
10656
+ let factors = collectFactors(canon(args[0]));
10657
+ for (let i = 1; i < args.length; i++) factors = factors.concat(collectFactors(inversePower(canon(args[i]))));
10658
+ return fromProduct(factors);
10659
+ }
10660
+ if (op === "^") {
10661
+ assertPowerArity(args);
10662
+ const base = canon(args[0]);
10663
+ const expCanon = canon(args[1]);
10664
+ if (expCanon.kind === "number" && Number.isFinite(expCanon.value)) return fromProduct(collectFactors(base).map((f) => ({
10665
+ base: f.base,
10666
+ exp: f.exp * expCanon.value
10667
+ })));
10668
+ return {
10669
+ kind: "op",
10670
+ op: "^",
10671
+ args: [base, expCanon]
10672
+ };
10673
+ }
10674
+ throw new ScalarBuildError(`unknown scalar operator '${String(op)}'`);
10675
+ }
10676
+ }
10677
+ }
10678
+ function scalarCanonical(node) {
10679
+ return canon(node);
10680
+ }
10681
+ function scalarCanonicalKey(node) {
10682
+ return encodeCanonicalKey(scalarCanonical(node));
10683
+ }
10684
+ function scalarEquals(a, b) {
10685
+ return scalarCanonicalKey(a) === scalarCanonicalKey(b);
10686
+ }
10687
+ //#endregion
10688
+ //#region src/scalar-enumerate.ts
10689
+ function nodeOperatorName(node) {
10690
+ if (node.kind === "op") return node.op;
10691
+ if (node.kind === "call") return node.fn;
10692
+ return null;
10693
+ }
10694
+ function countNamedInSubtree(root, inner) {
10695
+ let count = 0;
10696
+ function walk(n, isRoot) {
10697
+ if (!isRoot && nodeOperatorName(n) === inner) count += 1;
10698
+ if (n.kind === "call") walk(n.arg, false);
10699
+ else if (n.kind === "op") for (const child of n.args) walk(child, false);
10700
+ }
10701
+ walk(root, true);
10702
+ return count;
10703
+ }
10704
+ function argumentLimitsOk(node, argumentLimits) {
10705
+ const name = nodeOperatorName(node);
10706
+ if (name === null || !hasOwnProperty(argumentLimits, name)) return true;
10707
+ const limits = argumentLimits[name];
10708
+ if (node.kind === "call") {
10709
+ const cap = limits[0];
10710
+ if (cap !== void 0 && cap !== -1 && scalarSize(node.arg) > cap) return false;
10711
+ return true;
10712
+ }
10713
+ if (node.kind === "op") for (let i = 0; i < node.args.length; i += 1) {
10714
+ const cap = limits[i];
10715
+ if (cap !== void 0 && cap !== -1 && scalarSize(node.args[i]) > cap) return false;
10716
+ }
10717
+ return true;
10718
+ }
10719
+ function nestingOk(node, nesting) {
10720
+ let ok = true;
10721
+ forEachScalar(node, (sub) => {
10722
+ const outer = nodeOperatorName(sub);
10723
+ if (outer === null || !hasOwnProperty(nesting, outer)) return;
10724
+ const innerLimits = nesting[outer];
10725
+ for (const inner of Object.keys(innerLimits)) {
10726
+ if (!hasOwnProperty(innerLimits, inner)) continue;
10727
+ const max = innerLimits[inner];
10728
+ if (countNamedInSubtree(sub, inner) > max) ok = false;
10729
+ }
10730
+ });
10731
+ return ok;
10732
+ }
10733
+ function symbolAllowed(grammar, name) {
10734
+ if (grammar.variables.includes(name)) return true;
10735
+ const params = grammar.parameters;
10736
+ return params !== void 0 && params.includes(name);
10737
+ }
10738
+ function nodeStructureOk(grammar, node) {
10739
+ switch (node.kind) {
10740
+ case "number": return Number.isFinite(node.value);
10741
+ case "symbol": return symbolAllowed(grammar, node.name);
10742
+ case "call":
10743
+ if (!grammar.unary.includes(node.fn)) return false;
10744
+ return nodeStructureOk(grammar, node.arg);
10745
+ case "op":
10746
+ if (!grammar.binary.includes(node.op)) return false;
10747
+ if (node.args.length !== 2) return false;
10748
+ return nodeStructureOk(grammar, node.args[0]) && nodeStructureOk(grammar, node.args[1]);
10749
+ }
10750
+ }
10751
+ function scalarGrammarAccepts(grammar, node) {
10752
+ if (!nodeStructureOk(grammar, node)) return false;
10753
+ if (!(scalarSize(node) <= grammar.maxSize)) return false;
10754
+ const maxDepth = grammar.maxDepth;
10755
+ if (maxDepth !== void 0 && !(scalarDepth(node) <= maxDepth)) return false;
10756
+ if (grammar.argumentLimits !== void 0) {
10757
+ let limitsOk = true;
10758
+ forEachScalar(node, (sub) => {
10759
+ if (!argumentLimitsOk(sub, grammar.argumentLimits)) limitsOk = false;
10760
+ });
10761
+ if (!limitsOk) return false;
10762
+ }
10763
+ if (grammar.nesting !== void 0 && !nestingOk(node, grammar.nesting)) return false;
10764
+ return true;
10765
+ }
10766
+ const INTERRUPT_CHECK_INTERVAL = 128;
10767
+ function assertEnumerationInputs(grammar, budget) {
10768
+ if (Number.isNaN(grammar.maxSize)) throw new RangeError("enumerateScalars: grammar.maxSize must not be NaN");
10769
+ if (grammar.maxDepth !== void 0 && Number.isNaN(grammar.maxDepth)) throw new RangeError("enumerateScalars: grammar.maxDepth must not be NaN");
10770
+ if (Number.isNaN(budget.maxCandidates) || budget.maxCandidates < 0) throw new RangeError("enumerateScalars: budget.maxCandidates must be a number >= 0");
10771
+ if (budget.maxWallClockMs !== void 0 && Number.isNaN(budget.maxWallClockMs)) throw new RangeError("enumerateScalars: budget.maxWallClockMs must not be NaN");
10772
+ }
10773
+ function* enumerateScalars(grammar, budget, signal) {
10774
+ assertEnumerationInputs(grammar, budget);
10775
+ const b = createScalarBuilder();
10776
+ const yieldedKeys = /* @__PURE__ */ new Set();
10777
+ const pooledKeys = /* @__PURE__ */ new Set();
10778
+ const pool = [];
10779
+ const bySize = [];
10780
+ let yieldCount = 0;
10781
+ let sinceCheck = 0;
10782
+ const start = performance.now();
10783
+ const admitMonotone = grammar.admitMonotone === true;
10784
+ const interrupted = () => {
10785
+ if (signal?.aborted) return "cancelled";
10786
+ if (budget.maxWallClockMs !== void 0 && performance.now() - start >= budget.maxWallClockMs) return "time-limit";
10787
+ return null;
10788
+ };
10789
+ const addToPool = (node, size, key) => {
10790
+ if (pooledKeys.has(key)) return;
10791
+ pooledKeys.add(key);
10792
+ const entry = {
10793
+ node,
10794
+ size
10795
+ };
10796
+ pool.push(entry);
10797
+ (bySize[size] ??= []).push(entry);
10798
+ };
10799
+ const consider = function* (node, size) {
10800
+ sinceCheck += 1;
10801
+ if (sinceCheck >= INTERRUPT_CHECK_INTERVAL) {
10802
+ sinceCheck = 0;
10803
+ const stop = interrupted();
10804
+ if (stop !== null) return stop;
10805
+ }
10806
+ if (!scalarGrammarAccepts(grammar, node)) return "ok";
10807
+ const key = scalarCanonicalKey(node);
10808
+ if (yieldedKeys.has(key)) return "ok";
10809
+ if (!(grammar.admit === void 0 || grammar.admit(node))) {
10810
+ if (!admitMonotone) addToPool(node, size, key);
10811
+ return "ok";
10812
+ }
10813
+ const stop = interrupted() ?? (yieldCount >= budget.maxCandidates ? "candidate-limit" : null);
10814
+ if (stop !== null) return stop;
10815
+ sinceCheck = 0;
10816
+ addToPool(node, size, key);
10817
+ yieldedKeys.add(key);
10818
+ yieldCount += 1;
10819
+ yield node;
10820
+ return "ok";
10821
+ };
10822
+ for (let size = 1; size <= grammar.maxSize; size += 1) {
10823
+ if (size === 1) {
10824
+ const leaves = [];
10825
+ for (const name of grammar.variables) leaves.push(b.symbol(name));
10826
+ for (const name of grammar.parameters ?? []) leaves.push(b.symbol(name));
10827
+ for (const value of grammar.literals ?? []) leaves.push(b.number(value));
10828
+ for (const leaf of leaves) {
10829
+ const stop = yield* consider(leaf, 1);
10830
+ if (stop !== "ok") return stop;
10831
+ }
10832
+ continue;
10833
+ }
10834
+ const poolAtStart = pool.length;
10835
+ for (const op of grammar.binary) for (let i = 0; i < poolAtStart; i += 1) {
10836
+ const left = pool[i];
10837
+ const rightSize = size - 1 - left.size;
10838
+ if (rightSize < 1) continue;
10839
+ for (const right of bySize[rightSize] ?? []) {
10840
+ const stop = yield* consider(b.op(op, [left.node, right.node]), size);
10841
+ if (stop !== "ok") return stop;
10842
+ }
10843
+ }
10844
+ for (const fn of grammar.unary) for (const arg of bySize[size - 1] ?? []) {
10845
+ const stop = yield* consider({
10846
+ kind: "call",
10847
+ fn,
10848
+ arg: arg.node
10849
+ }, size);
10850
+ if (stop !== "ok") return stop;
10851
+ }
10852
+ }
10853
+ return "exhausted-space";
10854
+ }
10855
+ //#endregion
10856
+ //#region src/transform/utils/shortCircuitBinary.ts
10857
+ function evalShortCircuitBinary(args, scope, op, absorb) {
10858
+ const left = args[0].compile().evaluate(scope);
10859
+ if (!isCollection(left)) {
10860
+ const early = absorb(left);
10861
+ if (early.take) return early.value;
10862
+ }
10863
+ return op(left, args[1].compile().evaluate(scope));
10864
+ }
10317
10865
  const createAndTransform = /* #__PURE__ */ factory("and", ["and"], ({ and }) => {
10318
10866
  function andTransform(args, _math, scope) {
10319
- const condition1 = args[0].compile().evaluate(scope);
10320
- if (!isCollection(condition1) && !and(condition1, true)) return false;
10321
- return and(condition1, args[1].compile().evaluate(scope));
10867
+ return evalShortCircuitBinary(args, scope, and, (left) => !and(left, true) ? {
10868
+ take: true,
10869
+ value: false
10870
+ } : { take: false });
10322
10871
  }
10323
10872
  andTransform.rawArgs = true;
10324
10873
  return andTransform;
10325
10874
  }, { isTransformFunction: true });
10326
10875
  const createBitAndTransform = /* #__PURE__ */ factory("bitAnd", ["bitAnd"], ({ bitAnd }) => {
10327
10876
  function bitAndTransform(args, _math, scope) {
10328
- const condition1 = args[0].compile().evaluate(scope);
10329
- if (!isCollection(condition1)) {
10330
- if (isNaN(condition1)) return NaN;
10331
- if (condition1 === 0 || condition1 === false) return 0;
10332
- }
10333
- return bitAnd(condition1, args[1].compile().evaluate(scope));
10877
+ return evalShortCircuitBinary(args, scope, bitAnd, (left) => {
10878
+ if (isNaN(left)) return {
10879
+ take: true,
10880
+ value: NaN
10881
+ };
10882
+ if (left === 0 || left === false) return {
10883
+ take: true,
10884
+ value: 0
10885
+ };
10886
+ return { take: false };
10887
+ });
10334
10888
  }
10335
10889
  bitAndTransform.rawArgs = true;
10336
10890
  return bitAndTransform;
10337
10891
  }, { isTransformFunction: true });
10338
10892
  const createBitOrTransform = /* #__PURE__ */ factory("bitOr", ["bitOr"], ({ bitOr }) => {
10339
10893
  function bitOrTransform(args, _math, scope) {
10340
- const condition1 = args[0].compile().evaluate(scope);
10341
- if (!isCollection(condition1)) {
10342
- if (isNaN(condition1)) return NaN;
10343
- if (condition1 === -1) return -1;
10344
- if (condition1 === true) return 1;
10345
- }
10346
- return bitOr(condition1, args[1].compile().evaluate(scope));
10894
+ return evalShortCircuitBinary(args, scope, bitOr, (left) => {
10895
+ if (isNaN(left)) return {
10896
+ take: true,
10897
+ value: NaN
10898
+ };
10899
+ if (left === -1) return {
10900
+ take: true,
10901
+ value: -1
10902
+ };
10903
+ if (left === true) return {
10904
+ take: true,
10905
+ value: 1
10906
+ };
10907
+ return { take: false };
10908
+ });
10347
10909
  }
10348
10910
  bitOrTransform.rawArgs = true;
10349
10911
  return bitOrTransform;
@@ -10374,28 +10936,15 @@ const createConcatTransform = /* #__PURE__ */ factory("concat", ["typed", "conca
10374
10936
  } });
10375
10937
  }, { isTransformFunction: true });
10376
10938
  //#endregion
10377
- //#region src/transform/cumsum.transform.ts
10378
- const name$20 = "cumsum";
10379
- const createCumSumTransform = /* #__PURE__ */ factory(name$20, ["typed", "cumsum"], ({ typed, cumsum }) => {
10380
- return typed(name$20, { "...any": function(args) {
10381
- if (args.length === 2 && isCollection(args[0])) {
10382
- const dim = args[1];
10383
- if (isNumber(dim)) args[1] = dim - 1;
10384
- else if (isBigNumber(dim)) args[1] = dim.minus(1);
10385
- }
10386
- try {
10387
- return cumsum(...args);
10388
- } catch (err) {
10389
- throw errorTransform(err);
10390
- }
10391
- } });
10392
- }, { isTransformFunction: true });
10393
- //#endregion
10394
10939
  //#region src/transform/utils/dimToZeroBase.ts
10395
10940
  function dimToZeroBase(dim) {
10396
10941
  if (isNumber(dim)) return dim - 1;
10397
- else if (isBigNumber(dim)) return dim.minus(1);
10398
- else return dim;
10942
+ else if (isBigNumber(dim)) {
10943
+ const bn = dim;
10944
+ if (typeof bn.sub === "function") return bn.sub(1);
10945
+ if (typeof bn.minus === "function") return bn.minus(1);
10946
+ return dim;
10947
+ } else return dim;
10399
10948
  }
10400
10949
  function isNumberOrBigNumber(n) {
10401
10950
  return isNumber(n) || isBigNumber(n);
@@ -10411,6 +10960,19 @@ function lastDimToZeroBase(args) {
10411
10960
  return args;
10412
10961
  }
10413
10962
  //#endregion
10963
+ //#region src/transform/cumsum.transform.ts
10964
+ const name$20 = "cumsum";
10965
+ const createCumSumTransform = /* #__PURE__ */ factory(name$20, ["typed", "cumsum"], ({ typed, cumsum }) => {
10966
+ return typed(name$20, { "...any": function(args) {
10967
+ args = lastDimToZeroBase(args);
10968
+ try {
10969
+ return cumsum(...args);
10970
+ } catch (err) {
10971
+ throw errorTransform(err);
10972
+ }
10973
+ } });
10974
+ }, { isTransformFunction: true });
10975
+ //#endregion
10414
10976
  //#region src/transform/diff.transform.ts
10415
10977
  const name$19 = "diff";
10416
10978
  const createDiffTransform = /* #__PURE__ */ factory(name$19, ["typed", "diff"], ({ typed, diff }) => {
@@ -10440,6 +11002,33 @@ function compileInlineExpression(expression, math, scope) {
10440
11002
  return eq.evaluate(subScope);
10441
11003
  };
10442
11004
  }
11005
+ //#endregion
11006
+ //#region src/transform/utils/prepareTransformArgs.ts
11007
+ function compileAndEvaluate(arg, scope) {
11008
+ return arg.compile().evaluate(scope);
11009
+ }
11010
+ function prepareTransformArrayAndCallback(args, math, scope) {
11011
+ const callbackArity = args.length - 1;
11012
+ let x = args[0];
11013
+ let callback = args[callbackArity];
11014
+ if (x) x = compileAndEvaluate(x, scope);
11015
+ if (callback) {
11016
+ if (isSymbolNode(callback) || isFunctionAssignmentNode(callback)) callback = compileAndEvaluate(callback, scope);
11017
+ else callback = compileInlineExpression(callback, math, scope);
11018
+ }
11019
+ return {
11020
+ x,
11021
+ callback,
11022
+ callbackArity
11023
+ };
11024
+ }
11025
+ function applyArrayCallbackTransform(args, math, scope, op, transformCallback) {
11026
+ if (args.length === 0) return op();
11027
+ const x = args[0];
11028
+ if (args.length === 1) return op(x);
11029
+ const prepared = prepareTransformArrayAndCallback(args, math, scope);
11030
+ return op(prepared.x, transformCallback(prepared.callback, prepared.callbackArity));
11031
+ }
10443
11032
  const createTransformCallback = /* #__PURE__ */ factory("transformCallback", ["typed"], ({ typed }) => {
10444
11033
  return function(callback, numberOfArrays) {
10445
11034
  if (typed.isTypedFunction(callback)) return _transformTypedCallbackFunction(callback, numberOfArrays);
@@ -10479,45 +11068,19 @@ function _transformDims(dims) {
10479
11068
  return dims.map((dim) => dim + 1);
10480
11069
  }
10481
11070
  const createFilterTransform = /* #__PURE__ */ factory("filter", ["typed", "filter"], ({ typed, filter }) => {
11071
+ const transformCallback = createTransformCallback({ typed });
10482
11072
  function filterTransform(args, math, scope) {
10483
- const transformCallback = createTransformCallback({ typed });
10484
- if (args.length === 0) return filter();
10485
- let x = args[0];
10486
- if (args.length === 1) return filter(x);
10487
- const N = args.length - 1;
10488
- let callback = args[N];
10489
- if (x) x = _compileAndEvaluate(x, scope);
10490
- if (callback) {
10491
- if (isSymbolNode(callback) || isFunctionAssignmentNode(callback)) callback = _compileAndEvaluate(callback, scope);
10492
- else callback = compileInlineExpression(callback, math, scope);
10493
- }
10494
- return filter(x, transformCallback(callback, N));
11073
+ return applyArrayCallbackTransform(args, math, scope, filter, transformCallback);
10495
11074
  }
10496
11075
  filterTransform.rawArgs = true;
10497
- function _compileAndEvaluate(arg, scope) {
10498
- return arg.compile().evaluate(scope);
10499
- }
10500
11076
  return filterTransform;
10501
11077
  }, { isTransformFunction: true });
10502
11078
  const createForEachTransform = /* #__PURE__ */ factory("forEach", ["typed", "forEach"], ({ typed, forEach }) => {
10503
11079
  const transformCallback = createTransformCallback({ typed });
10504
11080
  function forEachTransform(args, math, scope) {
10505
- if (args.length === 0) return forEach();
10506
- let x = args[0];
10507
- if (args.length === 1) return forEach(x);
10508
- const N = args.length - 1;
10509
- let callback = args[N];
10510
- if (x) x = _compileAndEvaluate(x, scope);
10511
- if (callback) {
10512
- if (isSymbolNode(callback) || isFunctionAssignmentNode(callback)) callback = _compileAndEvaluate(callback, scope);
10513
- else callback = compileInlineExpression(callback, math, scope);
10514
- }
10515
- return forEach(x, transformCallback(callback, N));
11081
+ return applyArrayCallbackTransform(args, math, scope, forEach, transformCallback);
10516
11082
  }
10517
11083
  forEachTransform.rawArgs = true;
10518
- function _compileAndEvaluate(arg, scope) {
10519
- return arg.compile().evaluate(scope);
10520
- }
10521
11084
  return forEachTransform;
10522
11085
  }, { isTransformFunction: true });
10523
11086
  const createIndexTransform = /* #__PURE__ */ factory("index", ["Index", "getMatrixDataType"], ({ Index, getMatrixDataType }) => {
@@ -10567,8 +11130,7 @@ const createMapTransform = /* #__PURE__ */ factory("map", ["typed", "map"], ({ t
10567
11130
  const createMapSlicesTransform = /* #__PURE__ */ factory("mapSlices", ["typed", "mapSlices"], ({ typed, mapSlices }) => {
10568
11131
  return typed("mapSlices", { "...any": function(args) {
10569
11132
  const dim = args[1];
10570
- if (isNumber(dim)) args[1] = dim - 1;
10571
- else if (isBigNumber(dim)) args[1] = dim.minus(1);
11133
+ if (isNumberOrBigNumber(dim)) args[1] = dimToZeroBase(dim);
10572
11134
  try {
10573
11135
  return mapSlices(...args);
10574
11136
  } catch (err) {
@@ -10617,9 +11179,10 @@ const createNullishTransform = /* #__PURE__ */ factory("nullish", ["nullish"], (
10617
11179
  }, { isTransformFunction: true });
10618
11180
  const createOrTransform = /* #__PURE__ */ factory("or", ["or"], ({ or }) => {
10619
11181
  function orTransform(args, _math, scope) {
10620
- const condition1 = args[0].compile().evaluate(scope);
10621
- if (!isCollection(condition1) && or(condition1, false)) return true;
10622
- return or(condition1, args[1].compile().evaluate(scope));
11182
+ return evalShortCircuitBinary(args, scope, or, (left) => or(left, false) ? {
11183
+ take: true,
11184
+ value: true
11185
+ } : { take: false });
10623
11186
  }
10624
11187
  orTransform.rawArgs = true;
10625
11188
  return orTransform;
@@ -10727,4 +11290,4 @@ const createVarianceTransform = /* #__PURE__ */ factory(name, ["typed", "varianc
10727
11290
  } });
10728
11291
  }, { isTransformFunction: true });
10729
11292
  //#endregion
10730
- export { SCALAR_FUNCTIONS, SCALAR_FUNCTION_LOWERING, ScalarBuildError, compile, compileExpression, createAccessorNode, createAndTransform, createArrayNode, createAssignmentNode, createBitAndTransform, createBitOrTransform, createBlockNode, createColumnTransform, createConcatTransform, createConditionalNode, createConstantNode, createCumSumTransform, createDiffTransform, createEvaluate, createFilterTransform, createForEachTransform, createFunctionAssignmentNode, createFunctionNode, createHelpClass, createIndexNode, createIndexTransform, createMapSlicesTransform, createMapTransform, createMaxTransform, createMeanTransform, createMinTransform, createNode, createNullishTransform, createObjectNode, createOperatorNode, createOrTransform, createParenthesisNode, createParse, createParser, createParserClass, createPrintTransform, createQuantileSeqTransform, createRangeNode, createRangeTransform, createRelationalNode, createRowTransform, createScalarBuilder, createStdTransform, createSubsetTransform, createSumTransform, createSymbolNode, createVarianceTransform, embeddedDocs, escapeMathML, getAssociativity, getOperator, getPrecedence, isAssociativeWith, keywords, mathMLDocument, mathMLError, properties, toMathMLSymbol };
11293
+ export { SCALAR_FUNCTIONS, SCALAR_FUNCTION_LOWERING, ScalarBuildError, compile, compileExpression, createAccessorNode, createAndTransform, createArrayNode, createAssignmentNode, createBitAndTransform, createBitOrTransform, createBlockNode, createColumnTransform, createConcatTransform, createConditionalNode, createConstantNode, createCumSumTransform, createDiffTransform, createEvaluate, createFilterTransform, createForEachTransform, createFunctionAssignmentNode, createFunctionNode, createHelpClass, createIndexNode, createIndexTransform, createMapSlicesTransform, createMapTransform, createMaxTransform, createMeanTransform, createMinTransform, createNode, createNullishTransform, createObjectNode, createOperatorNode, createOrTransform, createParenthesisNode, createParse, createParser, createParserClass, createPrintTransform, createQuantileSeqTransform, createRangeNode, createRangeTransform, createRelationalNode, createRowTransform, createScalarBuilder, createStdTransform, createSubsetTransform, createSumTransform, createSymbolNode, createVarianceTransform, embeddedDocs, enumerateScalars, escapeMathML, forEachScalar, getAssociativity, getOperator, getPrecedence, getStringSubset, isAssociativeWith, keywords, mapScalar, mathMLDocument, mathMLError, properties, scalarCanonical, scalarCanonicalKey, scalarComplexity, scalarDepth, scalarEquals, scalarGrammarAccepts, scalarSize, scalarSymbols, setStringSubset, toMathMLSymbol };