@cortex-js/compute-engine 0.31.0 → 0.32.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 (153) hide show
  1. package/dist/compute-engine.esm.js +1843 -249
  2. package/dist/compute-engine.min.esm.js +38 -38
  3. package/dist/compute-engine.min.umd.js +38 -38
  4. package/dist/compute-engine.umd.js +1843 -249
  5. package/dist/math-json.esm.js +2 -2
  6. package/dist/math-json.min.esm.js +2 -2
  7. package/dist/math-json.min.umd.js +2 -2
  8. package/dist/math-json.umd.js +2 -2
  9. package/dist/types/common/ansi-codes.d.ts +1 -1
  10. package/dist/types/common/configuration-change.d.ts +1 -1
  11. package/dist/types/common/fuzzy-string-match.d.ts +1 -1
  12. package/dist/types/common/grapheme-splitter.d.ts +1 -1
  13. package/dist/types/common/interruptible.d.ts +1 -1
  14. package/dist/types/common/one-of.d.ts +1 -1
  15. package/dist/types/common/signals.d.ts +1 -1
  16. package/dist/types/common/type/ast-nodes.d.ts +1 -1
  17. package/dist/types/common/type/boxed-type.d.ts +1 -1
  18. package/dist/types/common/type/lexer.d.ts +1 -1
  19. package/dist/types/common/type/parse.d.ts +1 -1
  20. package/dist/types/common/type/parser.d.ts +1 -1
  21. package/dist/types/common/type/primitive.d.ts +1 -1
  22. package/dist/types/common/type/serialize.d.ts +1 -1
  23. package/dist/types/common/type/subtype.d.ts +1 -1
  24. package/dist/types/common/type/type-builder.d.ts +1 -1
  25. package/dist/types/common/type/types.d.ts +1 -1
  26. package/dist/types/common/type/utils.d.ts +1 -1
  27. package/dist/types/common/utils.d.ts +1 -1
  28. package/dist/types/compute-engine/assume.d.ts +1 -1
  29. package/dist/types/compute-engine/boxed-expression/abstract-boxed-expression.d.ts +1 -1
  30. package/dist/types/compute-engine/boxed-expression/apply.d.ts +1 -1
  31. package/dist/types/compute-engine/boxed-expression/arithmetic-add.d.ts +1 -1
  32. package/dist/types/compute-engine/boxed-expression/arithmetic-mul-div.d.ts +1 -1
  33. package/dist/types/compute-engine/boxed-expression/arithmetic-power.d.ts +1 -1
  34. package/dist/types/compute-engine/boxed-expression/ascii-math.d.ts +1 -1
  35. package/dist/types/compute-engine/boxed-expression/box.d.ts +1 -1
  36. package/dist/types/compute-engine/boxed-expression/boxed-dictionary.d.ts +1 -1
  37. package/dist/types/compute-engine/boxed-expression/boxed-function.d.ts +1 -1
  38. package/dist/types/compute-engine/boxed-expression/boxed-number.d.ts +1 -1
  39. package/dist/types/compute-engine/boxed-expression/boxed-operator-definition.d.ts +1 -1
  40. package/dist/types/compute-engine/boxed-expression/boxed-patterns.d.ts +1 -1
  41. package/dist/types/compute-engine/boxed-expression/boxed-string.d.ts +1 -1
  42. package/dist/types/compute-engine/boxed-expression/boxed-symbol.d.ts +1 -1
  43. package/dist/types/compute-engine/boxed-expression/boxed-tensor.d.ts +1 -1
  44. package/dist/types/compute-engine/boxed-expression/boxed-value-definition.d.ts +1 -1
  45. package/dist/types/compute-engine/boxed-expression/cache.d.ts +1 -1
  46. package/dist/types/compute-engine/boxed-expression/canonical-utils.d.ts +1 -1
  47. package/dist/types/compute-engine/boxed-expression/canonical.d.ts +1 -1
  48. package/dist/types/compute-engine/boxed-expression/compare.d.ts +1 -1
  49. package/dist/types/compute-engine/boxed-expression/expand.d.ts +1 -1
  50. package/dist/types/compute-engine/boxed-expression/expression-map.d.ts +1 -1
  51. package/dist/types/compute-engine/boxed-expression/factor.d.ts +1 -1
  52. package/dist/types/compute-engine/boxed-expression/flatten.d.ts +1 -1
  53. package/dist/types/compute-engine/boxed-expression/hold.d.ts +1 -1
  54. package/dist/types/compute-engine/boxed-expression/match.d.ts +23 -1
  55. package/dist/types/compute-engine/boxed-expression/negate.d.ts +1 -1
  56. package/dist/types/compute-engine/boxed-expression/numerics.d.ts +1 -1
  57. package/dist/types/compute-engine/boxed-expression/order.d.ts +1 -1
  58. package/dist/types/compute-engine/boxed-expression/polynomials.d.ts +1 -1
  59. package/dist/types/compute-engine/boxed-expression/product.d.ts +1 -1
  60. package/dist/types/compute-engine/boxed-expression/rules.d.ts +1 -1
  61. package/dist/types/compute-engine/boxed-expression/serialize.d.ts +1 -1
  62. package/dist/types/compute-engine/boxed-expression/sgn.d.ts +1 -1
  63. package/dist/types/compute-engine/boxed-expression/simplify.d.ts +1 -1
  64. package/dist/types/compute-engine/boxed-expression/solve.d.ts +1 -1
  65. package/dist/types/compute-engine/boxed-expression/terms.d.ts +1 -1
  66. package/dist/types/compute-engine/boxed-expression/trigonometry.d.ts +1 -1
  67. package/dist/types/compute-engine/boxed-expression/utils.d.ts +1 -1
  68. package/dist/types/compute-engine/boxed-expression/validate.d.ts +1 -1
  69. package/dist/types/compute-engine/collection-utils.d.ts +1 -1
  70. package/dist/types/compute-engine/compilation/base-compiler.d.ts +1 -1
  71. package/dist/types/compute-engine/compilation/javascript-target.d.ts +1 -1
  72. package/dist/types/compute-engine/compilation/types.d.ts +1 -1
  73. package/dist/types/compute-engine/cost-function.d.ts +1 -1
  74. package/dist/types/compute-engine/function-utils.d.ts +1 -1
  75. package/dist/types/compute-engine/global-types.d.ts +1 -1
  76. package/dist/types/compute-engine/index.d.ts +1 -1
  77. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-algebra.d.ts +1 -1
  78. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-arithmetic.d.ts +1 -1
  79. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-calculus.d.ts +1 -1
  80. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-complex.d.ts +1 -1
  81. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-core.d.ts +1 -1
  82. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-linear-algebra.d.ts +1 -1
  83. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-logic.d.ts +1 -1
  84. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-other.d.ts +1 -1
  85. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-relational-operators.d.ts +1 -1
  86. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-sets.d.ts +1 -1
  87. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-statistics.d.ts +1 -1
  88. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-symbols.d.ts +1 -1
  89. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-trigonometry.d.ts +1 -1
  90. package/dist/types/compute-engine/latex-syntax/dictionary/definitions.d.ts +1 -1
  91. package/dist/types/compute-engine/latex-syntax/parse-symbol.d.ts +1 -1
  92. package/dist/types/compute-engine/latex-syntax/parse.d.ts +14 -1
  93. package/dist/types/compute-engine/latex-syntax/serialize-number.d.ts +1 -1
  94. package/dist/types/compute-engine/latex-syntax/serializer-style.d.ts +1 -1
  95. package/dist/types/compute-engine/latex-syntax/serializer.d.ts +1 -1
  96. package/dist/types/compute-engine/latex-syntax/tokenizer.d.ts +1 -1
  97. package/dist/types/compute-engine/latex-syntax/types.d.ts +75 -24
  98. package/dist/types/compute-engine/latex-syntax/utils.d.ts +1 -1
  99. package/dist/types/compute-engine/library/arithmetic.d.ts +1 -1
  100. package/dist/types/compute-engine/library/calculus.d.ts +1 -1
  101. package/dist/types/compute-engine/library/collections.d.ts +1 -1
  102. package/dist/types/compute-engine/library/combinatorics.d.ts +1 -1
  103. package/dist/types/compute-engine/library/complex.d.ts +1 -1
  104. package/dist/types/compute-engine/library/control-structures.d.ts +1 -1
  105. package/dist/types/compute-engine/library/core.d.ts +1 -1
  106. package/dist/types/compute-engine/library/invisible-operator.d.ts +1 -1
  107. package/dist/types/compute-engine/library/library.d.ts +1 -1
  108. package/dist/types/compute-engine/library/linear-algebra.d.ts +1 -1
  109. package/dist/types/compute-engine/library/logic-analysis.d.ts +64 -0
  110. package/dist/types/compute-engine/library/logic-utils.d.ts +58 -0
  111. package/dist/types/compute-engine/library/logic.d.ts +2 -1
  112. package/dist/types/compute-engine/library/number-theory.d.ts +1 -1
  113. package/dist/types/compute-engine/library/polynomials.d.ts +1 -1
  114. package/dist/types/compute-engine/library/random-expression.d.ts +1 -1
  115. package/dist/types/compute-engine/library/relational-operator.d.ts +1 -1
  116. package/dist/types/compute-engine/library/sets.d.ts +1 -1
  117. package/dist/types/compute-engine/library/statistics.d.ts +1 -1
  118. package/dist/types/compute-engine/library/trigonometry.d.ts +1 -1
  119. package/dist/types/compute-engine/library/utils.d.ts +1 -1
  120. package/dist/types/compute-engine/numeric-value/big-numeric-value.d.ts +1 -1
  121. package/dist/types/compute-engine/numeric-value/exact-numeric-value.d.ts +1 -1
  122. package/dist/types/compute-engine/numeric-value/machine-numeric-value.d.ts +1 -1
  123. package/dist/types/compute-engine/numeric-value/types.d.ts +1 -1
  124. package/dist/types/compute-engine/numerics/bigint.d.ts +1 -1
  125. package/dist/types/compute-engine/numerics/expression.d.ts +1 -1
  126. package/dist/types/compute-engine/numerics/interval.d.ts +1 -1
  127. package/dist/types/compute-engine/numerics/monte-carlo.d.ts +1 -1
  128. package/dist/types/compute-engine/numerics/numeric-bigint.d.ts +1 -1
  129. package/dist/types/compute-engine/numerics/numeric-bignum.d.ts +1 -1
  130. package/dist/types/compute-engine/numerics/numeric-complex.d.ts +1 -1
  131. package/dist/types/compute-engine/numerics/numeric.d.ts +1 -1
  132. package/dist/types/compute-engine/numerics/primes.d.ts +1 -1
  133. package/dist/types/compute-engine/numerics/rationals.d.ts +1 -1
  134. package/dist/types/compute-engine/numerics/richardson.d.ts +1 -1
  135. package/dist/types/compute-engine/numerics/special-functions.d.ts +1 -1
  136. package/dist/types/compute-engine/numerics/statistics.d.ts +1 -1
  137. package/dist/types/compute-engine/numerics/strings.d.ts +1 -1
  138. package/dist/types/compute-engine/numerics/types.d.ts +1 -1
  139. package/dist/types/compute-engine/symbolic/antiderivative.d.ts +1 -1
  140. package/dist/types/compute-engine/symbolic/derivative.d.ts +1 -1
  141. package/dist/types/compute-engine/symbolic/distribute.d.ts +1 -1
  142. package/dist/types/compute-engine/symbolic/simplify-product.d.ts +6 -0
  143. package/dist/types/compute-engine/symbolic/simplify-rules.d.ts +1 -1
  144. package/dist/types/compute-engine/symbolic/simplify-sum.d.ts +6 -0
  145. package/dist/types/compute-engine/tensor/tensor-fields.d.ts +1 -1
  146. package/dist/types/compute-engine/tensor/tensors.d.ts +1 -1
  147. package/dist/types/compute-engine/types.d.ts +1 -1
  148. package/dist/types/compute-engine.d.ts +1 -1
  149. package/dist/types/math-json/symbols.d.ts +1 -1
  150. package/dist/types/math-json/types.d.ts +1 -1
  151. package/dist/types/math-json/utils.d.ts +1 -1
  152. package/dist/types/math-json.d.ts +2 -2
  153. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- /** Compute Engine 0.31.0 */
1
+ /** Compute Engine 0.32.1 */
2
2
  (function(global,factory){typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'],factory):(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ComputeEngine = {}));})(this, (function (exports) { 'use strict';
3
3
  var ComputeEngine = (() => {
4
4
  var __defProp = Object.defineProperty;
@@ -7869,7 +7869,11 @@ var ComputeEngine = (() => {
7869
7869
  if (denom === 2) {
7870
7870
  return serializer.serialize(["Divide", "1", ["Sqrt", base]]);
7871
7871
  }
7872
- return serializer.serialize(["Divide", "1", ["Root", base, denom]]);
7872
+ return serializer.serialize([
7873
+ "Divide",
7874
+ "1",
7875
+ ["Root", base, operand(exp2, 2)]
7876
+ ]);
7873
7877
  }
7874
7878
  if (denom === 2) {
7875
7879
  return `${serializer.serialize(["Sqrt", base])}^{${serializer.serialize(
@@ -7883,13 +7887,20 @@ var ComputeEngine = (() => {
7883
7887
  }
7884
7888
  }
7885
7889
  }
7890
+ const wrapNegativeBase = (latex) => latex.startsWith("-") ? serializer.wrapString(latex, "normal") : latex;
7886
7891
  if (operator(base) === "Power") {
7887
7892
  const baseBody = operand(base, 1);
7888
7893
  const baseExponent = operand(base, 2);
7894
+ const baseBodyLatex = wrapNegativeBase(serializer.wrapShort(baseBody));
7895
+ const baseExponentLatex = serializer.wrapShort(baseExponent);
7889
7896
  return `
7890
- ${serializer.wrapShort(baseBody)}^{${supsub("^", serializer.wrapShort(baseExponent), serializer.serialize(exp2))}}`;
7897
+ ${baseBodyLatex}^{${supsub("^", baseExponentLatex, serializer.serialize(exp2))}}`;
7891
7898
  }
7892
- return supsub("^", serializer.wrapShort(base), serializer.serialize(exp2));
7899
+ return supsub(
7900
+ "^",
7901
+ wrapNegativeBase(serializer.wrapShort(base)),
7902
+ serializer.serialize(exp2)
7903
+ );
7893
7904
  }
7894
7905
  var DEFINITIONS_ARITHMETIC = [
7895
7906
  // Constants
@@ -8170,8 +8181,9 @@ var ComputeEngine = (() => {
8170
8181
  },
8171
8182
  {
8172
8183
  name: "GCD",
8173
- latexTrigger: ["\\gcd"]
8184
+ latexTrigger: ["\\gcd"],
8174
8185
  // command from amsmath package
8186
+ kind: "function"
8175
8187
  },
8176
8188
  {
8177
8189
  symbolTrigger: "gcd",
@@ -8229,9 +8241,14 @@ var ComputeEngine = (() => {
8229
8241
  },
8230
8242
  {
8231
8243
  name: "LCM",
8232
- symbolTrigger: "lcm",
8244
+ latexTrigger: ["\\lcm"],
8233
8245
  kind: "function"
8234
8246
  },
8247
+ {
8248
+ symbolTrigger: "lcm",
8249
+ kind: "function",
8250
+ parse: "LCM"
8251
+ },
8235
8252
  {
8236
8253
  symbolTrigger: "LCM",
8237
8254
  kind: "function",
@@ -8505,7 +8522,11 @@ var ComputeEngine = (() => {
8505
8522
  {
8506
8523
  name: "Square",
8507
8524
  precedence: 720,
8508
- serialize: (serializer, expr) => serializer.wrapShort(operand(expr, 1)) + "^2"
8525
+ serialize: (serializer, expr) => {
8526
+ const base = serializer.wrapShort(operand(expr, 1));
8527
+ const wrapped = base.startsWith("-") ? serializer.wrapString(base, "normal") : base;
8528
+ return wrapped + "^2";
8529
+ }
8509
8530
  },
8510
8531
  {
8511
8532
  latexTrigger: ["\\sum"],
@@ -10615,40 +10636,43 @@ var ComputeEngine = (() => {
10615
10636
  parse: "False"
10616
10637
  },
10617
10638
  // Operators
10639
+ // Logic operators have lower precedence than comparisons (245)
10640
+ // so that `x = 1 \lor x = 2` parses as `(x = 1) \lor (x = 2)`
10641
+ // See https://github.com/cortex-js/compute-engine/issues/243
10618
10642
  {
10619
10643
  name: "And",
10620
10644
  kind: "infix",
10621
10645
  latexTrigger: ["\\land"],
10622
- precedence: 317
10646
+ precedence: 235
10623
10647
  // serialize: '\\land',
10624
10648
  },
10625
- { kind: "infix", latexTrigger: ["\\wedge"], parse: "And", precedence: 317 },
10626
- { kind: "infix", latexTrigger: "\\&", parse: "And", precedence: 317 },
10649
+ { kind: "infix", latexTrigger: ["\\wedge"], parse: "And", precedence: 235 },
10650
+ { kind: "infix", latexTrigger: "\\&", parse: "And", precedence: 235 },
10627
10651
  {
10628
10652
  kind: "infix",
10629
10653
  latexTrigger: "\\operatorname{and}",
10630
10654
  parse: "And",
10631
- precedence: 317
10655
+ precedence: 235
10632
10656
  },
10633
10657
  {
10634
10658
  name: "Or",
10635
10659
  kind: "infix",
10636
10660
  latexTrigger: ["\\lor"],
10637
- precedence: 310
10661
+ precedence: 230
10638
10662
  },
10639
- { kind: "infix", latexTrigger: ["\\vee"], parse: "Or", precedence: 310 },
10640
- { kind: "infix", latexTrigger: "\\parallel", parse: "Or", precedence: 310 },
10663
+ { kind: "infix", latexTrigger: ["\\vee"], parse: "Or", precedence: 230 },
10664
+ { kind: "infix", latexTrigger: "\\parallel", parse: "Or", precedence: 230 },
10641
10665
  {
10642
10666
  kind: "infix",
10643
10667
  latexTrigger: "\\operatorname{or}",
10644
10668
  parse: "Or",
10645
- precedence: 310
10669
+ precedence: 230
10646
10670
  },
10647
10671
  {
10648
10672
  name: "Xor",
10649
10673
  kind: "infix",
10650
10674
  latexTrigger: ["\\veebar"],
10651
- precedence: 315
10675
+ precedence: 232
10652
10676
  },
10653
10677
  // Possible alt: \oplus ⊕ U+2295
10654
10678
  {
@@ -10667,7 +10691,7 @@ var ComputeEngine = (() => {
10667
10691
  name: "Nand",
10668
10692
  kind: "infix",
10669
10693
  latexTrigger: ["\\barwedge"],
10670
- precedence: 315
10694
+ precedence: 232
10671
10695
  // serialize: '\\mid',
10672
10696
  },
10673
10697
  {
@@ -10675,7 +10699,7 @@ var ComputeEngine = (() => {
10675
10699
  kind: "infix",
10676
10700
  latexTrigger: ["\u22BD"],
10677
10701
  // bar vee
10678
- precedence: 315
10702
+ precedence: 232
10679
10703
  // serialize: '\\downarrow',
10680
10704
  },
10681
10705
  // Functions
@@ -10771,7 +10795,7 @@ var ComputeEngine = (() => {
10771
10795
  latexTrigger: ["\\forall"],
10772
10796
  precedence: 200,
10773
10797
  // Has to be lower than COMPARISON_PRECEDENCE
10774
- serialize: "\\forall",
10798
+ serialize: serializeQuantifier("\\forall"),
10775
10799
  parse: parseQuantifier("ForAll")
10776
10800
  },
10777
10801
  {
@@ -10780,7 +10804,7 @@ var ComputeEngine = (() => {
10780
10804
  latexTrigger: ["\\exists"],
10781
10805
  precedence: 200,
10782
10806
  // Has to be lower than COMPARISON_PRECEDENCE,
10783
- serialize: "\\exists",
10807
+ serialize: serializeQuantifier("\\exists"),
10784
10808
  parse: parseQuantifier("Exists")
10785
10809
  },
10786
10810
  {
@@ -10789,7 +10813,7 @@ var ComputeEngine = (() => {
10789
10813
  latexTrigger: ["\\exists", "!"],
10790
10814
  precedence: 200,
10791
10815
  // Has to be lower than COMPARISON_PRECEDENCE,
10792
- serialize: "\\exists!",
10816
+ serialize: serializeQuantifier("\\exists!"),
10793
10817
  parse: parseQuantifier("ExistsUnique")
10794
10818
  },
10795
10819
  {
@@ -10798,7 +10822,7 @@ var ComputeEngine = (() => {
10798
10822
  latexTrigger: ["\\lnot", "\\forall"],
10799
10823
  precedence: 200,
10800
10824
  // Has to be lower than COMPARISON_PRECEDENCE
10801
- serialize: "\\lnot\\forall",
10825
+ serialize: serializeQuantifier("\\lnot\\forall"),
10802
10826
  parse: parseQuantifier("NotForAll")
10803
10827
  },
10804
10828
  {
@@ -10807,7 +10831,7 @@ var ComputeEngine = (() => {
10807
10831
  latexTrigger: ["\\lnot", "\\exists"],
10808
10832
  precedence: 200,
10809
10833
  // Has to be lower than COMPARISON_PRECEDENCE,
10810
- serialize: "\\lnot\\exists",
10834
+ serialize: serializeQuantifier("\\lnot\\exists"),
10811
10835
  parse: parseQuantifier("NotExists")
10812
10836
  },
10813
10837
  {
@@ -10868,19 +10892,53 @@ var ComputeEngine = (() => {
10868
10892
  if (!DEFINITIONS_INEQUALITIES.some((x) => x.name === h)) return null;
10869
10893
  return ["Boole", body];
10870
10894
  }
10895
+ },
10896
+ // Predicate application in First-Order Logic.
10897
+ // ["Predicate", "P", "x", "y"] serializes to "P(x, y)"
10898
+ {
10899
+ name: "Predicate",
10900
+ serialize: (serializer, expr) => {
10901
+ const args = operands(expr);
10902
+ if (args.length === 0) return "";
10903
+ const pred = args[0];
10904
+ const predStr = typeof pred === "string" ? pred : serializer.serialize(pred);
10905
+ if (args.length === 1) return predStr;
10906
+ const argStrs = args.slice(1).map((arg) => serializer.serialize(arg));
10907
+ return `${predStr}(${argStrs.join(", ")})`;
10908
+ }
10871
10909
  }
10872
10910
  ];
10911
+ function serializeQuantifier(quantifierSymbol) {
10912
+ return (serializer, expr) => {
10913
+ const args = operands(expr);
10914
+ if (args.length === 0) return quantifierSymbol;
10915
+ if (args.length === 1)
10916
+ return `${quantifierSymbol} ${serializer.serialize(args[0])}`;
10917
+ const boundVar = serializer.serialize(args[0]);
10918
+ const body = serializer.serialize(args[1]);
10919
+ return `${quantifierSymbol} ${boundVar}, ${body}`;
10920
+ };
10921
+ }
10922
+ function tightBindingCondition(p, terminator) {
10923
+ return p.peek === "\\to" || p.peek === "\\rightarrow" || p.peek === "\\implies" || p.peek === "\\Rightarrow" || p.peek === "\\iff" || p.peek === "\\Leftrightarrow" || p.peek === "\\land" || p.peek === "\\wedge" || p.peek === "\\lor" || p.peek === "\\vee" || (terminator.condition?.(p) ?? false);
10924
+ }
10873
10925
  function parseQuantifier(kind) {
10874
10926
  return (parser, terminator) => {
10875
10927
  const index = parser.index;
10928
+ const useTightBinding = parser.options.quantifierScope !== "loose";
10876
10929
  const symbol2 = parser.parseSymbol(terminator);
10877
10930
  if (symbol2) {
10878
10931
  parser.skipSpace();
10879
10932
  if (parser.match(",") || parser.match("\\mid") || parser.match(".") || parser.match(":") || parser.match("\\colon")) {
10880
- const body2 = parser.parseExpression(terminator);
10933
+ const bodyTerminator = useTightBinding ? { ...terminator, condition: (p) => tightBindingCondition(p, terminator) } : terminator;
10934
+ parser.enterQuantifierScope();
10935
+ const body2 = parser.parseExpression(bodyTerminator);
10936
+ parser.exitQuantifierScope();
10881
10937
  return [kind, symbol2, missingIfEmpty(body2)];
10882
10938
  }
10939
+ parser.enterQuantifierScope();
10883
10940
  const body = parser.parseEnclosure();
10941
+ parser.exitQuantifierScope();
10884
10942
  if (body) return [kind, symbol2, missingIfEmpty(body)];
10885
10943
  }
10886
10944
  parser.index = index;
@@ -10888,11 +10946,16 @@ var ComputeEngine = (() => {
10888
10946
  if (condition === null) return null;
10889
10947
  parser.skipSpace();
10890
10948
  if (parser.matchAny([",", "\\mid", ":", "\\colon"])) {
10891
- const body = parser.parseExpression(terminator);
10949
+ const bodyTerminator = useTightBinding ? { ...terminator, condition: (p) => tightBindingCondition(p, terminator) } : terminator;
10950
+ parser.enterQuantifierScope();
10951
+ const body = parser.parseExpression(bodyTerminator);
10952
+ parser.exitQuantifierScope();
10892
10953
  return [kind, condition, missingIfEmpty(body)];
10893
10954
  }
10894
10955
  if (parser.match("(")) {
10956
+ parser.enterQuantifierScope();
10895
10957
  const body = parser.parseExpression(terminator);
10958
+ parser.exitQuantifierScope();
10896
10959
  if (!parser.match(")")) return null;
10897
10960
  return [kind, condition, missingIfEmpty(body)];
10898
10961
  }
@@ -11381,7 +11444,7 @@ var ComputeEngine = (() => {
11381
11444
  "\\ch": "Cosh",
11382
11445
  // Non-standard
11383
11446
  "\\cos": "Cos",
11384
- "\\cosh": "Csch",
11447
+ "\\cosh": "Cosh",
11385
11448
  "\\cosec": "Csc",
11386
11449
  // Non-standard
11387
11450
  "\\cot": "Cot",
@@ -13293,8 +13356,41 @@ var ComputeEngine = (() => {
13293
13356
  return parser.error(["invalid-symbol", { str: validateSymbol(id) }], start);
13294
13357
  }
13295
13358
  function parseSymbol(parser) {
13296
- if (/^[a-zA-Z]$/.test(parser.peek) || /^\p{XIDS}$/u.test(parser.peek))
13297
- return parser.nextToken();
13359
+ if (/^[a-zA-Z]$/.test(parser.peek) || /^\p{XIDS}$/u.test(parser.peek)) {
13360
+ let id2 = parser.nextToken();
13361
+ while (!parser.atEnd) {
13362
+ const currentPeek = parser.peek;
13363
+ if (currentPeek !== "_") break;
13364
+ const underscoreIndex = parser.index;
13365
+ parser.nextToken();
13366
+ const hasBrace = parser.match("<{>");
13367
+ if (hasBrace) {
13368
+ const firstToken = parser.peek;
13369
+ if (firstToken === "(" || firstToken === "\\lparen" || firstToken === "\\left") {
13370
+ parser.index = underscoreIndex;
13371
+ break;
13372
+ }
13373
+ const sub2 = parseSymbolBody(parser);
13374
+ const hasOperators = sub2 !== null && /plus|minus|times|ast/.test(sub2);
13375
+ if (sub2 === null || sub2.includes(",") || hasOperators || parser.peek !== "<}>") {
13376
+ parser.index = underscoreIndex;
13377
+ break;
13378
+ }
13379
+ parser.match("<}>");
13380
+ id2 += "_" + sub2;
13381
+ } else {
13382
+ const subToken = parser.peek;
13383
+ if (/^[a-zA-Z0-9]$/.test(subToken) || /^\p{XIDS}$/u.test(subToken)) {
13384
+ parser.nextToken();
13385
+ id2 += "_" + subToken;
13386
+ } else {
13387
+ parser.index = underscoreIndex;
13388
+ break;
13389
+ }
13390
+ }
13391
+ }
13392
+ return id2;
13393
+ }
13298
13394
  let id = matchPrefixedSymbol(parser);
13299
13395
  if (!id) {
13300
13396
  id = "";
@@ -13394,6 +13490,18 @@ var ComputeEngine = (() => {
13394
13490
  throw new Error(`Symbol ${id} already declared as a different type`);
13395
13491
  this.symbolTable.ids[id] = type2;
13396
13492
  }
13493
+ // Track whether we're inside a quantifier body (ForAll, Exists, etc.)
13494
+ // When true, single uppercase letters followed by () are parsed as predicates
13495
+ _quantifierScopeDepth = 0;
13496
+ get inQuantifierScope() {
13497
+ return this._quantifierScopeDepth > 0;
13498
+ }
13499
+ enterQuantifierScope() {
13500
+ this._quantifierScopeDepth++;
13501
+ }
13502
+ exitQuantifierScope() {
13503
+ if (this._quantifierScopeDepth > 0) this._quantifierScopeDepth--;
13504
+ }
13397
13505
  get index() {
13398
13506
  return this._index;
13399
13507
  }
@@ -14279,9 +14387,10 @@ var ComputeEngine = (() => {
14279
14387
  this.skipSpace();
14280
14388
  let body = this.parseExpression();
14281
14389
  this.skipSpace();
14282
- if (!this.matchBoundary()) {
14283
- const boundary = this._boundaries[this._boundaries.length - 1].tokens;
14284
- this.removeBoundary();
14390
+ const boundary = this._boundaries[this._boundaries.length - 1]?.tokens;
14391
+ const matchedBoundary = this.matchBoundary();
14392
+ const sameTrigger = typeof def.openTrigger === "string" && typeof def.closeTrigger === "string" && def.openTrigger === def.closeTrigger || Array.isArray(def.openTrigger) && Array.isArray(def.closeTrigger) && def.openTrigger.length === def.closeTrigger.length && def.openTrigger.every((tok, i) => tok === def.closeTrigger[i]);
14393
+ if (matchedBoundary && isEmptySequence(body) && sameTrigger && boundary) {
14285
14394
  this.index = bodyStart;
14286
14395
  this.skipSpace();
14287
14396
  body = this.parseExpression();
@@ -14291,6 +14400,18 @@ var ComputeEngine = (() => {
14291
14400
  if (!this.atEnd) continue;
14292
14401
  return null;
14293
14402
  }
14403
+ } else if (!matchedBoundary) {
14404
+ const boundary2 = this._boundaries[this._boundaries.length - 1].tokens;
14405
+ this.removeBoundary();
14406
+ this.index = bodyStart;
14407
+ this.skipSpace();
14408
+ body = this.parseExpression();
14409
+ this.skipSpace();
14410
+ if (!this.matchAll(boundary2)) {
14411
+ this.index = start;
14412
+ if (!this.atEnd) continue;
14413
+ return null;
14414
+ }
14294
14415
  }
14295
14416
  const result = def.parse(this, body ?? "Nothing");
14296
14417
  if (result !== null) return result;
@@ -14339,12 +14460,16 @@ var ComputeEngine = (() => {
14339
14460
  break;
14340
14461
  }
14341
14462
  }
14463
+ let isPredicate = false;
14342
14464
  if (fn === null) {
14343
14465
  this.index = start;
14344
14466
  fn = parseSymbol(this);
14345
14467
  if (!this.isFunctionOperator(fn)) {
14346
- this.index = start;
14347
- return null;
14468
+ if (!this.looksLikePredicate(fn)) {
14469
+ this.index = start;
14470
+ return null;
14471
+ }
14472
+ isPredicate = true;
14348
14473
  }
14349
14474
  }
14350
14475
  do {
@@ -14354,6 +14479,10 @@ var ComputeEngine = (() => {
14354
14479
  } while (true);
14355
14480
  const args = this.parseArguments("enclosure", until);
14356
14481
  if (args === null) return fn;
14482
+ if (isPredicate && typeof fn === "string") {
14483
+ if (this.inQuantifierScope || fn === "D" || fn === "N")
14484
+ return ["Predicate", fn, ...args];
14485
+ }
14357
14486
  return typeof fn === "string" ? [fn, ...args] : ["Apply", fn, ...args];
14358
14487
  }
14359
14488
  parseSymbol(until) {
@@ -14742,9 +14871,24 @@ var ComputeEngine = (() => {
14742
14871
  }
14743
14872
  isFunctionOperator(id) {
14744
14873
  if (id === null) return false;
14874
+ if (id === "D" || id === "N") return false;
14745
14875
  if (this.getSymbolType(id).matches("function")) return true;
14746
14876
  return false;
14747
14877
  }
14878
+ /**
14879
+ * Check if a symbol looks like a predicate in First-Order Logic.
14880
+ * A predicate is typically a single uppercase letter (P, Q, R, etc.)
14881
+ * followed by parentheses containing arguments.
14882
+ *
14883
+ * This enables automatic inference of predicates without explicit declaration,
14884
+ * so `\forall x. P(x)` works without having to declare `P` as a function.
14885
+ */
14886
+ looksLikePredicate(id) {
14887
+ if (id === null || typeof id !== "string") return false;
14888
+ if (!/^[A-Z]$/.test(id)) return false;
14889
+ this.skipSpace();
14890
+ return this.peek === "(" || this.peek === "\\left";
14891
+ }
14748
14892
  /** Return all defs of the specified kind.
14749
14893
  * The defs at the end of the dictionary have priority, since they may
14750
14894
  * override previous definitions. (For example, there is a core definition
@@ -18627,7 +18771,10 @@ var ComputeEngine = (() => {
18627
18771
  if (op === "Power") {
18628
18772
  const baseDeg = polynomialDegree(expr.op1, variable);
18629
18773
  if (baseDeg < 0) return -1;
18630
- if (baseDeg === 0) return 0;
18774
+ if (baseDeg === 0) {
18775
+ if (expr.op2.has(variable)) return -1;
18776
+ return 0;
18777
+ }
18631
18778
  const exp2 = asSmallInteger(expr.op2);
18632
18779
  if (exp2 === null || exp2 < 0) return -1;
18633
18780
  return baseDeg * exp2;
@@ -21666,8 +21813,6 @@ var ComputeEngine = (() => {
21666
21813
  }
21667
21814
  if (index.operator === "Hold") index = index.op1;
21668
21815
  if (!index.symbol) index = ce.typeError("symbol", index.type, index);
21669
- if (lower.symbol !== "Nothing") lower = checkType(ce, lower, "number");
21670
- if (upper.symbol !== "Nothing") upper = checkType(ce, upper, "number");
21671
21816
  return ce._fn("Limits", [index, lower, upper]);
21672
21817
  }
21673
21818
  return null;
@@ -22546,10 +22691,6 @@ var ComputeEngine = (() => {
22546
22691
  fractionalPart = options.decimalSeparator + fractionalPart;
22547
22692
  wholePart = insertWholeGroupSeparator(wholePart, options);
22548
22693
  if (!expString) return wholePart + fractionalPart;
22549
- if (!fractionalPart) {
22550
- if (wholePart === "1") return expString;
22551
- if (wholePart === "-1") return "-" + expString;
22552
- }
22553
22694
  return wholePart + fractionalPart + options.exponentProduct + expString;
22554
22695
  }
22555
22696
  function serializeAutoNotationNumber(valString, options) {
@@ -23056,7 +23197,8 @@ var ComputeEngine = (() => {
23056
23197
  Negate: [
23057
23198
  (expr, serialize) => {
23058
23199
  const base = serialize(expr.op1, 14);
23059
- if (base === "Power") return `-(${base})`;
23200
+ const op = expr.op1?.operator;
23201
+ if (op === "Power" || op === "Square") return `-(${base})`;
23060
23202
  return `-${base}`;
23061
23203
  },
23062
23204
  14
@@ -23277,11 +23419,11 @@ var ComputeEngine = (() => {
23277
23419
  const b = fn.op1 ?? fn;
23278
23420
  if (b.operator === "Block") body = serialize(b.op1 ?? b);
23279
23421
  else body = serialize(b);
23280
- } else if (fn?.symbol) {
23422
+ } else if (fn) {
23281
23423
  args = [];
23282
23424
  body = serialize(fn);
23283
23425
  } else {
23284
- return "int()";
23426
+ return `${op}()`;
23285
23427
  }
23286
23428
  let result = op;
23287
23429
  for (const limit2 of limits) {
@@ -25397,7 +25539,7 @@ ${lines.join("\n")}`;
25397
25539
  description: "Rounds a number up to the next largest integer",
25398
25540
  complexity: 1250,
25399
25541
  broadcastable: true,
25400
- signature: "(real) -> integer",
25542
+ signature: "(number) -> integer",
25401
25543
  sgn: ([x]) => {
25402
25544
  if (x.isLessEqual(-1)) return "negative";
25403
25545
  if (x.isPositive) return "positive";
@@ -26439,38 +26581,29 @@ ${lines.join("\n")}`;
26439
26581
  signature: "((number+) -> number, (tuple<integer>|tuple<integer, integer>)+) -> number",
26440
26582
  canonical: ([body, ...bounds], { scope }) => canonicalBigop("Product", body, bounds, scope),
26441
26583
  evaluate: (ops, options) => {
26442
- const fn = (acc, x) => {
26443
- x = x.evaluate(options);
26444
- return x.isNumberLiteral ? acc.mul(x.numericValue) : null;
26445
- };
26446
26584
  const result = run(
26447
26585
  reduceBigOp(
26448
26586
  ops[0],
26449
26587
  ops.slice(1),
26450
- fn,
26451
- options.engine._numericValue(1)
26588
+ (acc, x) => acc.mul(x.evaluate(options)),
26589
+ options.engine.One
26452
26590
  ),
26453
26591
  options.engine._timeRemaining
26454
26592
  );
26455
- return options.engine.number(result ?? NaN);
26593
+ return result?.evaluate() ?? options.engine.NaN;
26456
26594
  },
26457
26595
  evaluateAsync: async (ops, options) => {
26458
- const fn = (acc, x) => {
26459
- x = x.evaluate(options);
26460
- if (!x.isNumberLiteral) return null;
26461
- return acc.mul(x.numericValue);
26462
- };
26463
26596
  const result = await runAsync(
26464
26597
  reduceBigOp(
26465
26598
  ops[0],
26466
26599
  ops.slice(1),
26467
- fn,
26468
- options.engine._numericValue(1)
26600
+ (acc, x) => acc.mul(x.evaluate(options)),
26601
+ options.engine.One
26469
26602
  ),
26470
26603
  options.engine._timeRemaining,
26471
26604
  options.signal
26472
26605
  );
26473
- return options.engine.number(result ?? NaN);
26606
+ return result?.evaluate() ?? options.engine.NaN;
26474
26607
  }
26475
26608
  },
26476
26609
  Sum: {
@@ -26482,36 +26615,31 @@ ${lines.join("\n")}`;
26482
26615
  lazy: true,
26483
26616
  signature: "((number) -> number, bounds:tuple+) -> number",
26484
26617
  canonical: ([body, ...bounds], { scope }) => canonicalBigop("Sum", body, bounds, scope),
26485
- evaluate: ([fn, ...indexes], { engine }) => engine.number(
26486
- run(
26618
+ evaluate: ([body, ...indexes], { engine }) => {
26619
+ const result = run(
26487
26620
  reduceBigOp(
26488
- fn,
26621
+ body,
26489
26622
  indexes,
26490
- (acc, x) => {
26491
- x = x.evaluate();
26492
- return x.isNumberLiteral ? acc.add(x.numericValue) : null;
26493
- },
26494
- engine._numericValue(0)
26623
+ (acc, x) => acc.add(x.evaluate()),
26624
+ engine.Zero
26495
26625
  ),
26496
26626
  engine._timeRemaining
26497
- )
26498
- ),
26499
- evaluateAsync: async (xs, { engine, signal }) => engine.number(
26500
- await runAsync(
26627
+ );
26628
+ return result?.evaluate() ?? engine.NaN;
26629
+ },
26630
+ evaluateAsync: async (xs, { engine, signal }) => {
26631
+ const result = await runAsync(
26501
26632
  reduceBigOp(
26502
26633
  xs[0],
26503
26634
  xs.slice(1),
26504
- (acc, x) => {
26505
- x = x.evaluate();
26506
- if (!x.isNumberLiteral) return null;
26507
- return acc.add(x.numericValue);
26508
- },
26509
- engine._numericValue(0)
26635
+ (acc, x) => acc.add(x.evaluate()),
26636
+ engine.Zero
26510
26637
  ),
26511
26638
  engine._timeRemaining,
26512
26639
  signal
26513
- )
26514
- )
26640
+ );
26641
+ return result?.evaluate() ?? engine.NaN;
26642
+ }
26515
26643
  }
26516
26644
  }
26517
26645
  ];
@@ -26701,17 +26829,12 @@ ${lines.join("\n")}`;
26701
26829
  Ln: ["Divide", 1, "_"],
26702
26830
  Log: ["Power", ["Multiply", "_", ["Ln", "10"]], -1],
26703
26831
  Sqrt: ["Multiply", ["Power", "_", ["Negate", "Half"]], "Half"],
26704
- Abs: [
26705
- "Which",
26706
- ["Equal", "_", 0],
26707
- NaN,
26708
- ["Less", "_", 0],
26709
- -1,
26710
- ["Greater", "_", 0],
26711
- 1,
26712
- "True",
26713
- ["D", ["Abs", "_"], "_"]
26714
- ],
26832
+ // d/dx |x| = x/|x| = sign(x) for x ≠ 0 (undefined at x = 0)
26833
+ Abs: ["Sign", "_"],
26834
+ // Step functions: derivative is 0 almost everywhere (undefined at discontinuities)
26835
+ Floor: 0,
26836
+ Ceil: 0,
26837
+ Round: 0,
26715
26838
  // https://proofwiki.org/wiki/Derivative_of_Error_Function
26716
26839
  Erf: [
26717
26840
  "Multiply",
@@ -26720,51 +26843,36 @@ ${lines.join("\n")}`;
26720
26843
  ],
26721
26844
  // https://proofwiki.org/wiki/Derivative_of_Gamma_Function
26722
26845
  // https://en.wikipedia.org/wiki/Gamma_function
26846
+ // d/dx Γ(x) = Γ(x)·ψ(x) where ψ is the digamma function
26723
26847
  Gamma: ["Multiply", ["Gamma", "_"], ["Digamma", "_"]],
26724
- Digamma: [
26725
- "Add",
26726
- ["Multiply", ["Digamma", "_"], ["Gamma", "_"]],
26727
- ["Multiply", ["Power", "_", -1], ["Gamma", "_"]]
26728
- ],
26729
- Zeta: ["Multiply", ["Multiply", -1, ["Zeta", "_"]], ["Digamma", "_"]],
26730
- PolyGamma: [
26731
- "Add",
26732
- ["Multiply", ["PolyGamma", "_"], ["Gamma", "_"]],
26733
- ["Multiply", ["Power", "_", -1], ["Gamma", "_"]]
26734
- ],
26735
- Beta: [
26736
- "Multiply",
26737
- [
26738
- "Add",
26739
- ["Multiply", ["Beta", "_"], ["Digamma", "_"]],
26740
- ["Multiply", ["Power", "_", -1], ["Beta", "_"]]
26741
- ],
26742
- ["Beta", "_"]
26743
- ],
26848
+ // d/dx erfc(x) = -d/dx erf(x) = -2/√π * e^(-x²)
26744
26849
  Erfc: [
26745
- "Multiply",
26746
- ["Negate", ["Erfc", "_"]],
26747
- ["Exp", ["Negate", ["Power", "_", 2]]],
26748
- ["Power", "_", -1]
26850
+ "Negate",
26851
+ ["Multiply", ["Divide", 2, ["Sqrt", "Pi"]], ["Exp", ["Negate", ["Square", "_"]]]]
26749
26852
  ],
26750
- LambertW: [
26853
+ // d/dx ln(Γ(x)) = ψ(x) (digamma function)
26854
+ LogGamma: ["Digamma", "_"],
26855
+ // Note: LambertW derivative d/dx W(x) = W(x)/(x·(1+W(x))) is mathematically correct
26856
+ // but omitted because LambertW lacks a type signature, causing type errors.
26857
+ //
26858
+ // d/dx S(x) = sin(πx²/2) where S is the Fresnel sine integral
26859
+ FresnelS: ["Sin", ["Multiply", ["Divide", "Pi", 2], ["Square", "_"]]],
26860
+ // d/dx C(x) = cos(πx²/2) where C is the Fresnel cosine integral
26861
+ FresnelC: ["Cos", ["Multiply", ["Divide", "Pi", 2], ["Square", "_"]]],
26862
+ // d/dx erfi(x) = (2/√π)·e^(x²) where erfi is the imaginary error function
26863
+ Erfi: [
26751
26864
  "Multiply",
26752
- ["Power", "_", -1],
26753
- [
26754
- "Multiply",
26755
- ["Add", "_", ["LambertW", "_"]],
26756
- ["Add", ["LambertW", "_"], 1]
26757
- ]
26758
- ],
26759
- AiryAi: ["Multiply", ["AiryAi", "_"], ["AiryBi", "_"]],
26760
- AiryBi: ["Multiply", ["AiryAi", "_"], ["AiryBi", "_"]],
26761
- BesselJ: ["Multiply", ["BesselJ", "_"], ["BesselY", "_"]],
26762
- BesselY: ["Multiply", ["BesselJ", "_"], ["BesselY", "_"]],
26763
- BesselI: ["Multiply", ["BesselI", "_"], ["BesselK", "_"]],
26764
- BesselK: ["Multiply", ["BesselI", "_"], ["BesselK", "_"]],
26765
- FresnelS: ["Multiply", ["FresnelS", "_"], ["FresnelC", "_"]],
26766
- FresnelC: ["Multiply", ["FresnelS", "_"], ["FresnelC", "_"]],
26767
- Erfi: ["Multiply", ["Erfi", "_"], ["Erf", "_"]]
26865
+ ["Divide", 2, ["Sqrt", "Pi"]],
26866
+ ["Exp", ["Square", "_"]]
26867
+ ]
26868
+ // Note: Bessel functions (BesselJ, BesselY, BesselI, BesselK) and Airy functions
26869
+ // (AiryAi, AiryBi) have been omitted because their derivatives involve functions
26870
+ // of different orders or related derivative functions that are not in the standard
26871
+ // function set. For example, d/dx J_n(x) = (J_{n-1}(x) - J_{n+1}(x))/2.
26872
+ //
26873
+ // Similarly, Zeta, Digamma, PolyGamma, and Beta derivatives are omitted because
26874
+ // they either don't have simple closed forms or involve additional functions not
26875
+ // in the standard set (trigamma function, etc.).
26768
26876
  };
26769
26877
  function derivative(fn, order2) {
26770
26878
  if (order2 === 0) return fn;
@@ -26817,6 +26925,15 @@ ${lines.join("\n")}`;
26817
26925
  if (terms.some((term) => term === void 0)) return void 0;
26818
26926
  return simplifyDerivative(add3(...terms));
26819
26927
  }
26928
+ if (expr.operator === "Root") {
26929
+ const [base, n] = expr.ops;
26930
+ if (!base.has(v)) return ce.Zero;
26931
+ const exponent = ce.One.div(n);
26932
+ const basePrime = differentiate(base, v) ?? ce._fn("D", [base, ce.symbol(v)]);
26933
+ const newExponent = exponent.sub(ce.One);
26934
+ const power = ce.function("Power", [base, newExponent], { structural: true });
26935
+ return simplifyDerivative(exponent.mul(power).mul(basePrime));
26936
+ }
26820
26937
  if (expr.operator === "Power") {
26821
26938
  const [base, exponent] = expr.ops;
26822
26939
  const baseHasV = base.has(v);
@@ -26853,7 +26970,7 @@ ${lines.join("\n")}`;
26853
26970
  );
26854
26971
  }
26855
26972
  const h = DERIVATIVES_TABLE[expr.operator];
26856
- if (!h) {
26973
+ if (h === void 0) {
26857
26974
  if (expr.nops > 1) return void 0;
26858
26975
  const fPrime = ce._fn("Derivative", [ce.symbol(expr.operator), ce.One]);
26859
26976
  if (!fPrime.isValid) return void 0;
@@ -27434,6 +27551,14 @@ ${e.message}`);
27434
27551
  // Handle ax = 0
27435
27552
  condition: filter
27436
27553
  },
27554
+ // -ax + b = 0 => x = b/a
27555
+ // This handles cases where the coefficient is negative and represented as Negate(Multiply(...))
27556
+ {
27557
+ match: ["Add", ["Negate", ["Multiply", "_x", "__a"]], "__b"],
27558
+ replace: ["Divide", "__b", "__a"],
27559
+ useVariations: true,
27560
+ condition: filter
27561
+ },
27437
27562
  // ax^n + b = 0
27438
27563
  {
27439
27564
  match: ["Add", ["Multiply", "_a", ["Power", "_x", "_n"]], "__b"],
@@ -27569,27 +27694,140 @@ ${e.message}`);
27569
27694
  replace: ["Divide", ["Negate", ["Add", "__b", "__c"], "__a"]],
27570
27695
  condition: filter
27571
27696
  },
27572
- // ax + c\sqrt{dx + f} + g = 0
27573
- // plus
27697
+ //
27698
+ // Square root equations: ax + b√x + c = 0
27699
+ // Using substitution u = √x, this becomes au² + bu + c = 0
27700
+ // Solving: u = (-b ± √(b² - 4ac)) / 2a
27701
+ // Then x = u² = ((-b ± √(b² - 4ac)) / 2a)²
27702
+ //
27703
+ // ax + b√x + c = 0 (plus root)
27704
+ {
27705
+ match: ["Add", ["Multiply", "_x", "__a"], ["Multiply", "__b", ["Sqrt", "_x"]], "___c"],
27706
+ replace: [
27707
+ "Power",
27708
+ [
27709
+ "Divide",
27710
+ ["Add", ["Negate", "__b"], ["Sqrt", ["Subtract", ["Square", "__b"], ["Multiply", 4, "__a", "___c"]]]],
27711
+ ["Multiply", 2, "__a"]
27712
+ ],
27713
+ 2
27714
+ ],
27715
+ useVariations: true,
27716
+ condition: filter
27717
+ },
27718
+ // ax + b√x + c = 0 (minus root)
27719
+ {
27720
+ match: ["Add", ["Multiply", "_x", "__a"], ["Multiply", "__b", ["Sqrt", "_x"]], "___c"],
27721
+ replace: [
27722
+ "Power",
27723
+ [
27724
+ "Divide",
27725
+ ["Subtract", ["Negate", "__b"], ["Sqrt", ["Subtract", ["Square", "__b"], ["Multiply", 4, "__a", "___c"]]]],
27726
+ ["Multiply", 2, "__a"]
27727
+ ],
27728
+ 2
27729
+ ],
27730
+ useVariations: true,
27731
+ condition: filter
27732
+ },
27733
+ // Handle negated coefficient: ax - b√x + c = 0
27734
+ // This handles the Negate(Multiply(...)) pattern
27735
+ {
27736
+ match: ["Add", ["Multiply", "_x", "__a"], ["Negate", ["Multiply", "__b", ["Sqrt", "_x"]]], "___c"],
27737
+ replace: [
27738
+ "Power",
27739
+ [
27740
+ "Divide",
27741
+ ["Add", "__b", ["Sqrt", ["Add", ["Square", "__b"], ["Multiply", 4, "__a", "___c"]]]],
27742
+ ["Multiply", 2, "__a"]
27743
+ ],
27744
+ 2
27745
+ ],
27746
+ useVariations: true,
27747
+ condition: filter
27748
+ },
27749
+ // ax - b√x + c = 0 (minus root)
27750
+ {
27751
+ match: ["Add", ["Multiply", "_x", "__a"], ["Negate", ["Multiply", "__b", ["Sqrt", "_x"]]], "___c"],
27752
+ replace: [
27753
+ "Power",
27754
+ [
27755
+ "Divide",
27756
+ ["Subtract", "__b", ["Sqrt", ["Add", ["Square", "__b"], ["Multiply", 4, "__a", "___c"]]]],
27757
+ ["Multiply", 2, "__a"]
27758
+ ],
27759
+ 2
27760
+ ],
27761
+ useVariations: true,
27762
+ condition: filter
27763
+ },
27764
+ //
27765
+ // Additional solve rules
27766
+ //
27767
+ // a√x + b = 0 => x = (b/a)² (only valid when -b/a ≥ 0)
27768
+ {
27769
+ match: ["Add", ["Multiply", "__a", ["Sqrt", "_x"]], "__b"],
27770
+ replace: ["Square", ["Divide", ["Negate", "__b"], "__a"]],
27771
+ useVariations: true,
27772
+ condition: (sub2) => {
27773
+ if (!filter(sub2)) return false;
27774
+ const a = sub2.__a;
27775
+ const b = sub2.__b;
27776
+ if (!a || !b) return false;
27777
+ const ratio = b.div(a);
27778
+ return ratio.isNonPositive ?? true;
27779
+ }
27780
+ },
27781
+ // a·ln(x) + b = 0 => x = e^(-b/a)
27574
27782
  {
27575
- match: "ax + \\mathrm{__b} \\sqrt{cx + \\mathrm{__d}} + \\mathrm{__g}",
27576
- replace: "\\frac{-(2 a g - \\mathrm{__b}^2 c) + \\sqrt{(2 a \\mathrm{__g} - \\mathrm{__b}^2 c)^2 - 4 a^2(g^2 - b^2 \\mathrm{__d})}}{2 a^2}",
27783
+ match: ["Add", ["Multiply", "__a", ["Ln", "_x"]], "__b"],
27784
+ replace: ["Exp", ["Divide", ["Negate", "__b"], "__a"]],
27577
27785
  useVariations: true,
27578
27786
  condition: filter
27579
27787
  },
27580
- // minus
27788
+ // ln(x) + b = 0 => x = e^(-b)
27581
27789
  {
27582
- match: "ax + \\mathrm{__b} \\sqrt{cx + \\mathrm{__d}} + \\mathrm{__g}",
27583
- replace: "\\frac{-(2 a g - \\mathrm{__b}^2 c) - \\sqrt{(2 a \\mathrm{__g} - \\mathrm{__b}^2 c)^2 - 4 a^2(g^2 - b^2 \\mathrm{__d})}}{2 a^2}",
27790
+ match: ["Add", ["Ln", "_x"], "__b"],
27791
+ replace: ["Exp", ["Negate", "__b"]],
27584
27792
  useVariations: true,
27585
27793
  condition: filter
27586
27794
  }
27587
27795
  ];
27796
+ function clearDenominators(expr, variable) {
27797
+ if (expr.operator !== "Add") return expr;
27798
+ const ops = expr.ops;
27799
+ if (!ops || ops.length === 0) return expr;
27800
+ const denominators = ops.map((op) => op.denominator).filter((d) => !d.is(1));
27801
+ if (denominators.length === 0) return expr;
27802
+ const lcmFactors = [];
27803
+ for (const denom of denominators) {
27804
+ let isDuplicate = false;
27805
+ for (const existing of lcmFactors) {
27806
+ if (denom.isSame(existing)) {
27807
+ isDuplicate = true;
27808
+ break;
27809
+ }
27810
+ if (denom.symbol && existing.symbol && denom.symbol === existing.symbol) {
27811
+ isDuplicate = true;
27812
+ break;
27813
+ }
27814
+ }
27815
+ if (!isDuplicate) {
27816
+ lcmFactors.push(denom);
27817
+ }
27818
+ }
27819
+ let lcm4 = lcmFactors[0];
27820
+ for (let i = 1; i < lcmFactors.length; i++) {
27821
+ lcm4 = lcm4.mul(lcmFactors[i]);
27822
+ }
27823
+ return expr.mul(lcm4).simplify();
27824
+ }
27588
27825
  function findUnivariateRoots(expr, x) {
27589
27826
  const ce = expr.engine;
27590
27827
  if (expr.operator === "Equal")
27591
27828
  expr = expr.op1.expand().sub(expr.op2.expand()).simplify();
27592
27829
  else expr = expr.expand().simplify();
27830
+ expr = clearDenominators(expr);
27593
27831
  const rules = ce.getRuleSet("solve-univariate");
27594
27832
  let exprs = [expr.subs({ [x]: "_x" }, { canonical: false })];
27595
27833
  ce.pushScope();
@@ -28451,40 +28689,46 @@ ${e.message}`);
28451
28689
  ],
28452
28690
  condition: filter2
28453
28691
  },
28454
- // \arctan(ax + b) -> \frac{1}{a} \ln(\sec(ax + b) + \tan(ax + b))
28692
+ // \arctan(ax + b) -> (1/a) * [(ax+b)*arctan(ax+b) - (1/2)*ln(1+(ax+b)^2)]
28455
28693
  {
28456
28694
  match: ["Arctan", ["Add", ["Multiply", "_a", "_x"], "__b"]],
28457
28695
  replace: [
28458
28696
  "Divide",
28459
28697
  [
28460
- "Ln",
28698
+ "Subtract",
28461
28699
  [
28462
- "Add",
28463
- ["Sec", ["Add", ["Multiply", "_a", "_x"], "__b"]],
28464
- ["Tan", ["Add", ["Multiply", "_a", "_x"], "__b"]]
28700
+ "Multiply",
28701
+ ["Add", ["Multiply", "_a", "_x"], "__b"],
28702
+ ["Arctan", ["Add", ["Multiply", "_a", "_x"], "__b"]]
28703
+ ],
28704
+ [
28705
+ "Multiply",
28706
+ ["Rational", 1, 2],
28707
+ ["Ln", ["Add", 1, ["Power", ["Add", ["Multiply", "_a", "_x"], "__b"], 2]]]
28465
28708
  ]
28466
28709
  ],
28467
28710
  "_a"
28468
28711
  ],
28469
28712
  condition: filter2
28470
28713
  },
28471
- // \arccos(ax + b) -> \frac{1}{a} \ln(ax + b + \sqrt{(ax + b)^2 - 1})
28714
+ // \arccos(ax + b) -> (1/a) * [(ax+b)*arccos(ax+b) - sqrt(1-(ax+b)^2)]
28472
28715
  {
28473
28716
  match: ["Arccos", ["Add", ["Multiply", "_a", "_x"], "__b"]],
28474
28717
  replace: [
28475
28718
  "Divide",
28476
28719
  [
28477
- "Ln",
28720
+ "Subtract",
28478
28721
  [
28479
- "Add",
28722
+ "Multiply",
28480
28723
  ["Add", ["Multiply", "_a", "_x"], "__b"],
28724
+ ["Arccos", ["Add", ["Multiply", "_a", "_x"], "__b"]]
28725
+ ],
28726
+ [
28727
+ "Sqrt",
28481
28728
  [
28482
- "Sqrt",
28483
- [
28484
- "Subtract",
28485
- ["Power", ["Add", ["Multiply", "_a", "_x"], "__b"], 2],
28486
- 1
28487
- ]
28729
+ "Subtract",
28730
+ 1,
28731
+ ["Power", ["Add", ["Multiply", "_a", "_x"], "__b"], 2]
28488
28732
  ]
28489
28733
  ]
28490
28734
  ],
@@ -28492,23 +28736,24 @@ ${e.message}`);
28492
28736
  ],
28493
28737
  condition: filter2
28494
28738
  },
28495
- // \arcsin(ax + b) -> \frac{1}{a} \ln(ax + b + \sqrt{1 - (ax + b)^2})
28739
+ // \arcsin(ax + b) -> (1/a) * [(ax+b)*arcsin(ax+b) + sqrt(1-(ax+b)^2)]
28496
28740
  {
28497
28741
  match: ["Arcsin", ["Add", ["Multiply", "_a", "_x"], "__b"]],
28498
28742
  replace: [
28499
28743
  "Divide",
28500
28744
  [
28501
- "Ln",
28745
+ "Add",
28502
28746
  [
28503
- "Add",
28747
+ "Multiply",
28504
28748
  ["Add", ["Multiply", "_a", "_x"], "__b"],
28749
+ ["Arcsin", ["Add", ["Multiply", "_a", "_x"], "__b"]]
28750
+ ],
28751
+ [
28752
+ "Sqrt",
28505
28753
  [
28506
- "Sqrt",
28507
- [
28508
- "Subtract",
28509
- 1,
28510
- ["Power", ["Add", ["Multiply", "_a", "_x"], "__b"], 2]
28511
- ]
28754
+ "Subtract",
28755
+ 1,
28756
+ ["Power", ["Add", ["Multiply", "_a", "_x"], "__b"], 2]
28512
28757
  ]
28513
28758
  ]
28514
28759
  ],
@@ -29322,6 +29567,8 @@ ${e.message}`);
29322
29567
  }
29323
29568
  f = f?.canonical;
29324
29569
  if (f?.operator === "D") return f;
29570
+ if (f?.operator === "Apply" && f.op1?.operator === "Derivative")
29571
+ return f;
29325
29572
  if (f && hasSymbolicTranscendental(f)) return f;
29326
29573
  return f?.evaluate();
29327
29574
  }
@@ -29448,7 +29695,7 @@ ${e.message}`);
29448
29695
  complexity: 5e3,
29449
29696
  broadcastable: false,
29450
29697
  lazy: true,
29451
- signature: "(index:symbol, lower:number, upper:number) -> tuple",
29698
+ signature: "(index:symbol, lower:value, upper:value) -> tuple",
29452
29699
  canonical: (ops, { engine }) => canonicalLimits(ops, { engine }) ?? null
29453
29700
  }
29454
29701
  },
@@ -30961,12 +31208,22 @@ ${e.message}`);
30961
31208
  }
30962
31209
  if (op1.isIndexedCollection) return ce._fn("At", [op1, op2.canonical]);
30963
31210
  if (op1.symbol) {
30964
- const sub2 = op2.string ?? op2.symbol ?? asSmallInteger(op2)?.toString();
30965
- if (sub2) return ce.symbol(op1.symbol + "_" + sub2);
31211
+ const sub3 = op2.string ?? op2.symbol ?? asSmallInteger(op2)?.toString();
31212
+ if (sub3) return ce.symbol(op1.symbol + "_" + sub3);
31213
+ if (op2.operator === "InvisibleOperator" && op2.ops) {
31214
+ const parts = op2.ops.map(
31215
+ (x) => x.symbol ?? asSmallInteger(x)?.toString()
31216
+ );
31217
+ if (parts.every((p) => p !== void 0 && p !== null)) {
31218
+ return ce.symbol(op1.symbol + "_" + parts.join(""));
31219
+ }
31220
+ }
30966
31221
  }
30967
31222
  if (op2.operator === "Sequence")
30968
31223
  ce._fn("Subscript", [op1, ce._fn("List", op2.ops)]);
30969
- return ce._fn("Subscript", [op1, op2]);
31224
+ let sub2 = op2;
31225
+ if (op2.operator === "Delimiter" && op2.op1) sub2 = op2.op1.canonical;
31226
+ return ce._fn("Subscript", [op1, sub2]);
30970
31227
  }
30971
31228
  },
30972
31229
  Symbol: {
@@ -32696,6 +32953,539 @@ ${e.message}`);
32696
32953
  return ce._fn(operator2, [body]);
32697
32954
  }
32698
32955
 
32956
+ // src/compute-engine/library/logic-utils.ts
32957
+ function evaluateAnd(args, { engine: ce }) {
32958
+ if (args.length === 0) return ce.True;
32959
+ const ops = [];
32960
+ for (const arg of args) {
32961
+ if (arg.symbol === "False") return ce.False;
32962
+ if (arg.symbol !== "True") {
32963
+ let duplicate = false;
32964
+ for (const x of ops) {
32965
+ if (x.isSame(arg)) {
32966
+ duplicate = true;
32967
+ } else if (arg.operator === "Not" && arg.op1.isSame(x) || x.operator === "Not" && x.op1.isSame(arg)) {
32968
+ return ce.False;
32969
+ }
32970
+ }
32971
+ if (!duplicate) ops.push(arg);
32972
+ }
32973
+ }
32974
+ if (ops.length === 0) return ce.True;
32975
+ if (ops.length === 1) return ops[0];
32976
+ return ce._fn("And", ops);
32977
+ }
32978
+ function evaluateOr(args, { engine: ce }) {
32979
+ if (args.length === 0) return ce.True;
32980
+ const ops = [];
32981
+ for (const arg of args) {
32982
+ if (arg.symbol === "True") return ce.True;
32983
+ if (arg.symbol !== "False") {
32984
+ let duplicate = false;
32985
+ for (const x of ops) {
32986
+ if (x.isSame(arg)) {
32987
+ duplicate = true;
32988
+ } else if (arg.operator === "Not" && arg.op1.isSame(x) || x.operator === "Not" && x.op1.isSame(arg)) {
32989
+ return ce.True;
32990
+ }
32991
+ }
32992
+ if (!duplicate) ops.push(arg);
32993
+ }
32994
+ }
32995
+ if (ops.length === 0) return ce.False;
32996
+ if (ops.length === 1) return ops[0];
32997
+ return ce._fn("Or", ops);
32998
+ }
32999
+ function evaluateNot(args, { engine: ce }) {
33000
+ const op1 = args[0]?.symbol;
33001
+ if (op1 === "True") return ce.False;
33002
+ if (op1 === "False") return ce.True;
33003
+ return void 0;
33004
+ }
33005
+ function evaluateEquivalent(args, { engine: ce }) {
33006
+ const lhs = args[0].symbol;
33007
+ const rhs = args[1].symbol;
33008
+ if (lhs === "True" && rhs === "True" || lhs === "False" && rhs === "False")
33009
+ return ce.True;
33010
+ if (lhs === "True" && rhs === "False" || lhs === "False" && rhs === "True")
33011
+ return ce.False;
33012
+ return void 0;
33013
+ }
33014
+ function evaluateImplies(args, { engine: ce }) {
33015
+ const lhs = args[0].symbol;
33016
+ const rhs = args[1].symbol;
33017
+ if (lhs === "True" && rhs === "True" || lhs === "False" && rhs === "False" || lhs === "False" && rhs === "True")
33018
+ return ce.True;
33019
+ if (lhs === "True" && rhs === "False") return ce.False;
33020
+ return void 0;
33021
+ }
33022
+ function evaluateXor(args, { engine: ce }) {
33023
+ if (args.length === 0) return ce.False;
33024
+ let trueCount = 0;
33025
+ const unknowns = [];
33026
+ for (const arg of args) {
33027
+ if (arg.symbol === "True") {
33028
+ trueCount++;
33029
+ } else if (arg.symbol === "False") {
33030
+ } else {
33031
+ unknowns.push(arg);
33032
+ }
33033
+ }
33034
+ if (unknowns.length === 0) {
33035
+ return trueCount % 2 === 1 ? ce.True : ce.False;
33036
+ }
33037
+ if (unknowns.length === 1 && trueCount % 2 === 1) {
33038
+ return ce._fn("Not", [unknowns[0]]);
33039
+ }
33040
+ if (unknowns.length === 1 && trueCount % 2 === 0) {
33041
+ return unknowns[0];
33042
+ }
33043
+ return void 0;
33044
+ }
33045
+ function evaluateNand(args, { engine: ce }) {
33046
+ if (args.length === 0) return ce.False;
33047
+ for (const arg of args) {
33048
+ if (arg.symbol === "False") return ce.True;
33049
+ }
33050
+ let allTrue = true;
33051
+ for (const arg of args) {
33052
+ if (arg.symbol !== "True") {
33053
+ allTrue = false;
33054
+ break;
33055
+ }
33056
+ }
33057
+ if (allTrue) return ce.False;
33058
+ return void 0;
33059
+ }
33060
+ function evaluateNor(args, { engine: ce }) {
33061
+ if (args.length === 0) return ce.True;
33062
+ for (const arg of args) {
33063
+ if (arg.symbol === "True") return ce.False;
33064
+ }
33065
+ let allFalse = true;
33066
+ for (const arg of args) {
33067
+ if (arg.symbol !== "False") {
33068
+ allFalse = false;
33069
+ break;
33070
+ }
33071
+ }
33072
+ if (allFalse) return ce.True;
33073
+ return void 0;
33074
+ }
33075
+ function toNNF(expr, ce) {
33076
+ const op = expr.operator;
33077
+ if (!op) return expr;
33078
+ if (expr.symbol === "True" || expr.symbol === "False") return expr;
33079
+ if (op === "Not") {
33080
+ const inner = expr.op1;
33081
+ if (!inner) return expr;
33082
+ const innerOp = inner.operator;
33083
+ if (innerOp === "Not") {
33084
+ return toNNF(inner.op1, ce);
33085
+ }
33086
+ if (innerOp === "And") {
33087
+ const negatedOps = inner.ops.map((x) => toNNF(ce._fn("Not", [x]), ce));
33088
+ return ce._fn("Or", negatedOps);
33089
+ }
33090
+ if (innerOp === "Or") {
33091
+ const negatedOps = inner.ops.map((x) => toNNF(ce._fn("Not", [x]), ce));
33092
+ return ce._fn("And", negatedOps);
33093
+ }
33094
+ if (inner.symbol === "True") return ce.False;
33095
+ if (inner.symbol === "False") return ce.True;
33096
+ if (innerOp === "Implies") {
33097
+ const a = inner.op1;
33098
+ const b = inner.op2;
33099
+ return toNNF(ce._fn("And", [a, ce._fn("Not", [b])]), ce);
33100
+ }
33101
+ if (innerOp === "Equivalent") {
33102
+ const a = inner.op1;
33103
+ const b = inner.op2;
33104
+ return toNNF(
33105
+ ce._fn("Or", [
33106
+ ce._fn("And", [a, ce._fn("Not", [b])]),
33107
+ ce._fn("And", [ce._fn("Not", [a]), b])
33108
+ ]),
33109
+ ce
33110
+ );
33111
+ }
33112
+ if (innerOp === "Xor") {
33113
+ const ops = inner.ops;
33114
+ if (ops.length === 2) {
33115
+ const a = ops[0];
33116
+ const b = ops[1];
33117
+ return toNNF(
33118
+ ce._fn("Or", [
33119
+ ce._fn("And", [a, b]),
33120
+ ce._fn("And", [ce._fn("Not", [a]), ce._fn("Not", [b])])
33121
+ ]),
33122
+ ce
33123
+ );
33124
+ }
33125
+ return toNNF(ce._fn("Not", [toNNF(inner, ce)]), ce);
33126
+ }
33127
+ if (innerOp === "Nand") {
33128
+ return toNNF(ce._fn("And", inner.ops), ce);
33129
+ }
33130
+ if (innerOp === "Nor") {
33131
+ return toNNF(ce._fn("Or", inner.ops), ce);
33132
+ }
33133
+ return expr;
33134
+ }
33135
+ if (op === "Implies") {
33136
+ const a = expr.op1;
33137
+ const b = expr.op2;
33138
+ return toNNF(ce._fn("Or", [ce._fn("Not", [a]), b]), ce);
33139
+ }
33140
+ if (op === "Equivalent") {
33141
+ const a = expr.op1;
33142
+ const b = expr.op2;
33143
+ return toNNF(
33144
+ ce._fn("And", [
33145
+ ce._fn("Or", [ce._fn("Not", [a]), b]),
33146
+ ce._fn("Or", [ce._fn("Not", [b]), a])
33147
+ ]),
33148
+ ce
33149
+ );
33150
+ }
33151
+ if (op === "Xor") {
33152
+ const ops = expr.ops;
33153
+ if (ops.length === 2) {
33154
+ const a = ops[0];
33155
+ const b = ops[1];
33156
+ return toNNF(
33157
+ ce._fn("And", [
33158
+ ce._fn("Or", [a, b]),
33159
+ ce._fn("Or", [ce._fn("Not", [a]), ce._fn("Not", [b])])
33160
+ ]),
33161
+ ce
33162
+ );
33163
+ }
33164
+ if (ops.length > 2) {
33165
+ const first = ce._fn("Xor", [ops[0], ops[1]]);
33166
+ const rest = ops.slice(2);
33167
+ return toNNF(ce._fn("Xor", [first, ...rest]), ce);
33168
+ }
33169
+ if (ops.length === 1) return toNNF(ops[0], ce);
33170
+ return ce.False;
33171
+ }
33172
+ if (op === "Nand") {
33173
+ const ops = expr.ops;
33174
+ return toNNF(ce._fn("Not", [ce._fn("And", ops)]), ce);
33175
+ }
33176
+ if (op === "Nor") {
33177
+ const ops = expr.ops;
33178
+ return toNNF(ce._fn("Not", [ce._fn("Or", ops)]), ce);
33179
+ }
33180
+ if (op === "And" || op === "Or") {
33181
+ const nnfOps = expr.ops.map((x) => toNNF(x, ce));
33182
+ return ce._fn(op, nnfOps);
33183
+ }
33184
+ return expr;
33185
+ }
33186
+ function distributeOrOverAnd(expr, ce) {
33187
+ const op = expr.operator;
33188
+ if (op !== "Or") {
33189
+ if (op === "And") {
33190
+ return ce._fn(
33191
+ "And",
33192
+ expr.ops.map((x) => distributeOrOverAnd(x, ce))
33193
+ );
33194
+ }
33195
+ return expr;
33196
+ }
33197
+ const orOperands = [];
33198
+ for (const operand2 of expr.ops) {
33199
+ if (operand2.operator === "Or") {
33200
+ orOperands.push(...operand2.ops);
33201
+ } else {
33202
+ orOperands.push(operand2);
33203
+ }
33204
+ }
33205
+ const andIndex = orOperands.findIndex((x) => x.operator === "And");
33206
+ if (andIndex === -1) {
33207
+ return expr;
33208
+ }
33209
+ const andExpr = orOperands[andIndex];
33210
+ const otherOperands = [
33211
+ ...orOperands.slice(0, andIndex),
33212
+ ...orOperands.slice(andIndex + 1)
33213
+ ];
33214
+ const otherOr = otherOperands.length === 1 ? otherOperands[0] : ce._fn("Or", otherOperands);
33215
+ const distributed = ce._fn(
33216
+ "And",
33217
+ andExpr.ops.map((x) => ce._fn("Or", [x, otherOr]))
33218
+ );
33219
+ return distributeOrOverAnd(distributed, ce);
33220
+ }
33221
+ function toCNF(expr, ce) {
33222
+ const nnf = toNNF(expr, ce);
33223
+ const cnf = distributeOrOverAnd(nnf, ce);
33224
+ return cnf.simplify();
33225
+ }
33226
+ function distributeAndOverOr(expr, ce) {
33227
+ const op = expr.operator;
33228
+ if (op !== "And") {
33229
+ if (op === "Or") {
33230
+ return ce._fn(
33231
+ "Or",
33232
+ expr.ops.map((x) => distributeAndOverOr(x, ce))
33233
+ );
33234
+ }
33235
+ return expr;
33236
+ }
33237
+ const andOperands = [];
33238
+ for (const operand2 of expr.ops) {
33239
+ if (operand2.operator === "And") {
33240
+ andOperands.push(...operand2.ops);
33241
+ } else {
33242
+ andOperands.push(operand2);
33243
+ }
33244
+ }
33245
+ const orIndex = andOperands.findIndex((x) => x.operator === "Or");
33246
+ if (orIndex === -1) {
33247
+ return expr;
33248
+ }
33249
+ const orExpr = andOperands[orIndex];
33250
+ const otherOperands = [
33251
+ ...andOperands.slice(0, orIndex),
33252
+ ...andOperands.slice(orIndex + 1)
33253
+ ];
33254
+ const otherAnd = otherOperands.length === 1 ? otherOperands[0] : ce._fn("And", otherOperands);
33255
+ const distributed = ce._fn(
33256
+ "Or",
33257
+ orExpr.ops.map((x) => ce._fn("And", [x, otherAnd]))
33258
+ );
33259
+ return distributeAndOverOr(distributed, ce);
33260
+ }
33261
+ function toDNF(expr, ce) {
33262
+ const nnf = toNNF(expr, ce);
33263
+ const dnf = distributeAndOverOr(nnf, ce);
33264
+ return dnf.simplify();
33265
+ }
33266
+ function extractVariables(expr) {
33267
+ const variables = /* @__PURE__ */ new Set();
33268
+ function visit(e) {
33269
+ if (e.symbol === "True" || e.symbol === "False") return;
33270
+ if (e.symbol && e.operator === "Symbol") {
33271
+ variables.add(e.symbol);
33272
+ return;
33273
+ }
33274
+ if (e.ops) {
33275
+ for (const op of e.ops) {
33276
+ visit(op);
33277
+ }
33278
+ }
33279
+ }
33280
+ visit(expr);
33281
+ return Array.from(variables).sort();
33282
+ }
33283
+ function evaluateWithAssignment(expr, assignment, ce) {
33284
+ const subs = {};
33285
+ for (const [variable, value] of Object.entries(assignment)) {
33286
+ subs[variable] = value ? ce.True : ce.False;
33287
+ }
33288
+ const substituted = expr.subs(subs).canonical;
33289
+ return substituted.evaluate();
33290
+ }
33291
+ function* generateAssignments(variables) {
33292
+ const n = variables.length;
33293
+ const total = 1 << n;
33294
+ for (let i = 0; i < total; i++) {
33295
+ const assignment = {};
33296
+ for (let j = 0; j < n; j++) {
33297
+ assignment[variables[j]] = (i >> n - 1 - j & 1) === 1;
33298
+ }
33299
+ yield assignment;
33300
+ }
33301
+ }
33302
+
33303
+ // src/compute-engine/library/logic-analysis.ts
33304
+ function extractFiniteDomain(condition, ce) {
33305
+ if (condition.operator !== "Element") return null;
33306
+ const variable = condition.op1?.symbol;
33307
+ if (!variable) return null;
33308
+ const domain = condition.op2;
33309
+ if (!domain) return null;
33310
+ if (domain.operator === "Set" || domain.operator === "List") {
33311
+ const values = domain.ops;
33312
+ if (values && values.length <= 1e3) {
33313
+ return { variable, values: [...values] };
33314
+ }
33315
+ return null;
33316
+ }
33317
+ if (domain.operator === "Range") {
33318
+ const start = asSmallInteger(domain.op1);
33319
+ const end = asSmallInteger(domain.op2);
33320
+ const step = domain.ops && domain.ops.length >= 3 ? asSmallInteger(domain.op3) : 1;
33321
+ if (start !== null && end !== null && step !== null && step !== 0) {
33322
+ const count = Math.floor((end - start) / step) + 1;
33323
+ if (count > 0 && count <= 1e3) {
33324
+ const values = [];
33325
+ for (let i = start; step > 0 ? i <= end : i >= end; i += step) {
33326
+ values.push(ce.number(i));
33327
+ }
33328
+ return { variable, values };
33329
+ }
33330
+ }
33331
+ return null;
33332
+ }
33333
+ if (domain.operator === "Interval") {
33334
+ const start = asSmallInteger(domain.op1);
33335
+ const end = asSmallInteger(domain.op2);
33336
+ if (start !== null && end !== null) {
33337
+ const count = end - start + 1;
33338
+ if (count > 0 && count <= 1e3) {
33339
+ const values = [];
33340
+ for (let i = start; i <= end; i++) {
33341
+ values.push(ce.number(i));
33342
+ }
33343
+ return { variable, values };
33344
+ }
33345
+ }
33346
+ return null;
33347
+ }
33348
+ return null;
33349
+ }
33350
+ function bodyContainsVariable(expr, variable) {
33351
+ if (expr.symbol === variable) return true;
33352
+ if (expr.ops) {
33353
+ for (const op of expr.ops) {
33354
+ if (bodyContainsVariable(op, variable)) return true;
33355
+ }
33356
+ }
33357
+ return false;
33358
+ }
33359
+ function collectNestedDomains(body, ce) {
33360
+ const canonicalBody = body.canonical;
33361
+ const op = canonicalBody.operator;
33362
+ if (op !== "ForAll" && op !== "Exists") return [];
33363
+ const condition = canonicalBody.op1;
33364
+ const innerBody = canonicalBody.op2;
33365
+ if (!condition || !innerBody) return [];
33366
+ const domain = extractFiniteDomain(condition, ce);
33367
+ if (!domain) return [];
33368
+ const innerDomains = collectNestedDomains(innerBody, ce);
33369
+ return [{ variable: domain.variable, values: domain.values }, ...innerDomains];
33370
+ }
33371
+ function getInnermostBody(body) {
33372
+ const canonicalBody = body.canonical;
33373
+ const op = canonicalBody.operator;
33374
+ if (op === "ForAll" || op === "Exists") {
33375
+ const innerBody = canonicalBody.op2;
33376
+ if (innerBody) return getInnermostBody(innerBody);
33377
+ }
33378
+ return canonicalBody;
33379
+ }
33380
+ function evaluateForAllCartesian(domains, body, ce) {
33381
+ const indices = domains.map(() => 0);
33382
+ const lengths = domains.map((d) => d.values.length);
33383
+ if (lengths.some((l) => l === 0)) return ce.True;
33384
+ while (true) {
33385
+ const subs = {};
33386
+ for (let i = 0; i < domains.length; i++) {
33387
+ subs[domains[i].variable] = domains[i].values[indices[i]];
33388
+ }
33389
+ const substituted = body.subs(subs).canonical;
33390
+ const result = substituted.evaluate();
33391
+ if (result.symbol === "False") {
33392
+ return ce.False;
33393
+ }
33394
+ if (result.symbol !== "True") {
33395
+ return void 0;
33396
+ }
33397
+ let dim = domains.length - 1;
33398
+ while (dim >= 0) {
33399
+ indices[dim]++;
33400
+ if (indices[dim] < lengths[dim]) break;
33401
+ indices[dim] = 0;
33402
+ dim--;
33403
+ }
33404
+ if (dim < 0) break;
33405
+ }
33406
+ return ce.True;
33407
+ }
33408
+ function evaluateExistsCartesian(domains, body, ce) {
33409
+ const indices = domains.map(() => 0);
33410
+ const lengths = domains.map((d) => d.values.length);
33411
+ if (lengths.some((l) => l === 0)) return ce.False;
33412
+ while (true) {
33413
+ const subs = {};
33414
+ for (let i = 0; i < domains.length; i++) {
33415
+ subs[domains[i].variable] = domains[i].values[indices[i]];
33416
+ }
33417
+ const substituted = body.subs(subs).canonical;
33418
+ const result = substituted.evaluate();
33419
+ if (result.symbol === "True") {
33420
+ return ce.True;
33421
+ }
33422
+ let dim = domains.length - 1;
33423
+ while (dim >= 0) {
33424
+ indices[dim]++;
33425
+ if (indices[dim] < lengths[dim]) break;
33426
+ indices[dim] = 0;
33427
+ dim--;
33428
+ }
33429
+ if (dim < 0) break;
33430
+ }
33431
+ return ce.False;
33432
+ }
33433
+ function isSatisfiable(expr, ce) {
33434
+ const variables = extractVariables(expr);
33435
+ if (variables.length === 0) {
33436
+ const result = expr.evaluate();
33437
+ return result.symbol === "True" ? ce.True : ce.False;
33438
+ }
33439
+ if (variables.length > 20) {
33440
+ return ce._fn("IsSatisfiable", [expr]);
33441
+ }
33442
+ for (const assignment of generateAssignments(variables)) {
33443
+ const result = evaluateWithAssignment(expr, assignment, ce);
33444
+ if (result.symbol === "True") {
33445
+ return ce.True;
33446
+ }
33447
+ }
33448
+ return ce.False;
33449
+ }
33450
+ function isTautology(expr, ce) {
33451
+ const variables = extractVariables(expr);
33452
+ if (variables.length === 0) {
33453
+ const result = expr.evaluate();
33454
+ return result.symbol === "True" ? ce.True : ce.False;
33455
+ }
33456
+ if (variables.length > 20) {
33457
+ return ce._fn("IsTautology", [expr]);
33458
+ }
33459
+ for (const assignment of generateAssignments(variables)) {
33460
+ const result = evaluateWithAssignment(expr, assignment, ce);
33461
+ if (result.symbol !== "True") {
33462
+ return ce.False;
33463
+ }
33464
+ }
33465
+ return ce.True;
33466
+ }
33467
+ function generateTruthTable(expr, ce) {
33468
+ const variables = extractVariables(expr);
33469
+ if (variables.length > 10) {
33470
+ return ce._fn("TruthTable", [expr]);
33471
+ }
33472
+ const rows = [];
33473
+ const header = ce._fn("List", [
33474
+ ...variables.map((v) => ce.string(v)),
33475
+ ce.string("Result")
33476
+ ]);
33477
+ rows.push(header);
33478
+ for (const assignment of generateAssignments(variables)) {
33479
+ const result = evaluateWithAssignment(expr, assignment, ce);
33480
+ const row = ce._fn("List", [
33481
+ ...variables.map((v) => assignment[v] ? ce.True : ce.False),
33482
+ result
33483
+ ]);
33484
+ rows.push(row);
33485
+ }
33486
+ return ce._fn("List", rows);
33487
+ }
33488
+
32699
33489
  // src/compute-engine/library/logic.ts
32700
33490
  var LOGIC_LIBRARY = {
32701
33491
  True: {
@@ -32766,11 +33556,96 @@ ${e.message}`);
32766
33556
  signature: "(boolean, boolean) -> boolean",
32767
33557
  evaluate: evaluateImplies
32768
33558
  },
32769
- Exists: { signature: "function", lazy: true },
32770
- NotExists: { signature: "function", lazy: true },
32771
- ExistsUnique: { signature: "function", lazy: true },
32772
- ForAll: { signature: "function", lazy: true },
32773
- NotForAll: { signature: "function", lazy: true },
33559
+ Xor: {
33560
+ description: "Exclusive or: true when an odd number of operands are true",
33561
+ wikidata: "Q498186",
33562
+ broadcastable: true,
33563
+ associative: true,
33564
+ commutative: true,
33565
+ complexity: 10200,
33566
+ signature: "(boolean+) -> boolean",
33567
+ evaluate: evaluateXor
33568
+ },
33569
+ Nand: {
33570
+ description: "Not-and: negation of conjunction",
33571
+ wikidata: "Q189550",
33572
+ broadcastable: true,
33573
+ commutative: true,
33574
+ complexity: 10200,
33575
+ signature: "(boolean+) -> boolean",
33576
+ evaluate: evaluateNand
33577
+ },
33578
+ Nor: {
33579
+ description: "Not-or: negation of disjunction",
33580
+ wikidata: "Q189561",
33581
+ broadcastable: true,
33582
+ commutative: true,
33583
+ complexity: 10200,
33584
+ signature: "(boolean+) -> boolean",
33585
+ evaluate: evaluateNor
33586
+ },
33587
+ // Quantifiers return boolean values (they are propositions)
33588
+ // They support evaluation over finite domains (e.g., ForAll with Element condition)
33589
+ // The first argument can be:
33590
+ // - a symbol (e.g., "x") for symbolic quantification
33591
+ // - an Element expression (e.g., ["Element", "x", ["Set", 1, 2, 3]]) for finite domain evaluation
33592
+ Exists: {
33593
+ signature: "(value, boolean) -> boolean",
33594
+ lazy: true,
33595
+ scoped: true,
33596
+ evaluate: evaluateExists
33597
+ },
33598
+ NotExists: {
33599
+ signature: "(value, boolean) -> boolean",
33600
+ lazy: true,
33601
+ scoped: true,
33602
+ evaluate: (args, options) => {
33603
+ const result = evaluateExists(args, options);
33604
+ if (result?.symbol === "True") return options.engine.False;
33605
+ if (result?.symbol === "False") return options.engine.True;
33606
+ return void 0;
33607
+ }
33608
+ },
33609
+ ExistsUnique: {
33610
+ signature: "(value, boolean) -> boolean",
33611
+ lazy: true,
33612
+ scoped: true,
33613
+ evaluate: evaluateExistsUnique
33614
+ },
33615
+ ForAll: {
33616
+ signature: "(value, boolean) -> boolean",
33617
+ lazy: true,
33618
+ scoped: true,
33619
+ evaluate: evaluateForAll
33620
+ },
33621
+ NotForAll: {
33622
+ signature: "(value, boolean) -> boolean",
33623
+ lazy: true,
33624
+ scoped: true,
33625
+ evaluate: (args, options) => {
33626
+ const result = evaluateForAll(args, options);
33627
+ if (result?.symbol === "True") return options.engine.False;
33628
+ if (result?.symbol === "False") return options.engine.True;
33629
+ return void 0;
33630
+ }
33631
+ },
33632
+ // Predicate application in First-Order Logic.
33633
+ // ["Predicate", "P", "x"] represents the predicate P applied to x.
33634
+ // This is semantically different from a function application: predicates
33635
+ // return boolean values and are used in logical formulas.
33636
+ // In LaTeX, P(x) inside a quantifier context parses to ["Predicate", "P", "x"].
33637
+ Predicate: {
33638
+ description: "Apply a predicate to arguments, returning a boolean",
33639
+ signature: "(symbol, value+) -> boolean",
33640
+ lazy: true,
33641
+ // Predicates remain symbolic unless explicitly defined
33642
+ evaluate: (args, { engine }) => {
33643
+ if (args.length === 0) return void 0;
33644
+ const pred = args[0];
33645
+ if (!pred.symbol) return void 0;
33646
+ return void 0;
33647
+ }
33648
+ },
32774
33649
  KroneckerDelta: {
32775
33650
  description: "Return 1 if the arguments are equal, 0 otherwise",
32776
33651
  signature: "(value+) -> integer",
@@ -32791,82 +33666,189 @@ ${e.message}`);
32791
33666
  evaluate: (args, { engine: ce }) => args[0].symbol === "True" ? ce.One : ce.Zero
32792
33667
  }
32793
33668
  };
32794
- function evaluateAnd(args, { engine: ce }) {
32795
- if (args.length === 0) return ce.True;
32796
- const ops = [];
32797
- for (const arg of args) {
32798
- if (arg.symbol === "False") return ce.False;
32799
- if (arg.symbol !== "True") {
32800
- let duplicate = false;
32801
- for (const x of ops) {
32802
- if (x.isSame(arg)) {
32803
- duplicate = true;
32804
- } else if (arg.operator === "Not" && arg.op1.isSame(x) || x.operator === "Not" && x.op1.isSame(arg)) {
32805
- return ce.False;
32806
- }
32807
- }
32808
- if (!duplicate) ops.push(arg);
32809
- }
32810
- }
32811
- if (ops.length === 0) return ce.True;
32812
- if (ops.length === 1) return ops[0];
32813
- return ce._fn("And", ops);
33669
+ function simplifyLogicFunction(x) {
33670
+ const fn = {
33671
+ And: evaluateAnd,
33672
+ Or: evaluateOr,
33673
+ Not: evaluateNot,
33674
+ Equivalent: evaluateEquivalent,
33675
+ Implies: evaluateImplies,
33676
+ Xor: evaluateXor,
33677
+ Nand: evaluateNand,
33678
+ Nor: evaluateNor
33679
+ }[x.operator];
33680
+ if (!fn || !x.ops) return void 0;
33681
+ const value = fn(x.ops, { engine: x.engine });
33682
+ if (!value) return void 0;
33683
+ return { value, because: "logic" };
32814
33684
  }
32815
- function evaluateOr(args, { engine: ce }) {
32816
- if (args.length === 0) return ce.True;
32817
- const ops = [];
32818
- for (const arg of args) {
32819
- if (arg.symbol === "True") return ce.True;
32820
- if (arg.symbol !== "False") {
32821
- let duplicate = false;
32822
- for (const x of ops) {
32823
- if (x.isSame(arg)) {
32824
- duplicate = true;
32825
- } else if (arg.operator === "Not" && arg.op1.isSame(x) || x.operator === "Not" && x.op1.isSame(arg)) {
32826
- return ce.True;
32827
- }
33685
+ function evaluateForAll(args, { engine: ce }) {
33686
+ if (args.length < 2) return void 0;
33687
+ const condition = args[0];
33688
+ const body = args[1];
33689
+ const canonicalBody = body.canonical;
33690
+ if (canonicalBody.symbol === "True") return ce.True;
33691
+ if (canonicalBody.symbol === "False") return ce.False;
33692
+ const variable = condition.symbol ?? condition.op1?.symbol;
33693
+ if (variable && !bodyContainsVariable(canonicalBody, variable)) {
33694
+ return canonicalBody.evaluate();
33695
+ }
33696
+ const domain = extractFiniteDomain(condition, ce);
33697
+ if (domain) {
33698
+ const nestedDomains = collectNestedDomains(body, ce);
33699
+ if (nestedDomains.length > 0) {
33700
+ return evaluateForAllCartesian(
33701
+ [{ variable: domain.variable, values: domain.values }, ...nestedDomains],
33702
+ getInnermostBody(body),
33703
+ ce
33704
+ );
33705
+ }
33706
+ for (const value of domain.values) {
33707
+ const substituted = body.subs({ [domain.variable]: value }).canonical;
33708
+ const result = substituted.evaluate();
33709
+ if (result.symbol === "False") {
33710
+ return ce.False;
33711
+ }
33712
+ if (result.symbol !== "True") {
33713
+ return void 0;
32828
33714
  }
32829
- if (!duplicate) ops.push(arg);
32830
33715
  }
33716
+ return ce.True;
32831
33717
  }
32832
- if (ops.length === 0) return ce.False;
32833
- if (ops.length === 1) return ops[0];
32834
- return ce._fn("Or", ops);
32835
- }
32836
- function evaluateNot(args, { engine: ce }) {
32837
- const op1 = args[0]?.symbol;
32838
- if (op1 === "True") return ce.False;
32839
- if (op1 === "False") return ce.True;
33718
+ const bodyEval = canonicalBody.evaluate();
33719
+ if (bodyEval.symbol === "True") return ce.True;
33720
+ if (bodyEval.symbol === "False") return ce.False;
32840
33721
  return void 0;
32841
33722
  }
32842
- function evaluateEquivalent(args, { engine: ce }) {
32843
- const lhs = args[0].symbol;
32844
- const rhs = args[1].symbol;
32845
- if (lhs === "True" && rhs === "True" || lhs === "False" && rhs === "False")
32846
- return ce.True;
32847
- if (lhs === "True" && rhs === "False" || lhs === "False" && rhs === "True")
33723
+ function evaluateExists(args, { engine: ce }) {
33724
+ if (args.length < 2) return void 0;
33725
+ const condition = args[0];
33726
+ const body = args[1];
33727
+ const canonicalBody = body.canonical;
33728
+ if (canonicalBody.symbol === "True") return ce.True;
33729
+ if (canonicalBody.symbol === "False") return ce.False;
33730
+ const variable = condition.symbol ?? condition.op1?.symbol;
33731
+ if (variable && !bodyContainsVariable(canonicalBody, variable)) {
33732
+ return canonicalBody.evaluate();
33733
+ }
33734
+ const domain = extractFiniteDomain(condition, ce);
33735
+ if (domain) {
33736
+ const nestedDomains = collectNestedDomains(body, ce);
33737
+ if (nestedDomains.length > 0) {
33738
+ return evaluateExistsCartesian(
33739
+ [{ variable: domain.variable, values: domain.values }, ...nestedDomains],
33740
+ getInnermostBody(body),
33741
+ ce
33742
+ );
33743
+ }
33744
+ for (const value of domain.values) {
33745
+ const substituted = body.subs({ [domain.variable]: value }).canonical;
33746
+ const result = substituted.evaluate();
33747
+ if (result.symbol === "True") {
33748
+ return ce.True;
33749
+ }
33750
+ }
32848
33751
  return ce.False;
33752
+ }
33753
+ const bodyEval = canonicalBody.evaluate();
33754
+ if (bodyEval.symbol === "True") return ce.True;
33755
+ if (bodyEval.symbol === "False") return ce.False;
32849
33756
  return void 0;
32850
33757
  }
32851
- function evaluateImplies(args, { engine: ce }) {
32852
- const lhs = args[0].symbol;
32853
- const rhs = args[1].symbol;
32854
- if (lhs === "True" && rhs === "True" || lhs === "False" && rhs === "False" || lhs === "False" && rhs === "True")
32855
- return ce.True;
32856
- if (lhs === "True" && rhs === "False") return ce.False;
33758
+ function evaluateExistsUnique(args, { engine: ce }) {
33759
+ if (args.length < 2) return void 0;
33760
+ const condition = args[0];
33761
+ const body = args[1];
33762
+ const domain = extractFiniteDomain(condition, ce);
33763
+ if (domain) {
33764
+ let count = 0;
33765
+ for (const value of domain.values) {
33766
+ const substituted = body.subs({ [domain.variable]: value }).canonical;
33767
+ const result = substituted.evaluate();
33768
+ if (result.symbol === "True") {
33769
+ count++;
33770
+ if (count > 1) return ce.False;
33771
+ } else if (result.symbol !== "False") {
33772
+ return void 0;
33773
+ }
33774
+ }
33775
+ return count === 1 ? ce.True : ce.False;
33776
+ }
32857
33777
  return void 0;
32858
33778
  }
32859
- function simplifyLogicFunction(x) {
32860
- const value = {
32861
- And: evaluateAnd,
32862
- Or: evaluateOr,
32863
- Not: evaluateNot,
32864
- Equivalent: evaluateEquivalent,
32865
- Implies: evaluateImplies
32866
- }[x.operator]?.(x.engine, x.ops);
32867
- if (!value) return void 0;
32868
- return { value, because: "logic" };
32869
- }
33779
+ var LOGIC_FUNCTION_LIBRARY = {
33780
+ /**
33781
+ * Convert a boolean expression to Conjunctive Normal Form (CNF).
33782
+ * CNF is a conjunction (And) of disjunctions (Or) of literals.
33783
+ * A literal is either a variable or its negation.
33784
+ *
33785
+ * Example: (A ∨ B) ∧ (¬A ∨ C)
33786
+ */
33787
+ ToCNF: {
33788
+ signature: "(boolean) -> boolean",
33789
+ evaluate: ([expr], { engine: ce }) => {
33790
+ if (!expr) return void 0;
33791
+ return toCNF(expr.evaluate(), ce);
33792
+ }
33793
+ },
33794
+ /**
33795
+ * Convert a boolean expression to Disjunctive Normal Form (DNF).
33796
+ * DNF is a disjunction (Or) of conjunctions (And) of literals.
33797
+ * A literal is either a variable or its negation.
33798
+ *
33799
+ * Example: (A ∧ B) ∨ (¬A ∧ C)
33800
+ */
33801
+ ToDNF: {
33802
+ signature: "(boolean) -> boolean",
33803
+ evaluate: ([expr], { engine: ce }) => {
33804
+ if (!expr) return void 0;
33805
+ return toDNF(expr.evaluate(), ce);
33806
+ }
33807
+ },
33808
+ /**
33809
+ * Check if a boolean expression is satisfiable.
33810
+ * Returns True if there exists an assignment of truth values to variables
33811
+ * that makes the expression true.
33812
+ */
33813
+ IsSatisfiable: {
33814
+ signature: "(boolean) -> boolean",
33815
+ evaluate: ([expr], { engine: ce }) => {
33816
+ if (!expr) return void 0;
33817
+ return isSatisfiable(expr, ce);
33818
+ }
33819
+ },
33820
+ /**
33821
+ * Check if a boolean expression is a tautology.
33822
+ * Returns True if the expression is true for all possible assignments
33823
+ * of truth values to variables.
33824
+ */
33825
+ IsTautology: {
33826
+ signature: "(boolean) -> boolean",
33827
+ evaluate: ([expr], { engine: ce }) => {
33828
+ if (!expr) return void 0;
33829
+ return isTautology(expr, ce);
33830
+ }
33831
+ },
33832
+ /**
33833
+ * Generate a truth table for a boolean expression.
33834
+ * Returns a List of Lists, where each inner list contains the variable
33835
+ * assignments followed by the result.
33836
+ *
33837
+ * Example: TruthTable(["And", "A", "B"]) returns:
33838
+ * [["List", "A", "B", "Result"],
33839
+ * ["List", False, False, False],
33840
+ * ["List", False, True, False],
33841
+ * ["List", True, False, False],
33842
+ * ["List", True, True, True]]
33843
+ */
33844
+ TruthTable: {
33845
+ signature: "(boolean) -> list",
33846
+ evaluate: ([expr], { engine: ce }) => {
33847
+ if (!expr) return void 0;
33848
+ return generateTruthTable(expr, ce);
33849
+ }
33850
+ }
33851
+ };
32870
33852
 
32871
33853
  // src/compute-engine/library/number-theory.ts
32872
33854
  var NUMBER_THEORY_LIBRARY = [
@@ -35398,7 +36380,7 @@ ${e.message}`);
35398
36380
  "domains": [],
35399
36381
  // 'domains': getDomainsDictionary(),
35400
36382
  "linear-algebra": LINEAR_ALGEBRA_LIBRARY,
35401
- "logic": LOGIC_LIBRARY,
36383
+ "logic": [LOGIC_LIBRARY, LOGIC_FUNCTION_LIBRARY],
35402
36384
  "number-theory": NUMBER_THEORY_LIBRARY,
35403
36385
  "numeric": [],
35404
36386
  // @todo // 'numeric': [
@@ -35694,6 +36676,64 @@ Error in definition of "${name}"`,
35694
36676
  const ce = expr.engine;
35695
36677
  let result = null;
35696
36678
  const operator2 = pattern.operator;
36679
+ if (operator2 === "Divide" && expr.numericValue !== null && !expr.denominator.is(1)) {
36680
+ const divideExpr = ce.function(
36681
+ "Divide",
36682
+ [expr.numerator, expr.denominator],
36683
+ { canonical: false, structural: true }
36684
+ );
36685
+ return matchArguments(divideExpr, pattern.ops, substitution, options);
36686
+ }
36687
+ if (operator2 === "Divide" && expr.operator === "Multiply") {
36688
+ const ops = expr.ops;
36689
+ for (let i = 0; i < ops.length; i++) {
36690
+ const op = ops[i];
36691
+ if (op.numericValue !== null && op.numerator.is(1) && !op.denominator.is(1)) {
36692
+ const others = ops.filter((_, j) => j !== i);
36693
+ const numerator = others.length === 1 ? others[0] : ce.function("Multiply", others, { canonical: false });
36694
+ const divideExpr = ce.function(
36695
+ "Divide",
36696
+ [numerator, op.denominator],
36697
+ { canonical: false, structural: true }
36698
+ );
36699
+ const result2 = matchArguments(
36700
+ divideExpr,
36701
+ pattern.ops,
36702
+ substitution,
36703
+ options
36704
+ );
36705
+ if (result2 !== null) return result2;
36706
+ }
36707
+ }
36708
+ }
36709
+ if (operator2 === "Power" && expr.operator === "Divide" && expr.op1.is(1)) {
36710
+ const powerExpr = ce.function(
36711
+ "Power",
36712
+ [expr.op2, ce.number(-1)],
36713
+ { canonical: false, structural: true }
36714
+ );
36715
+ const result2 = matchArguments(
36716
+ powerExpr,
36717
+ pattern.ops,
36718
+ substitution,
36719
+ options
36720
+ );
36721
+ if (result2 !== null) return result2;
36722
+ }
36723
+ if (operator2 === "Power" && expr.operator === "Root") {
36724
+ const powerExpr = ce.function(
36725
+ "Power",
36726
+ [expr.op1, ce.box(["Divide", 1, expr.op2], { canonical: false })],
36727
+ { canonical: false, structural: true }
36728
+ );
36729
+ const result2 = matchArguments(
36730
+ powerExpr,
36731
+ pattern.ops,
36732
+ substitution,
36733
+ options
36734
+ );
36735
+ if (result2 !== null) return result2;
36736
+ }
35697
36737
  if (operator2.startsWith("_")) {
35698
36738
  result = captureWildcard(operator2, ce.box(expr.operator), substitution);
35699
36739
  if (result !== null)
@@ -38743,12 +39783,568 @@ Error in definition of "${name}"`,
38743
39783
  }
38744
39784
  };
38745
39785
 
39786
+ // src/compute-engine/symbolic/simplify-sum.ts
39787
+ function simplifySum(x) {
39788
+ if (x.operator !== "Sum") return void 0;
39789
+ let body = x.op1;
39790
+ const limits = x.op2;
39791
+ if (!body || !limits || limits.operator !== "Limits") return void 0;
39792
+ const index = limits.op1?.symbol;
39793
+ const lower = limits.op2;
39794
+ const upper = limits.op3;
39795
+ if (!index || !lower || !upper) return void 0;
39796
+ const ce = x.engine;
39797
+ if (body.operator === "Sum" || body.operator === "Product") {
39798
+ const simplifiedBody = body.simplify();
39799
+ if (!simplifiedBody.isSame(body)) {
39800
+ const newSum = ce.function("Sum", [simplifiedBody, limits]);
39801
+ return { value: newSum, because: "simplified nested sum/product" };
39802
+ }
39803
+ }
39804
+ if (lower.isNumberLiteral && upper.isNumberLiteral) {
39805
+ const lowerVal = lower.numericValue;
39806
+ const upperVal = upper.numericValue;
39807
+ if (typeof lowerVal === "number" && typeof upperVal === "number" && Number.isInteger(lowerVal) && Number.isInteger(upperVal)) {
39808
+ if (upperVal < lowerVal) {
39809
+ return { value: ce.Zero, because: "empty sum" };
39810
+ }
39811
+ if (upperVal === lowerVal) {
39812
+ return {
39813
+ value: body.subs({ [index]: lower }).simplify(),
39814
+ because: "single term sum"
39815
+ };
39816
+ }
39817
+ }
39818
+ }
39819
+ const bodyUnknowns = new Set(body.unknowns);
39820
+ if (!bodyUnknowns.has(index)) {
39821
+ const count = upper.sub(lower).add(ce.One).simplify();
39822
+ if (count.isNumberLiteral && count.numericValue !== null) {
39823
+ const countVal = typeof count.numericValue === "number" ? count.numericValue : count.numericValue.re;
39824
+ if (countVal <= 0) {
39825
+ return { value: ce.Zero, because: "empty sum" };
39826
+ }
39827
+ }
39828
+ return {
39829
+ value: count.mul(body.simplify()),
39830
+ because: "sum of constant"
39831
+ };
39832
+ }
39833
+ if (body.symbol === index) {
39834
+ const a = lower;
39835
+ const b = upper;
39836
+ const result = b.mul(b.add(ce.One)).sub(a.mul(a.sub(ce.One))).div(2);
39837
+ return { value: result.simplify(), because: "triangular number" };
39838
+ }
39839
+ if (body.operator === "Power" && body.op1?.symbol === index && body.op2?.is(2) && lower.is(1)) {
39840
+ const b = upper;
39841
+ const result = b.mul(b.add(ce.One)).mul(b.mul(2).add(ce.One)).div(6);
39842
+ return { value: result, because: "sum of squares" };
39843
+ }
39844
+ if (body.operator === "Power" && body.op1?.symbol === index && body.op2?.is(3) && lower.is(1)) {
39845
+ const b = upper;
39846
+ const triangular = b.mul(b.add(ce.One)).div(2);
39847
+ return { value: triangular.pow(2), because: "sum of cubes" };
39848
+ }
39849
+ if (body.operator === "Power" && body.op1?.is(-1) && body.op2?.symbol === index && lower.is(0)) {
39850
+ const b = upper;
39851
+ const result = ce.One.add(ce.number(-1).pow(b)).div(2);
39852
+ return { value: result, because: "alternating unit series" };
39853
+ }
39854
+ if (body.operator === "Multiply" && body.ops && lower.is(0)) {
39855
+ let hasAlternating = false;
39856
+ let hasIndex = false;
39857
+ for (const op of body.ops) {
39858
+ if (op.operator === "Power" && op.op1?.is(-1) && op.op2?.symbol === index) {
39859
+ hasAlternating = true;
39860
+ } else if (op.symbol === index) {
39861
+ hasIndex = true;
39862
+ }
39863
+ }
39864
+ if (hasAlternating && hasIndex && body.ops.length === 2) {
39865
+ const b = upper;
39866
+ const result = ce.function("Multiply", [
39867
+ ce.function("Power", [ce.number(-1), b]),
39868
+ ce.function("Floor", [
39869
+ ce.function("Divide", [ce.function("Add", [b, ce.One]), ce.number(2)])
39870
+ ])
39871
+ ]);
39872
+ return { value: result, because: "alternating linear series" };
39873
+ }
39874
+ }
39875
+ if (body.operator === "Add" && body.ops) {
39876
+ let constant = null;
39877
+ let coefficient = null;
39878
+ for (const term of body.ops) {
39879
+ const termUnknowns = new Set(term.unknowns);
39880
+ if (!termUnknowns.has(index)) {
39881
+ constant = constant ? constant.add(term) : term;
39882
+ } else if (term.symbol === index) {
39883
+ coefficient = coefficient ? coefficient.add(ce.One) : ce.One;
39884
+ } else if (term.operator === "Multiply" && term.ops?.some((op) => op.symbol === index)) {
39885
+ const coef = term.ops.filter((op) => op.symbol !== index);
39886
+ if (coef.length === term.ops.length - 1) {
39887
+ const c = coef.length === 1 ? coef[0] : ce.function("Multiply", coef);
39888
+ coefficient = coefficient ? coefficient.add(c) : c;
39889
+ }
39890
+ } else {
39891
+ constant = null;
39892
+ coefficient = null;
39893
+ break;
39894
+ }
39895
+ }
39896
+ if (constant !== null && coefficient !== null) {
39897
+ const m = lower;
39898
+ const b = upper;
39899
+ if (lower.is(0)) {
39900
+ const bPlus1 = ce.function("Add", [b, ce.One]);
39901
+ const inner = ce.function("Add", [
39902
+ constant,
39903
+ ce.function("Divide", [
39904
+ ce.function("Multiply", [coefficient, b]),
39905
+ ce.number(2)
39906
+ ])
39907
+ ]);
39908
+ const result = ce.function("Multiply", [bPlus1, inner]);
39909
+ return { value: result, because: "arithmetic progression" };
39910
+ } else {
39911
+ const numTerms = ce.function("Add", [
39912
+ ce.function("Subtract", [b, m]),
39913
+ ce.One
39914
+ ]);
39915
+ const avgIndex = ce.function("Divide", [
39916
+ ce.function("Add", [m, b]),
39917
+ ce.number(2)
39918
+ ]);
39919
+ const avgValue = ce.function("Add", [
39920
+ constant,
39921
+ ce.function("Multiply", [coefficient, avgIndex])
39922
+ ]);
39923
+ const result = ce.function("Multiply", [numTerms, avgValue]);
39924
+ return { value: result, because: "arithmetic progression" };
39925
+ }
39926
+ }
39927
+ }
39928
+ if (body.operator === "Power" && body.op2?.symbol === index && !new Set(body.op1?.unknowns ?? []).has(index)) {
39929
+ const r = body.op1;
39930
+ const b = upper;
39931
+ if (lower.is(0)) {
39932
+ const numerator = ce.One.sub(r.pow(b.add(ce.One)));
39933
+ const denominator = ce.One.sub(r);
39934
+ return { value: numerator.div(denominator), because: "geometric series" };
39935
+ } else if (lower.is(1)) {
39936
+ const numerator = r.sub(r.pow(b.add(ce.One)));
39937
+ const denominator = ce.One.sub(r);
39938
+ return { value: numerator.div(denominator), because: "geometric series" };
39939
+ }
39940
+ }
39941
+ if (body.operator === "Binomial" && lower.is(0) && body.op2?.symbol === index) {
39942
+ const n = body.op1;
39943
+ if (n && upper.isSame(n)) {
39944
+ const result = ce.function("Power", [ce.number(2), n]);
39945
+ return { value: result, because: "sum of binomial coefficients" };
39946
+ }
39947
+ }
39948
+ if (body.operator === "Multiply" && body.ops && lower.is(0)) {
39949
+ let hasBinomial = false;
39950
+ let hasAlternating = false;
39951
+ let binomialN = null;
39952
+ for (const op of body.ops) {
39953
+ if (op.operator === "Binomial" && op.op2?.symbol === index) {
39954
+ hasBinomial = true;
39955
+ binomialN = op.op1 ?? null;
39956
+ } else if (op.operator === "Power" && op.op1?.is(-1) && op.op2?.symbol === index) {
39957
+ hasAlternating = true;
39958
+ }
39959
+ }
39960
+ if (hasBinomial && hasAlternating && binomialN && upper.isSame(binomialN)) {
39961
+ return { value: ce.Zero, because: "alternating binomial sum" };
39962
+ }
39963
+ let hasIndex = false;
39964
+ binomialN = null;
39965
+ hasBinomial = false;
39966
+ for (const op of body.ops) {
39967
+ if (op.symbol === index) {
39968
+ hasIndex = true;
39969
+ } else if (op.operator === "Binomial" && op.op2?.symbol === index) {
39970
+ hasBinomial = true;
39971
+ binomialN = op.op1 ?? null;
39972
+ }
39973
+ }
39974
+ if (hasIndex && hasBinomial && binomialN && upper.isSame(binomialN) && body.ops.length === 2) {
39975
+ const n = binomialN;
39976
+ const result = ce.function("Multiply", [
39977
+ n,
39978
+ ce.function("Power", [ce.number(2), n.sub(ce.One)])
39979
+ ]);
39980
+ return { value: result, because: "weighted binomial sum" };
39981
+ }
39982
+ let hasIndexSquared = false;
39983
+ binomialN = null;
39984
+ hasBinomial = false;
39985
+ for (const op of body.ops) {
39986
+ if (op.operator === "Power" && op.op1?.symbol === index && op.op2?.is(2)) {
39987
+ hasIndexSquared = true;
39988
+ } else if (op.operator === "Binomial" && op.op2?.symbol === index) {
39989
+ hasBinomial = true;
39990
+ binomialN = op.op1 ?? null;
39991
+ }
39992
+ }
39993
+ if (hasIndexSquared && hasBinomial && binomialN && upper.isSame(binomialN) && body.ops.length === 2) {
39994
+ const n = binomialN;
39995
+ const result = ce.function("Multiply", [
39996
+ n,
39997
+ n.add(ce.One),
39998
+ ce.function("Power", [ce.number(2), n.sub(ce.number(2))])
39999
+ ]);
40000
+ return { value: result, because: "weighted squared binomial sum" };
40001
+ }
40002
+ let hasIndexCubed = false;
40003
+ binomialN = null;
40004
+ hasBinomial = false;
40005
+ for (const op of body.ops) {
40006
+ if (op.operator === "Power" && op.op1?.symbol === index && op.op2?.is(3)) {
40007
+ hasIndexCubed = true;
40008
+ } else if (op.operator === "Binomial" && op.op2?.symbol === index) {
40009
+ hasBinomial = true;
40010
+ binomialN = op.op1 ?? null;
40011
+ }
40012
+ }
40013
+ if (hasIndexCubed && hasBinomial && binomialN && upper.isSame(binomialN) && body.ops.length === 2) {
40014
+ const n = binomialN;
40015
+ const result = ce.function("Multiply", [
40016
+ ce.function("Power", [n, ce.number(2)]),
40017
+ n.add(ce.number(3)),
40018
+ ce.function("Power", [ce.number(2), n.sub(ce.number(3))])
40019
+ ]);
40020
+ return { value: result, because: "weighted cubed binomial sum" };
40021
+ }
40022
+ let hasAltTerm = false;
40023
+ let hasIndexTerm = false;
40024
+ binomialN = null;
40025
+ hasBinomial = false;
40026
+ for (const op of body.ops) {
40027
+ if (op.operator === "Power" && op.op1?.is(-1) && op.op2?.symbol === index) {
40028
+ hasAltTerm = true;
40029
+ } else if (op.symbol === index) {
40030
+ hasIndexTerm = true;
40031
+ } else if (op.operator === "Binomial" && op.op2?.symbol === index) {
40032
+ hasBinomial = true;
40033
+ binomialN = op.op1 ?? null;
40034
+ }
40035
+ }
40036
+ if (hasAltTerm && hasIndexTerm && hasBinomial && binomialN && upper.isSame(binomialN) && body.ops.length === 3) {
40037
+ return { value: ce.Zero, because: "alternating weighted binomial sum" };
40038
+ }
40039
+ }
40040
+ if (body.operator === "Power" && body.op1?.operator === "Binomial" && body.op2?.is(2) && lower.is(0)) {
40041
+ const binomial2 = body.op1;
40042
+ const n = binomial2.op1;
40043
+ const k = binomial2.op2;
40044
+ if (n && k?.symbol === index && upper.isSame(n)) {
40045
+ const result = ce.function("Binomial", [
40046
+ ce.function("Multiply", [ce.number(2), n]),
40047
+ n
40048
+ ]);
40049
+ return { value: result, because: "sum of binomial squares" };
40050
+ }
40051
+ }
40052
+ if (body.operator === "Multiply" && body.ops?.length === 2 && lower.is(1)) {
40053
+ const [op1, op2] = body.ops;
40054
+ const isKTimesKPlus1 = op1.symbol === index && op2.operator === "Add" && op2.ops?.length === 2 && op2.ops.some((o) => o.symbol === index) && op2.ops.some((o) => o.is(1)) || op2.symbol === index && op1.operator === "Add" && op1.ops?.length === 2 && op1.ops.some((o) => o.symbol === index) && op1.ops.some((o) => o.is(1));
40055
+ if (isKTimesKPlus1) {
40056
+ const n = upper;
40057
+ const result = ce.function("Divide", [
40058
+ ce.function("Multiply", [
40059
+ n,
40060
+ ce.function("Add", [n, ce.One]),
40061
+ ce.function("Add", [n, ce.number(2)])
40062
+ ]),
40063
+ ce.number(3)
40064
+ ]);
40065
+ return { value: result, because: "sum of k*(k+1)" };
40066
+ }
40067
+ }
40068
+ if (body.operator === "Divide" && body.op1?.is(1) && body.op2?.operator === "Multiply") {
40069
+ const denom = body.op2;
40070
+ if (denom.ops?.length === 2) {
40071
+ const [d1, d2] = denom.ops;
40072
+ if (lower.is(1)) {
40073
+ const isKTimesKPlus1 = d1.symbol === index && d2.operator === "Add" && d2.ops?.length === 2 && d2.ops.some((op) => op.symbol === index) && d2.ops.some((op) => op.is(1)) || d2.symbol === index && d1.operator === "Add" && d1.ops?.length === 2 && d1.ops.some((op) => op.symbol === index) && d1.ops.some((op) => op.is(1));
40074
+ if (isKTimesKPlus1) {
40075
+ const n = upper;
40076
+ const result = n.div(n.add(ce.One));
40077
+ return { value: result, because: "partial fractions (telescoping)" };
40078
+ }
40079
+ }
40080
+ if (lower.is(2)) {
40081
+ const isKTimesKMinus1 = d1.symbol === index && d2.operator === "Add" && d2.ops?.length === 2 && d2.ops.some((op) => op.symbol === index) && d2.ops.some((op) => op.is(-1)) || d2.symbol === index && d1.operator === "Add" && d1.ops?.length === 2 && d1.ops.some((op) => op.symbol === index) && d1.ops.some((op) => op.is(-1));
40082
+ if (isKTimesKMinus1) {
40083
+ const n = upper;
40084
+ const result = n.sub(ce.One).div(n);
40085
+ return { value: result, because: "partial fractions (telescoping k*(k-1))" };
40086
+ }
40087
+ }
40088
+ }
40089
+ }
40090
+ if (body.operator === "Multiply" && body.ops) {
40091
+ const constantFactors = [];
40092
+ const indexFactors = [];
40093
+ for (const factor2 of body.ops) {
40094
+ const factorUnknowns = new Set(factor2.unknowns);
40095
+ if (factorUnknowns.has(index)) {
40096
+ indexFactors.push(factor2);
40097
+ } else {
40098
+ constantFactors.push(factor2);
40099
+ }
40100
+ }
40101
+ if (constantFactors.length > 0 && indexFactors.length > 0) {
40102
+ const constant = constantFactors.length === 1 ? constantFactors[0] : ce.function("Multiply", constantFactors);
40103
+ const indexPart = indexFactors.length === 1 ? indexFactors[0] : ce.function("Multiply", indexFactors);
40104
+ const newSum = ce.function("Sum", [indexPart, limits]);
40105
+ return {
40106
+ value: constant.mul(newSum),
40107
+ because: "factor out constant from sum"
40108
+ };
40109
+ }
40110
+ }
40111
+ return void 0;
40112
+ }
40113
+
40114
+ // src/compute-engine/symbolic/simplify-product.ts
40115
+ function simplifyProduct(x) {
40116
+ if (x.operator !== "Product") return void 0;
40117
+ let body = x.op1;
40118
+ const limits = x.op2;
40119
+ if (!body || !limits || limits.operator !== "Limits") return void 0;
40120
+ const index = limits.op1?.symbol;
40121
+ const lower = limits.op2;
40122
+ const upper = limits.op3;
40123
+ if (!index || !lower || !upper) return void 0;
40124
+ const ce = x.engine;
40125
+ if (body.operator === "Sum" || body.operator === "Product") {
40126
+ const simplifiedBody = body.simplify();
40127
+ if (!simplifiedBody.isSame(body)) {
40128
+ const newProduct = ce.function("Product", [simplifiedBody, limits]);
40129
+ return { value: newProduct, because: "simplified nested sum/product" };
40130
+ }
40131
+ }
40132
+ if (lower.isNumberLiteral && upper.isNumberLiteral) {
40133
+ const lowerVal = lower.numericValue;
40134
+ const upperVal = upper.numericValue;
40135
+ if (typeof lowerVal === "number" && typeof upperVal === "number" && Number.isInteger(lowerVal) && Number.isInteger(upperVal)) {
40136
+ if (upperVal < lowerVal) {
40137
+ return { value: ce.One, because: "empty product" };
40138
+ }
40139
+ if (upperVal === lowerVal) {
40140
+ return {
40141
+ value: body.subs({ [index]: lower }).simplify(),
40142
+ because: "single term product"
40143
+ };
40144
+ }
40145
+ }
40146
+ }
40147
+ const bodyUnknowns = new Set(body.unknowns);
40148
+ if (!bodyUnknowns.has(index)) {
40149
+ const count = upper.sub(lower).add(ce.One).simplify();
40150
+ if (count.isNumberLiteral && count.numericValue !== null) {
40151
+ const countVal = typeof count.numericValue === "number" ? count.numericValue : count.numericValue.re;
40152
+ if (countVal <= 0) {
40153
+ return { value: ce.One, because: "empty product" };
40154
+ }
40155
+ }
40156
+ return {
40157
+ value: body.simplify().pow(count),
40158
+ because: "product of constant"
40159
+ };
40160
+ }
40161
+ if (body.symbol === index && lower.is(1)) {
40162
+ return {
40163
+ value: ce.function("Factorial", [upper]),
40164
+ because: "factorial"
40165
+ };
40166
+ }
40167
+ if (body.operator === "Add" && body.ops?.length === 2 && lower.is(1)) {
40168
+ const [op1, op2] = body.ops;
40169
+ let indexTerm = null;
40170
+ let constTerm = null;
40171
+ if (op1.symbol === index && !new Set(op2.unknowns).has(index)) {
40172
+ indexTerm = op1;
40173
+ constTerm = op2;
40174
+ } else if (op2.symbol === index && !new Set(op1.unknowns).has(index)) {
40175
+ indexTerm = op2;
40176
+ constTerm = op1;
40177
+ }
40178
+ if (indexTerm && constTerm) {
40179
+ const b = upper;
40180
+ const c = constTerm;
40181
+ const result = ce.function("Divide", [
40182
+ ce.function("Factorial", [ce.function("Add", [b, c])]),
40183
+ ce.function("Factorial", [c])
40184
+ ]);
40185
+ return { value: result, because: "shifted factorial" };
40186
+ }
40187
+ }
40188
+ if (body.operator === "Divide" && lower.is(1)) {
40189
+ const num = body.op1;
40190
+ const denom = body.op2;
40191
+ if (denom?.symbol === index && num?.operator === "Add" && num.ops?.length === 2 && num.ops.some((o) => o.symbol === index) && num.ops.some((o) => o.is(1))) {
40192
+ return { value: upper.add(ce.One), because: "telescoping product" };
40193
+ }
40194
+ }
40195
+ if (body.operator === "Add" && body.ops?.length === 2 && lower.is(2)) {
40196
+ let hasOne = false;
40197
+ let hasNegInvSq = false;
40198
+ for (const op of body.ops) {
40199
+ if (op.is(1)) {
40200
+ hasOne = true;
40201
+ } else if (op.operator === "Negate" && op.op1?.operator === "Power" && op.op1.op1?.symbol === index && op.op1.op2?.is(-2)) {
40202
+ hasNegInvSq = true;
40203
+ } else if (op.operator === "Power" && op.op1?.symbol === index && op.op2?.is(-2)) {
40204
+ } else if (op.operator === "Multiply" && op.ops?.some((o) => o.is(-1)) && op.ops?.some(
40205
+ (o) => o.operator === "Power" && o.op1?.symbol === index && o.op2?.is(-2)
40206
+ )) {
40207
+ hasNegInvSq = true;
40208
+ }
40209
+ }
40210
+ if (hasOne && hasNegInvSq) {
40211
+ const n = upper;
40212
+ const result = ce.function("Divide", [
40213
+ ce.function("Add", [n, ce.One]),
40214
+ ce.function("Multiply", [ce.number(2), n])
40215
+ ]);
40216
+ return { value: result, because: "Wallis-like product" };
40217
+ }
40218
+ }
40219
+ if (body.operator === "Add" && body.ops?.length === 2 && lower.is(1)) {
40220
+ const [op1, op2] = body.ops;
40221
+ let hasLinearTerm = false;
40222
+ let coefficient = 0;
40223
+ let constantTerm = 0;
40224
+ for (const op of body.ops) {
40225
+ if (op.isNumberLiteral && typeof op.numericValue === "number") {
40226
+ constantTerm = op.numericValue;
40227
+ } else if (op.operator === "Multiply" && op.ops?.length === 2) {
40228
+ const [a, b] = op.ops;
40229
+ if (a.isNumberLiteral && typeof a.numericValue === "number" && b.symbol === index) {
40230
+ coefficient = a.numericValue;
40231
+ hasLinearTerm = true;
40232
+ } else if (b.isNumberLiteral && typeof b.numericValue === "number" && a.symbol === index) {
40233
+ coefficient = b.numericValue;
40234
+ hasLinearTerm = true;
40235
+ }
40236
+ }
40237
+ }
40238
+ if (hasLinearTerm && coefficient === 2 && constantTerm === -1) {
40239
+ const b = upper;
40240
+ const result = ce.function("Factorial2", [
40241
+ ce.function("Subtract", [
40242
+ ce.function("Multiply", [ce.number(2), b]),
40243
+ ce.One
40244
+ ])
40245
+ ]);
40246
+ return { value: result, because: "odd double factorial" };
40247
+ }
40248
+ }
40249
+ if (body.operator === "Multiply" && body.ops?.length === 2 && lower.is(1)) {
40250
+ const [op1, op2] = body.ops;
40251
+ if (op1.is(2) && op2.symbol === index || op2.is(2) && op1.symbol === index) {
40252
+ const b = upper;
40253
+ const result = ce.function("Multiply", [
40254
+ ce.function("Power", [ce.number(2), b]),
40255
+ ce.function("Factorial", [b])
40256
+ ]);
40257
+ return { value: result, because: "even double factorial" };
40258
+ }
40259
+ }
40260
+ if (body.operator === "Add" && body.ops?.length === 2 && lower.is(0)) {
40261
+ let base = null;
40262
+ let hasIndex = false;
40263
+ for (const op of body.ops) {
40264
+ if (op.symbol === index) {
40265
+ hasIndex = true;
40266
+ } else if (!new Set(op.unknowns).has(index)) {
40267
+ base = op;
40268
+ }
40269
+ }
40270
+ if (hasIndex && base) {
40271
+ const n = upper.add(ce.One).simplify();
40272
+ const result = ce.function("Pochhammer", [base, n]);
40273
+ return { value: result, because: "rising factorial (Pochhammer)" };
40274
+ }
40275
+ }
40276
+ if (lower.is(0)) {
40277
+ let base = null;
40278
+ let hasNegIndex = false;
40279
+ if (body.operator === "Subtract" && body.ops?.length === 2) {
40280
+ const [op1, op2] = body.ops;
40281
+ if (op2.symbol === index && !new Set(op1.unknowns).has(index)) {
40282
+ base = op1;
40283
+ hasNegIndex = true;
40284
+ }
40285
+ } else if (body.operator === "Add" && body.ops?.length === 2) {
40286
+ for (const op of body.ops) {
40287
+ if (op.operator === "Negate" && op.op1?.symbol === index) {
40288
+ hasNegIndex = true;
40289
+ } else if (!new Set(op.unknowns).has(index)) {
40290
+ base = op;
40291
+ }
40292
+ }
40293
+ }
40294
+ if (hasNegIndex && base) {
40295
+ const n = upper.add(ce.One).simplify();
40296
+ const result = ce.function("Divide", [
40297
+ ce.function("Factorial", [base]),
40298
+ ce.function("Factorial", [base.sub(n)])
40299
+ ]);
40300
+ return { value: result, because: "falling factorial" };
40301
+ }
40302
+ }
40303
+ if (body.operator === "Multiply" && body.ops) {
40304
+ const constantFactors = [];
40305
+ const indexFactors = [];
40306
+ for (const factor2 of body.ops) {
40307
+ const factorUnknowns = new Set(factor2.unknowns);
40308
+ if (factorUnknowns.has(index)) {
40309
+ indexFactors.push(factor2);
40310
+ } else {
40311
+ constantFactors.push(factor2);
40312
+ }
40313
+ }
40314
+ if (constantFactors.length > 0 && indexFactors.length > 0) {
40315
+ const constant = constantFactors.length === 1 ? constantFactors[0] : ce.function("Multiply", constantFactors);
40316
+ const indexPart = indexFactors.length === 1 ? indexFactors[0] : ce.function("Multiply", indexFactors);
40317
+ const count = upper.sub(lower).add(ce.One).simplify();
40318
+ const newProduct = ce.function("Product", [indexPart, limits]);
40319
+ return {
40320
+ value: constant.pow(count).mul(newProduct),
40321
+ because: "factor out constant from product"
40322
+ };
40323
+ }
40324
+ }
40325
+ return void 0;
40326
+ }
40327
+
38746
40328
  // src/compute-engine/symbolic/simplify-rules.ts
38747
40329
  var SIMPLIFY_RULES = [
38748
40330
  // The Golden Ratio, a constant that can be simplified
38749
40331
  "\\varphi -> \\frac{1+\\sqrt{5}}{2}",
38750
40332
  simplifyRelationalOperator,
38751
40333
  simplifySystemOfEquations,
40334
+ //
40335
+ // Cancel common polynomial factors in Divide expressions
40336
+ // e.g., (x² - 1)/(x - 1) → x + 1
40337
+ // Must run before expand to preserve polynomial structure
40338
+ //
40339
+ (x) => {
40340
+ if (x.operator !== "Divide") return void 0;
40341
+ const unknowns = x.unknowns;
40342
+ if (unknowns.length !== 1) return void 0;
40343
+ const variable = unknowns[0];
40344
+ const result = cancelCommonFactors(x, variable);
40345
+ if (result.isSame(x)) return void 0;
40346
+ return { value: result, because: "cancel common polynomial factors" };
40347
+ },
38752
40348
  // Try to expand the expression:
38753
40349
  // x*(y+z) -> x*y + x*z
38754
40350
  // { replace: (x) => expand(x) ?? undefined, id: 'expand' },
@@ -38838,7 +40434,8 @@ Error in definition of "${name}"`,
38838
40434
  const ce = x.engine;
38839
40435
  if (s === void 0) return void 0;
38840
40436
  if (s === "positive") return { value: ce.One, because: "sign positive" };
38841
- if (s === "negative") return { value: ce.One, because: "sign negative" };
40437
+ if (s === "negative")
40438
+ return { value: ce.NegativeOne, because: "sign negative" };
38842
40439
  if (s === "zero") return { value: ce.Zero, because: "sign zero" };
38843
40440
  if (s === "unsigned") return { value: ce.NaN, because: "sign unsinged" };
38844
40441
  return void 0;
@@ -38921,14 +40518,10 @@ Error in definition of "${name}"`,
38921
40518
  because: "congruent"
38922
40519
  };
38923
40520
  },
38924
- //
38925
- // Product, Sum
38926
- //
38927
- (x) => {
38928
- if (x.operator === "Max") {
38929
- }
38930
- return void 0;
38931
- },
40521
+ // Sum simplification (extracted to simplify-sum.ts)
40522
+ simplifySum,
40523
+ // Product simplification (extracted to simplify-product.ts)
40524
+ simplifyProduct,
38932
40525
  //
38933
40526
  // Constructible values of trig functions
38934
40527
  //
@@ -41154,7 +42747,8 @@ Error in definition of "${name}"`,
41154
42747
  return BoxedType.unknown;
41155
42748
  },
41156
42749
  parseUnexpectedToken: (_lhs, _parser) => null,
41157
- preserveLatex: false
42750
+ preserveLatex: false,
42751
+ quantifierScope: "tight"
41158
42752
  };
41159
42753
  const result = parse2(
41160
42754
  asLatexString(latex) ?? latex,
@@ -41307,10 +42901,10 @@ Error in definition of "${name}"`,
41307
42901
  }
41308
42902
 
41309
42903
  // src/compute-engine.ts
41310
- var version = "0.31.0";
42904
+ var version = "0.32.1";
41311
42905
  globalThis[Symbol.for("io.cortexjs.compute-engine")] = {
41312
42906
  ComputeEngine: ComputeEngine.prototype.constructor,
41313
- version: "0.31.0"
42907
+ version: "0.32.1"
41314
42908
  };
41315
42909
  return __toCommonJS(compute_engine_exports);
41316
42910
  })();