@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
 
3
3
  // node_modules/complex-esm/dist/src/complex.js
4
4
  var cosh = Math.cosh || function(x) {
@@ -7841,7 +7841,11 @@ function serializePower(serializer, expr) {
7841
7841
  if (denom === 2) {
7842
7842
  return serializer.serialize(["Divide", "1", ["Sqrt", base]]);
7843
7843
  }
7844
- return serializer.serialize(["Divide", "1", ["Root", base, denom]]);
7844
+ return serializer.serialize([
7845
+ "Divide",
7846
+ "1",
7847
+ ["Root", base, operand(exp2, 2)]
7848
+ ]);
7845
7849
  }
7846
7850
  if (denom === 2) {
7847
7851
  return `${serializer.serialize(["Sqrt", base])}^{${serializer.serialize(
@@ -7855,13 +7859,20 @@ function serializePower(serializer, expr) {
7855
7859
  }
7856
7860
  }
7857
7861
  }
7862
+ const wrapNegativeBase = (latex) => latex.startsWith("-") ? serializer.wrapString(latex, "normal") : latex;
7858
7863
  if (operator(base) === "Power") {
7859
7864
  const baseBody = operand(base, 1);
7860
7865
  const baseExponent = operand(base, 2);
7866
+ const baseBodyLatex = wrapNegativeBase(serializer.wrapShort(baseBody));
7867
+ const baseExponentLatex = serializer.wrapShort(baseExponent);
7861
7868
  return `
7862
- ${serializer.wrapShort(baseBody)}^{${supsub("^", serializer.wrapShort(baseExponent), serializer.serialize(exp2))}}`;
7869
+ ${baseBodyLatex}^{${supsub("^", baseExponentLatex, serializer.serialize(exp2))}}`;
7863
7870
  }
7864
- return supsub("^", serializer.wrapShort(base), serializer.serialize(exp2));
7871
+ return supsub(
7872
+ "^",
7873
+ wrapNegativeBase(serializer.wrapShort(base)),
7874
+ serializer.serialize(exp2)
7875
+ );
7865
7876
  }
7866
7877
  var DEFINITIONS_ARITHMETIC = [
7867
7878
  // Constants
@@ -8142,8 +8153,9 @@ var DEFINITIONS_ARITHMETIC = [
8142
8153
  },
8143
8154
  {
8144
8155
  name: "GCD",
8145
- latexTrigger: ["\\gcd"]
8156
+ latexTrigger: ["\\gcd"],
8146
8157
  // command from amsmath package
8158
+ kind: "function"
8147
8159
  },
8148
8160
  {
8149
8161
  symbolTrigger: "gcd",
@@ -8201,9 +8213,14 @@ var DEFINITIONS_ARITHMETIC = [
8201
8213
  },
8202
8214
  {
8203
8215
  name: "LCM",
8204
- symbolTrigger: "lcm",
8216
+ latexTrigger: ["\\lcm"],
8205
8217
  kind: "function"
8206
8218
  },
8219
+ {
8220
+ symbolTrigger: "lcm",
8221
+ kind: "function",
8222
+ parse: "LCM"
8223
+ },
8207
8224
  {
8208
8225
  symbolTrigger: "LCM",
8209
8226
  kind: "function",
@@ -8477,7 +8494,11 @@ var DEFINITIONS_ARITHMETIC = [
8477
8494
  {
8478
8495
  name: "Square",
8479
8496
  precedence: 720,
8480
- serialize: (serializer, expr) => serializer.wrapShort(operand(expr, 1)) + "^2"
8497
+ serialize: (serializer, expr) => {
8498
+ const base = serializer.wrapShort(operand(expr, 1));
8499
+ const wrapped = base.startsWith("-") ? serializer.wrapString(base, "normal") : base;
8500
+ return wrapped + "^2";
8501
+ }
8481
8502
  },
8482
8503
  {
8483
8504
  latexTrigger: ["\\sum"],
@@ -10587,40 +10608,43 @@ var DEFINITIONS_LOGIC = [
10587
10608
  parse: "False"
10588
10609
  },
10589
10610
  // Operators
10611
+ // Logic operators have lower precedence than comparisons (245)
10612
+ // so that `x = 1 \lor x = 2` parses as `(x = 1) \lor (x = 2)`
10613
+ // See https://github.com/cortex-js/compute-engine/issues/243
10590
10614
  {
10591
10615
  name: "And",
10592
10616
  kind: "infix",
10593
10617
  latexTrigger: ["\\land"],
10594
- precedence: 317
10618
+ precedence: 235
10595
10619
  // serialize: '\\land',
10596
10620
  },
10597
- { kind: "infix", latexTrigger: ["\\wedge"], parse: "And", precedence: 317 },
10598
- { kind: "infix", latexTrigger: "\\&", parse: "And", precedence: 317 },
10621
+ { kind: "infix", latexTrigger: ["\\wedge"], parse: "And", precedence: 235 },
10622
+ { kind: "infix", latexTrigger: "\\&", parse: "And", precedence: 235 },
10599
10623
  {
10600
10624
  kind: "infix",
10601
10625
  latexTrigger: "\\operatorname{and}",
10602
10626
  parse: "And",
10603
- precedence: 317
10627
+ precedence: 235
10604
10628
  },
10605
10629
  {
10606
10630
  name: "Or",
10607
10631
  kind: "infix",
10608
10632
  latexTrigger: ["\\lor"],
10609
- precedence: 310
10633
+ precedence: 230
10610
10634
  },
10611
- { kind: "infix", latexTrigger: ["\\vee"], parse: "Or", precedence: 310 },
10612
- { kind: "infix", latexTrigger: "\\parallel", parse: "Or", precedence: 310 },
10635
+ { kind: "infix", latexTrigger: ["\\vee"], parse: "Or", precedence: 230 },
10636
+ { kind: "infix", latexTrigger: "\\parallel", parse: "Or", precedence: 230 },
10613
10637
  {
10614
10638
  kind: "infix",
10615
10639
  latexTrigger: "\\operatorname{or}",
10616
10640
  parse: "Or",
10617
- precedence: 310
10641
+ precedence: 230
10618
10642
  },
10619
10643
  {
10620
10644
  name: "Xor",
10621
10645
  kind: "infix",
10622
10646
  latexTrigger: ["\\veebar"],
10623
- precedence: 315
10647
+ precedence: 232
10624
10648
  },
10625
10649
  // Possible alt: \oplus ⊕ U+2295
10626
10650
  {
@@ -10639,7 +10663,7 @@ var DEFINITIONS_LOGIC = [
10639
10663
  name: "Nand",
10640
10664
  kind: "infix",
10641
10665
  latexTrigger: ["\\barwedge"],
10642
- precedence: 315
10666
+ precedence: 232
10643
10667
  // serialize: '\\mid',
10644
10668
  },
10645
10669
  {
@@ -10647,7 +10671,7 @@ var DEFINITIONS_LOGIC = [
10647
10671
  kind: "infix",
10648
10672
  latexTrigger: ["\u22BD"],
10649
10673
  // bar vee
10650
- precedence: 315
10674
+ precedence: 232
10651
10675
  // serialize: '\\downarrow',
10652
10676
  },
10653
10677
  // Functions
@@ -10743,7 +10767,7 @@ var DEFINITIONS_LOGIC = [
10743
10767
  latexTrigger: ["\\forall"],
10744
10768
  precedence: 200,
10745
10769
  // Has to be lower than COMPARISON_PRECEDENCE
10746
- serialize: "\\forall",
10770
+ serialize: serializeQuantifier("\\forall"),
10747
10771
  parse: parseQuantifier("ForAll")
10748
10772
  },
10749
10773
  {
@@ -10752,7 +10776,7 @@ var DEFINITIONS_LOGIC = [
10752
10776
  latexTrigger: ["\\exists"],
10753
10777
  precedence: 200,
10754
10778
  // Has to be lower than COMPARISON_PRECEDENCE,
10755
- serialize: "\\exists",
10779
+ serialize: serializeQuantifier("\\exists"),
10756
10780
  parse: parseQuantifier("Exists")
10757
10781
  },
10758
10782
  {
@@ -10761,7 +10785,7 @@ var DEFINITIONS_LOGIC = [
10761
10785
  latexTrigger: ["\\exists", "!"],
10762
10786
  precedence: 200,
10763
10787
  // Has to be lower than COMPARISON_PRECEDENCE,
10764
- serialize: "\\exists!",
10788
+ serialize: serializeQuantifier("\\exists!"),
10765
10789
  parse: parseQuantifier("ExistsUnique")
10766
10790
  },
10767
10791
  {
@@ -10770,7 +10794,7 @@ var DEFINITIONS_LOGIC = [
10770
10794
  latexTrigger: ["\\lnot", "\\forall"],
10771
10795
  precedence: 200,
10772
10796
  // Has to be lower than COMPARISON_PRECEDENCE
10773
- serialize: "\\lnot\\forall",
10797
+ serialize: serializeQuantifier("\\lnot\\forall"),
10774
10798
  parse: parseQuantifier("NotForAll")
10775
10799
  },
10776
10800
  {
@@ -10779,7 +10803,7 @@ var DEFINITIONS_LOGIC = [
10779
10803
  latexTrigger: ["\\lnot", "\\exists"],
10780
10804
  precedence: 200,
10781
10805
  // Has to be lower than COMPARISON_PRECEDENCE,
10782
- serialize: "\\lnot\\exists",
10806
+ serialize: serializeQuantifier("\\lnot\\exists"),
10783
10807
  parse: parseQuantifier("NotExists")
10784
10808
  },
10785
10809
  {
@@ -10840,19 +10864,53 @@ var DEFINITIONS_LOGIC = [
10840
10864
  if (!DEFINITIONS_INEQUALITIES.some((x) => x.name === h)) return null;
10841
10865
  return ["Boole", body];
10842
10866
  }
10867
+ },
10868
+ // Predicate application in First-Order Logic.
10869
+ // ["Predicate", "P", "x", "y"] serializes to "P(x, y)"
10870
+ {
10871
+ name: "Predicate",
10872
+ serialize: (serializer, expr) => {
10873
+ const args = operands(expr);
10874
+ if (args.length === 0) return "";
10875
+ const pred = args[0];
10876
+ const predStr = typeof pred === "string" ? pred : serializer.serialize(pred);
10877
+ if (args.length === 1) return predStr;
10878
+ const argStrs = args.slice(1).map((arg) => serializer.serialize(arg));
10879
+ return `${predStr}(${argStrs.join(", ")})`;
10880
+ }
10843
10881
  }
10844
10882
  ];
10883
+ function serializeQuantifier(quantifierSymbol) {
10884
+ return (serializer, expr) => {
10885
+ const args = operands(expr);
10886
+ if (args.length === 0) return quantifierSymbol;
10887
+ if (args.length === 1)
10888
+ return `${quantifierSymbol} ${serializer.serialize(args[0])}`;
10889
+ const boundVar = serializer.serialize(args[0]);
10890
+ const body = serializer.serialize(args[1]);
10891
+ return `${quantifierSymbol} ${boundVar}, ${body}`;
10892
+ };
10893
+ }
10894
+ function tightBindingCondition(p, terminator) {
10895
+ 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);
10896
+ }
10845
10897
  function parseQuantifier(kind) {
10846
10898
  return (parser, terminator) => {
10847
10899
  const index = parser.index;
10900
+ const useTightBinding = parser.options.quantifierScope !== "loose";
10848
10901
  const symbol2 = parser.parseSymbol(terminator);
10849
10902
  if (symbol2) {
10850
10903
  parser.skipSpace();
10851
10904
  if (parser.match(",") || parser.match("\\mid") || parser.match(".") || parser.match(":") || parser.match("\\colon")) {
10852
- const body2 = parser.parseExpression(terminator);
10905
+ const bodyTerminator = useTightBinding ? { ...terminator, condition: (p) => tightBindingCondition(p, terminator) } : terminator;
10906
+ parser.enterQuantifierScope();
10907
+ const body2 = parser.parseExpression(bodyTerminator);
10908
+ parser.exitQuantifierScope();
10853
10909
  return [kind, symbol2, missingIfEmpty(body2)];
10854
10910
  }
10911
+ parser.enterQuantifierScope();
10855
10912
  const body = parser.parseEnclosure();
10913
+ parser.exitQuantifierScope();
10856
10914
  if (body) return [kind, symbol2, missingIfEmpty(body)];
10857
10915
  }
10858
10916
  parser.index = index;
@@ -10860,11 +10918,16 @@ function parseQuantifier(kind) {
10860
10918
  if (condition === null) return null;
10861
10919
  parser.skipSpace();
10862
10920
  if (parser.matchAny([",", "\\mid", ":", "\\colon"])) {
10863
- const body = parser.parseExpression(terminator);
10921
+ const bodyTerminator = useTightBinding ? { ...terminator, condition: (p) => tightBindingCondition(p, terminator) } : terminator;
10922
+ parser.enterQuantifierScope();
10923
+ const body = parser.parseExpression(bodyTerminator);
10924
+ parser.exitQuantifierScope();
10864
10925
  return [kind, condition, missingIfEmpty(body)];
10865
10926
  }
10866
10927
  if (parser.match("(")) {
10928
+ parser.enterQuantifierScope();
10867
10929
  const body = parser.parseExpression(terminator);
10930
+ parser.exitQuantifierScope();
10868
10931
  if (!parser.match(")")) return null;
10869
10932
  return [kind, condition, missingIfEmpty(body)];
10870
10933
  }
@@ -11353,7 +11416,7 @@ function parseTrig(op) {
11353
11416
  "\\ch": "Cosh",
11354
11417
  // Non-standard
11355
11418
  "\\cos": "Cos",
11356
- "\\cosh": "Csch",
11419
+ "\\cosh": "Cosh",
11357
11420
  "\\cosec": "Csc",
11358
11421
  // Non-standard
11359
11422
  "\\cot": "Cot",
@@ -13265,8 +13328,41 @@ function parseInvalidSymbol(parser) {
13265
13328
  return parser.error(["invalid-symbol", { str: validateSymbol(id) }], start);
13266
13329
  }
13267
13330
  function parseSymbol(parser) {
13268
- if (/^[a-zA-Z]$/.test(parser.peek) || /^\p{XIDS}$/u.test(parser.peek))
13269
- return parser.nextToken();
13331
+ if (/^[a-zA-Z]$/.test(parser.peek) || /^\p{XIDS}$/u.test(parser.peek)) {
13332
+ let id2 = parser.nextToken();
13333
+ while (!parser.atEnd) {
13334
+ const currentPeek = parser.peek;
13335
+ if (currentPeek !== "_") break;
13336
+ const underscoreIndex = parser.index;
13337
+ parser.nextToken();
13338
+ const hasBrace = parser.match("<{>");
13339
+ if (hasBrace) {
13340
+ const firstToken = parser.peek;
13341
+ if (firstToken === "(" || firstToken === "\\lparen" || firstToken === "\\left") {
13342
+ parser.index = underscoreIndex;
13343
+ break;
13344
+ }
13345
+ const sub2 = parseSymbolBody(parser);
13346
+ const hasOperators = sub2 !== null && /plus|minus|times|ast/.test(sub2);
13347
+ if (sub2 === null || sub2.includes(",") || hasOperators || parser.peek !== "<}>") {
13348
+ parser.index = underscoreIndex;
13349
+ break;
13350
+ }
13351
+ parser.match("<}>");
13352
+ id2 += "_" + sub2;
13353
+ } else {
13354
+ const subToken = parser.peek;
13355
+ if (/^[a-zA-Z0-9]$/.test(subToken) || /^\p{XIDS}$/u.test(subToken)) {
13356
+ parser.nextToken();
13357
+ id2 += "_" + subToken;
13358
+ } else {
13359
+ parser.index = underscoreIndex;
13360
+ break;
13361
+ }
13362
+ }
13363
+ }
13364
+ return id2;
13365
+ }
13270
13366
  let id = matchPrefixedSymbol(parser);
13271
13367
  if (!id) {
13272
13368
  id = "";
@@ -13366,6 +13462,18 @@ var _Parser = class {
13366
13462
  throw new Error(`Symbol ${id} already declared as a different type`);
13367
13463
  this.symbolTable.ids[id] = type2;
13368
13464
  }
13465
+ // Track whether we're inside a quantifier body (ForAll, Exists, etc.)
13466
+ // When true, single uppercase letters followed by () are parsed as predicates
13467
+ _quantifierScopeDepth = 0;
13468
+ get inQuantifierScope() {
13469
+ return this._quantifierScopeDepth > 0;
13470
+ }
13471
+ enterQuantifierScope() {
13472
+ this._quantifierScopeDepth++;
13473
+ }
13474
+ exitQuantifierScope() {
13475
+ if (this._quantifierScopeDepth > 0) this._quantifierScopeDepth--;
13476
+ }
13369
13477
  get index() {
13370
13478
  return this._index;
13371
13479
  }
@@ -14251,9 +14359,10 @@ var _Parser = class {
14251
14359
  this.skipSpace();
14252
14360
  let body = this.parseExpression();
14253
14361
  this.skipSpace();
14254
- if (!this.matchBoundary()) {
14255
- const boundary = this._boundaries[this._boundaries.length - 1].tokens;
14256
- this.removeBoundary();
14362
+ const boundary = this._boundaries[this._boundaries.length - 1]?.tokens;
14363
+ const matchedBoundary = this.matchBoundary();
14364
+ 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]);
14365
+ if (matchedBoundary && isEmptySequence(body) && sameTrigger && boundary) {
14257
14366
  this.index = bodyStart;
14258
14367
  this.skipSpace();
14259
14368
  body = this.parseExpression();
@@ -14263,6 +14372,18 @@ var _Parser = class {
14263
14372
  if (!this.atEnd) continue;
14264
14373
  return null;
14265
14374
  }
14375
+ } else if (!matchedBoundary) {
14376
+ const boundary2 = this._boundaries[this._boundaries.length - 1].tokens;
14377
+ this.removeBoundary();
14378
+ this.index = bodyStart;
14379
+ this.skipSpace();
14380
+ body = this.parseExpression();
14381
+ this.skipSpace();
14382
+ if (!this.matchAll(boundary2)) {
14383
+ this.index = start;
14384
+ if (!this.atEnd) continue;
14385
+ return null;
14386
+ }
14266
14387
  }
14267
14388
  const result = def.parse(this, body ?? "Nothing");
14268
14389
  if (result !== null) return result;
@@ -14311,12 +14432,16 @@ var _Parser = class {
14311
14432
  break;
14312
14433
  }
14313
14434
  }
14435
+ let isPredicate = false;
14314
14436
  if (fn === null) {
14315
14437
  this.index = start;
14316
14438
  fn = parseSymbol(this);
14317
14439
  if (!this.isFunctionOperator(fn)) {
14318
- this.index = start;
14319
- return null;
14440
+ if (!this.looksLikePredicate(fn)) {
14441
+ this.index = start;
14442
+ return null;
14443
+ }
14444
+ isPredicate = true;
14320
14445
  }
14321
14446
  }
14322
14447
  do {
@@ -14326,6 +14451,10 @@ var _Parser = class {
14326
14451
  } while (true);
14327
14452
  const args = this.parseArguments("enclosure", until);
14328
14453
  if (args === null) return fn;
14454
+ if (isPredicate && typeof fn === "string") {
14455
+ if (this.inQuantifierScope || fn === "D" || fn === "N")
14456
+ return ["Predicate", fn, ...args];
14457
+ }
14329
14458
  return typeof fn === "string" ? [fn, ...args] : ["Apply", fn, ...args];
14330
14459
  }
14331
14460
  parseSymbol(until) {
@@ -14714,9 +14843,24 @@ var _Parser = class {
14714
14843
  }
14715
14844
  isFunctionOperator(id) {
14716
14845
  if (id === null) return false;
14846
+ if (id === "D" || id === "N") return false;
14717
14847
  if (this.getSymbolType(id).matches("function")) return true;
14718
14848
  return false;
14719
14849
  }
14850
+ /**
14851
+ * Check if a symbol looks like a predicate in First-Order Logic.
14852
+ * A predicate is typically a single uppercase letter (P, Q, R, etc.)
14853
+ * followed by parentheses containing arguments.
14854
+ *
14855
+ * This enables automatic inference of predicates without explicit declaration,
14856
+ * so `\forall x. P(x)` works without having to declare `P` as a function.
14857
+ */
14858
+ looksLikePredicate(id) {
14859
+ if (id === null || typeof id !== "string") return false;
14860
+ if (!/^[A-Z]$/.test(id)) return false;
14861
+ this.skipSpace();
14862
+ return this.peek === "(" || this.peek === "\\left";
14863
+ }
14720
14864
  /** Return all defs of the specified kind.
14721
14865
  * The defs at the end of the dictionary have priority, since they may
14722
14866
  * override previous definitions. (For example, there is a core definition
@@ -18599,7 +18743,10 @@ function polynomialDegree(expr, variable) {
18599
18743
  if (op === "Power") {
18600
18744
  const baseDeg = polynomialDegree(expr.op1, variable);
18601
18745
  if (baseDeg < 0) return -1;
18602
- if (baseDeg === 0) return 0;
18746
+ if (baseDeg === 0) {
18747
+ if (expr.op2.has(variable)) return -1;
18748
+ return 0;
18749
+ }
18603
18750
  const exp2 = asSmallInteger(expr.op2);
18604
18751
  if (exp2 === null || exp2 < 0) return -1;
18605
18752
  return baseDeg * exp2;
@@ -21638,8 +21785,6 @@ function canonicalLimits(ops, { engine: ce }) {
21638
21785
  }
21639
21786
  if (index.operator === "Hold") index = index.op1;
21640
21787
  if (!index.symbol) index = ce.typeError("symbol", index.type, index);
21641
- if (lower.symbol !== "Nothing") lower = checkType(ce, lower, "number");
21642
- if (upper.symbol !== "Nothing") upper = checkType(ce, upper, "number");
21643
21788
  return ce._fn("Limits", [index, lower, upper]);
21644
21789
  }
21645
21790
  return null;
@@ -22518,10 +22663,6 @@ function serializeScientificNotationNumber(valString, options, expMultiple = 1)
22518
22663
  fractionalPart = options.decimalSeparator + fractionalPart;
22519
22664
  wholePart = insertWholeGroupSeparator(wholePart, options);
22520
22665
  if (!expString) return wholePart + fractionalPart;
22521
- if (!fractionalPart) {
22522
- if (wholePart === "1") return expString;
22523
- if (wholePart === "-1") return "-" + expString;
22524
- }
22525
22666
  return wholePart + fractionalPart + options.exponentProduct + expString;
22526
22667
  }
22527
22668
  function serializeAutoNotationNumber(valString, options) {
@@ -23028,7 +23169,8 @@ var OPERATORS = {
23028
23169
  Negate: [
23029
23170
  (expr, serialize) => {
23030
23171
  const base = serialize(expr.op1, 14);
23031
- if (base === "Power") return `-(${base})`;
23172
+ const op = expr.op1?.operator;
23173
+ if (op === "Power" || op === "Square") return `-(${base})`;
23032
23174
  return `-${base}`;
23033
23175
  },
23034
23176
  14
@@ -23249,11 +23391,11 @@ function bigOp(expr, op, serialize) {
23249
23391
  const b = fn.op1 ?? fn;
23250
23392
  if (b.operator === "Block") body = serialize(b.op1 ?? b);
23251
23393
  else body = serialize(b);
23252
- } else if (fn?.symbol) {
23394
+ } else if (fn) {
23253
23395
  args = [];
23254
23396
  body = serialize(fn);
23255
23397
  } else {
23256
- return "int()";
23398
+ return `${op}()`;
23257
23399
  }
23258
23400
  let result = op;
23259
23401
  for (const limit2 of limits) {
@@ -25369,7 +25511,7 @@ var ARITHMETIC_LIBRARY = [
25369
25511
  description: "Rounds a number up to the next largest integer",
25370
25512
  complexity: 1250,
25371
25513
  broadcastable: true,
25372
- signature: "(real) -> integer",
25514
+ signature: "(number) -> integer",
25373
25515
  sgn: ([x]) => {
25374
25516
  if (x.isLessEqual(-1)) return "negative";
25375
25517
  if (x.isPositive) return "positive";
@@ -26411,38 +26553,29 @@ var ARITHMETIC_LIBRARY = [
26411
26553
  signature: "((number+) -> number, (tuple<integer>|tuple<integer, integer>)+) -> number",
26412
26554
  canonical: ([body, ...bounds], { scope }) => canonicalBigop("Product", body, bounds, scope),
26413
26555
  evaluate: (ops, options) => {
26414
- const fn = (acc, x) => {
26415
- x = x.evaluate(options);
26416
- return x.isNumberLiteral ? acc.mul(x.numericValue) : null;
26417
- };
26418
26556
  const result = run(
26419
26557
  reduceBigOp(
26420
26558
  ops[0],
26421
26559
  ops.slice(1),
26422
- fn,
26423
- options.engine._numericValue(1)
26560
+ (acc, x) => acc.mul(x.evaluate(options)),
26561
+ options.engine.One
26424
26562
  ),
26425
26563
  options.engine._timeRemaining
26426
26564
  );
26427
- return options.engine.number(result ?? NaN);
26565
+ return result?.evaluate() ?? options.engine.NaN;
26428
26566
  },
26429
26567
  evaluateAsync: async (ops, options) => {
26430
- const fn = (acc, x) => {
26431
- x = x.evaluate(options);
26432
- if (!x.isNumberLiteral) return null;
26433
- return acc.mul(x.numericValue);
26434
- };
26435
26568
  const result = await runAsync(
26436
26569
  reduceBigOp(
26437
26570
  ops[0],
26438
26571
  ops.slice(1),
26439
- fn,
26440
- options.engine._numericValue(1)
26572
+ (acc, x) => acc.mul(x.evaluate(options)),
26573
+ options.engine.One
26441
26574
  ),
26442
26575
  options.engine._timeRemaining,
26443
26576
  options.signal
26444
26577
  );
26445
- return options.engine.number(result ?? NaN);
26578
+ return result?.evaluate() ?? options.engine.NaN;
26446
26579
  }
26447
26580
  },
26448
26581
  Sum: {
@@ -26454,36 +26587,31 @@ var ARITHMETIC_LIBRARY = [
26454
26587
  lazy: true,
26455
26588
  signature: "((number) -> number, bounds:tuple+) -> number",
26456
26589
  canonical: ([body, ...bounds], { scope }) => canonicalBigop("Sum", body, bounds, scope),
26457
- evaluate: ([fn, ...indexes], { engine }) => engine.number(
26458
- run(
26590
+ evaluate: ([body, ...indexes], { engine }) => {
26591
+ const result = run(
26459
26592
  reduceBigOp(
26460
- fn,
26593
+ body,
26461
26594
  indexes,
26462
- (acc, x) => {
26463
- x = x.evaluate();
26464
- return x.isNumberLiteral ? acc.add(x.numericValue) : null;
26465
- },
26466
- engine._numericValue(0)
26595
+ (acc, x) => acc.add(x.evaluate()),
26596
+ engine.Zero
26467
26597
  ),
26468
26598
  engine._timeRemaining
26469
- )
26470
- ),
26471
- evaluateAsync: async (xs, { engine, signal }) => engine.number(
26472
- await runAsync(
26599
+ );
26600
+ return result?.evaluate() ?? engine.NaN;
26601
+ },
26602
+ evaluateAsync: async (xs, { engine, signal }) => {
26603
+ const result = await runAsync(
26473
26604
  reduceBigOp(
26474
26605
  xs[0],
26475
26606
  xs.slice(1),
26476
- (acc, x) => {
26477
- x = x.evaluate();
26478
- if (!x.isNumberLiteral) return null;
26479
- return acc.add(x.numericValue);
26480
- },
26481
- engine._numericValue(0)
26607
+ (acc, x) => acc.add(x.evaluate()),
26608
+ engine.Zero
26482
26609
  ),
26483
26610
  engine._timeRemaining,
26484
26611
  signal
26485
- )
26486
- )
26612
+ );
26613
+ return result?.evaluate() ?? engine.NaN;
26614
+ }
26487
26615
  }
26488
26616
  }
26489
26617
  ];
@@ -26673,17 +26801,12 @@ var DERIVATIVES_TABLE = {
26673
26801
  Ln: ["Divide", 1, "_"],
26674
26802
  Log: ["Power", ["Multiply", "_", ["Ln", "10"]], -1],
26675
26803
  Sqrt: ["Multiply", ["Power", "_", ["Negate", "Half"]], "Half"],
26676
- Abs: [
26677
- "Which",
26678
- ["Equal", "_", 0],
26679
- NaN,
26680
- ["Less", "_", 0],
26681
- -1,
26682
- ["Greater", "_", 0],
26683
- 1,
26684
- "True",
26685
- ["D", ["Abs", "_"], "_"]
26686
- ],
26804
+ // d/dx |x| = x/|x| = sign(x) for x ≠ 0 (undefined at x = 0)
26805
+ Abs: ["Sign", "_"],
26806
+ // Step functions: derivative is 0 almost everywhere (undefined at discontinuities)
26807
+ Floor: 0,
26808
+ Ceil: 0,
26809
+ Round: 0,
26687
26810
  // https://proofwiki.org/wiki/Derivative_of_Error_Function
26688
26811
  Erf: [
26689
26812
  "Multiply",
@@ -26692,51 +26815,36 @@ var DERIVATIVES_TABLE = {
26692
26815
  ],
26693
26816
  // https://proofwiki.org/wiki/Derivative_of_Gamma_Function
26694
26817
  // https://en.wikipedia.org/wiki/Gamma_function
26818
+ // d/dx Γ(x) = Γ(x)·ψ(x) where ψ is the digamma function
26695
26819
  Gamma: ["Multiply", ["Gamma", "_"], ["Digamma", "_"]],
26696
- Digamma: [
26697
- "Add",
26698
- ["Multiply", ["Digamma", "_"], ["Gamma", "_"]],
26699
- ["Multiply", ["Power", "_", -1], ["Gamma", "_"]]
26700
- ],
26701
- Zeta: ["Multiply", ["Multiply", -1, ["Zeta", "_"]], ["Digamma", "_"]],
26702
- PolyGamma: [
26703
- "Add",
26704
- ["Multiply", ["PolyGamma", "_"], ["Gamma", "_"]],
26705
- ["Multiply", ["Power", "_", -1], ["Gamma", "_"]]
26706
- ],
26707
- Beta: [
26708
- "Multiply",
26709
- [
26710
- "Add",
26711
- ["Multiply", ["Beta", "_"], ["Digamma", "_"]],
26712
- ["Multiply", ["Power", "_", -1], ["Beta", "_"]]
26713
- ],
26714
- ["Beta", "_"]
26715
- ],
26820
+ // d/dx erfc(x) = -d/dx erf(x) = -2/√π * e^(-x²)
26716
26821
  Erfc: [
26717
- "Multiply",
26718
- ["Negate", ["Erfc", "_"]],
26719
- ["Exp", ["Negate", ["Power", "_", 2]]],
26720
- ["Power", "_", -1]
26822
+ "Negate",
26823
+ ["Multiply", ["Divide", 2, ["Sqrt", "Pi"]], ["Exp", ["Negate", ["Square", "_"]]]]
26721
26824
  ],
26722
- LambertW: [
26825
+ // d/dx ln(Γ(x)) = ψ(x) (digamma function)
26826
+ LogGamma: ["Digamma", "_"],
26827
+ // Note: LambertW derivative d/dx W(x) = W(x)/(x·(1+W(x))) is mathematically correct
26828
+ // but omitted because LambertW lacks a type signature, causing type errors.
26829
+ //
26830
+ // d/dx S(x) = sin(πx²/2) where S is the Fresnel sine integral
26831
+ FresnelS: ["Sin", ["Multiply", ["Divide", "Pi", 2], ["Square", "_"]]],
26832
+ // d/dx C(x) = cos(πx²/2) where C is the Fresnel cosine integral
26833
+ FresnelC: ["Cos", ["Multiply", ["Divide", "Pi", 2], ["Square", "_"]]],
26834
+ // d/dx erfi(x) = (2/√π)·e^(x²) where erfi is the imaginary error function
26835
+ Erfi: [
26723
26836
  "Multiply",
26724
- ["Power", "_", -1],
26725
- [
26726
- "Multiply",
26727
- ["Add", "_", ["LambertW", "_"]],
26728
- ["Add", ["LambertW", "_"], 1]
26729
- ]
26730
- ],
26731
- AiryAi: ["Multiply", ["AiryAi", "_"], ["AiryBi", "_"]],
26732
- AiryBi: ["Multiply", ["AiryAi", "_"], ["AiryBi", "_"]],
26733
- BesselJ: ["Multiply", ["BesselJ", "_"], ["BesselY", "_"]],
26734
- BesselY: ["Multiply", ["BesselJ", "_"], ["BesselY", "_"]],
26735
- BesselI: ["Multiply", ["BesselI", "_"], ["BesselK", "_"]],
26736
- BesselK: ["Multiply", ["BesselI", "_"], ["BesselK", "_"]],
26737
- FresnelS: ["Multiply", ["FresnelS", "_"], ["FresnelC", "_"]],
26738
- FresnelC: ["Multiply", ["FresnelS", "_"], ["FresnelC", "_"]],
26739
- Erfi: ["Multiply", ["Erfi", "_"], ["Erf", "_"]]
26837
+ ["Divide", 2, ["Sqrt", "Pi"]],
26838
+ ["Exp", ["Square", "_"]]
26839
+ ]
26840
+ // Note: Bessel functions (BesselJ, BesselY, BesselI, BesselK) and Airy functions
26841
+ // (AiryAi, AiryBi) have been omitted because their derivatives involve functions
26842
+ // of different orders or related derivative functions that are not in the standard
26843
+ // function set. For example, d/dx J_n(x) = (J_{n-1}(x) - J_{n+1}(x))/2.
26844
+ //
26845
+ // Similarly, Zeta, Digamma, PolyGamma, and Beta derivatives are omitted because
26846
+ // they either don't have simple closed forms or involve additional functions not
26847
+ // in the standard set (trigamma function, etc.).
26740
26848
  };
26741
26849
  function derivative(fn, order2) {
26742
26850
  if (order2 === 0) return fn;
@@ -26789,6 +26897,15 @@ function differentiate(expr, v) {
26789
26897
  if (terms.some((term) => term === void 0)) return void 0;
26790
26898
  return simplifyDerivative(add3(...terms));
26791
26899
  }
26900
+ if (expr.operator === "Root") {
26901
+ const [base, n] = expr.ops;
26902
+ if (!base.has(v)) return ce.Zero;
26903
+ const exponent = ce.One.div(n);
26904
+ const basePrime = differentiate(base, v) ?? ce._fn("D", [base, ce.symbol(v)]);
26905
+ const newExponent = exponent.sub(ce.One);
26906
+ const power = ce.function("Power", [base, newExponent], { structural: true });
26907
+ return simplifyDerivative(exponent.mul(power).mul(basePrime));
26908
+ }
26792
26909
  if (expr.operator === "Power") {
26793
26910
  const [base, exponent] = expr.ops;
26794
26911
  const baseHasV = base.has(v);
@@ -26825,7 +26942,7 @@ function differentiate(expr, v) {
26825
26942
  );
26826
26943
  }
26827
26944
  const h = DERIVATIVES_TABLE[expr.operator];
26828
- if (!h) {
26945
+ if (h === void 0) {
26829
26946
  if (expr.nops > 1) return void 0;
26830
26947
  const fPrime = ce._fn("Derivative", [ce.symbol(expr.operator), ce.One]);
26831
26948
  if (!fPrime.isValid) return void 0;
@@ -27406,6 +27523,14 @@ var UNIVARIATE_ROOTS = [
27406
27523
  // Handle ax = 0
27407
27524
  condition: filter
27408
27525
  },
27526
+ // -ax + b = 0 => x = b/a
27527
+ // This handles cases where the coefficient is negative and represented as Negate(Multiply(...))
27528
+ {
27529
+ match: ["Add", ["Negate", ["Multiply", "_x", "__a"]], "__b"],
27530
+ replace: ["Divide", "__b", "__a"],
27531
+ useVariations: true,
27532
+ condition: filter
27533
+ },
27409
27534
  // ax^n + b = 0
27410
27535
  {
27411
27536
  match: ["Add", ["Multiply", "_a", ["Power", "_x", "_n"]], "__b"],
@@ -27541,27 +27666,140 @@ var UNIVARIATE_ROOTS = [
27541
27666
  replace: ["Divide", ["Negate", ["Add", "__b", "__c"], "__a"]],
27542
27667
  condition: filter
27543
27668
  },
27544
- // ax + c\sqrt{dx + f} + g = 0
27545
- // plus
27669
+ //
27670
+ // Square root equations: ax + b√x + c = 0
27671
+ // Using substitution u = √x, this becomes au² + bu + c = 0
27672
+ // Solving: u = (-b ± √(b² - 4ac)) / 2a
27673
+ // Then x = u² = ((-b ± √(b² - 4ac)) / 2a)²
27674
+ //
27675
+ // ax + b√x + c = 0 (plus root)
27676
+ {
27677
+ match: ["Add", ["Multiply", "_x", "__a"], ["Multiply", "__b", ["Sqrt", "_x"]], "___c"],
27678
+ replace: [
27679
+ "Power",
27680
+ [
27681
+ "Divide",
27682
+ ["Add", ["Negate", "__b"], ["Sqrt", ["Subtract", ["Square", "__b"], ["Multiply", 4, "__a", "___c"]]]],
27683
+ ["Multiply", 2, "__a"]
27684
+ ],
27685
+ 2
27686
+ ],
27687
+ useVariations: true,
27688
+ condition: filter
27689
+ },
27690
+ // ax + b√x + c = 0 (minus root)
27691
+ {
27692
+ match: ["Add", ["Multiply", "_x", "__a"], ["Multiply", "__b", ["Sqrt", "_x"]], "___c"],
27693
+ replace: [
27694
+ "Power",
27695
+ [
27696
+ "Divide",
27697
+ ["Subtract", ["Negate", "__b"], ["Sqrt", ["Subtract", ["Square", "__b"], ["Multiply", 4, "__a", "___c"]]]],
27698
+ ["Multiply", 2, "__a"]
27699
+ ],
27700
+ 2
27701
+ ],
27702
+ useVariations: true,
27703
+ condition: filter
27704
+ },
27705
+ // Handle negated coefficient: ax - b√x + c = 0
27706
+ // This handles the Negate(Multiply(...)) pattern
27707
+ {
27708
+ match: ["Add", ["Multiply", "_x", "__a"], ["Negate", ["Multiply", "__b", ["Sqrt", "_x"]]], "___c"],
27709
+ replace: [
27710
+ "Power",
27711
+ [
27712
+ "Divide",
27713
+ ["Add", "__b", ["Sqrt", ["Add", ["Square", "__b"], ["Multiply", 4, "__a", "___c"]]]],
27714
+ ["Multiply", 2, "__a"]
27715
+ ],
27716
+ 2
27717
+ ],
27718
+ useVariations: true,
27719
+ condition: filter
27720
+ },
27721
+ // ax - b√x + c = 0 (minus root)
27722
+ {
27723
+ match: ["Add", ["Multiply", "_x", "__a"], ["Negate", ["Multiply", "__b", ["Sqrt", "_x"]]], "___c"],
27724
+ replace: [
27725
+ "Power",
27726
+ [
27727
+ "Divide",
27728
+ ["Subtract", "__b", ["Sqrt", ["Add", ["Square", "__b"], ["Multiply", 4, "__a", "___c"]]]],
27729
+ ["Multiply", 2, "__a"]
27730
+ ],
27731
+ 2
27732
+ ],
27733
+ useVariations: true,
27734
+ condition: filter
27735
+ },
27736
+ //
27737
+ // Additional solve rules
27738
+ //
27739
+ // a√x + b = 0 => x = (b/a)² (only valid when -b/a ≥ 0)
27740
+ {
27741
+ match: ["Add", ["Multiply", "__a", ["Sqrt", "_x"]], "__b"],
27742
+ replace: ["Square", ["Divide", ["Negate", "__b"], "__a"]],
27743
+ useVariations: true,
27744
+ condition: (sub2) => {
27745
+ if (!filter(sub2)) return false;
27746
+ const a = sub2.__a;
27747
+ const b = sub2.__b;
27748
+ if (!a || !b) return false;
27749
+ const ratio = b.div(a);
27750
+ return ratio.isNonPositive ?? true;
27751
+ }
27752
+ },
27753
+ // a·ln(x) + b = 0 => x = e^(-b/a)
27546
27754
  {
27547
- match: "ax + \\mathrm{__b} \\sqrt{cx + \\mathrm{__d}} + \\mathrm{__g}",
27548
- 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}",
27755
+ match: ["Add", ["Multiply", "__a", ["Ln", "_x"]], "__b"],
27756
+ replace: ["Exp", ["Divide", ["Negate", "__b"], "__a"]],
27549
27757
  useVariations: true,
27550
27758
  condition: filter
27551
27759
  },
27552
- // minus
27760
+ // ln(x) + b = 0 => x = e^(-b)
27553
27761
  {
27554
- match: "ax + \\mathrm{__b} \\sqrt{cx + \\mathrm{__d}} + \\mathrm{__g}",
27555
- 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}",
27762
+ match: ["Add", ["Ln", "_x"], "__b"],
27763
+ replace: ["Exp", ["Negate", "__b"]],
27556
27764
  useVariations: true,
27557
27765
  condition: filter
27558
27766
  }
27559
27767
  ];
27768
+ function clearDenominators(expr, variable) {
27769
+ if (expr.operator !== "Add") return expr;
27770
+ const ops = expr.ops;
27771
+ if (!ops || ops.length === 0) return expr;
27772
+ const denominators = ops.map((op) => op.denominator).filter((d) => !d.is(1));
27773
+ if (denominators.length === 0) return expr;
27774
+ const lcmFactors = [];
27775
+ for (const denom of denominators) {
27776
+ let isDuplicate = false;
27777
+ for (const existing of lcmFactors) {
27778
+ if (denom.isSame(existing)) {
27779
+ isDuplicate = true;
27780
+ break;
27781
+ }
27782
+ if (denom.symbol && existing.symbol && denom.symbol === existing.symbol) {
27783
+ isDuplicate = true;
27784
+ break;
27785
+ }
27786
+ }
27787
+ if (!isDuplicate) {
27788
+ lcmFactors.push(denom);
27789
+ }
27790
+ }
27791
+ let lcm4 = lcmFactors[0];
27792
+ for (let i = 1; i < lcmFactors.length; i++) {
27793
+ lcm4 = lcm4.mul(lcmFactors[i]);
27794
+ }
27795
+ return expr.mul(lcm4).simplify();
27796
+ }
27560
27797
  function findUnivariateRoots(expr, x) {
27561
27798
  const ce = expr.engine;
27562
27799
  if (expr.operator === "Equal")
27563
27800
  expr = expr.op1.expand().sub(expr.op2.expand()).simplify();
27564
27801
  else expr = expr.expand().simplify();
27802
+ expr = clearDenominators(expr);
27565
27803
  const rules = ce.getRuleSet("solve-univariate");
27566
27804
  let exprs = [expr.subs({ [x]: "_x" }, { canonical: false })];
27567
27805
  ce.pushScope();
@@ -28423,40 +28661,46 @@ var INTEGRATION_RULES = [
28423
28661
  ],
28424
28662
  condition: filter2
28425
28663
  },
28426
- // \arctan(ax + b) -> \frac{1}{a} \ln(\sec(ax + b) + \tan(ax + b))
28664
+ // \arctan(ax + b) -> (1/a) * [(ax+b)*arctan(ax+b) - (1/2)*ln(1+(ax+b)^2)]
28427
28665
  {
28428
28666
  match: ["Arctan", ["Add", ["Multiply", "_a", "_x"], "__b"]],
28429
28667
  replace: [
28430
28668
  "Divide",
28431
28669
  [
28432
- "Ln",
28670
+ "Subtract",
28433
28671
  [
28434
- "Add",
28435
- ["Sec", ["Add", ["Multiply", "_a", "_x"], "__b"]],
28436
- ["Tan", ["Add", ["Multiply", "_a", "_x"], "__b"]]
28672
+ "Multiply",
28673
+ ["Add", ["Multiply", "_a", "_x"], "__b"],
28674
+ ["Arctan", ["Add", ["Multiply", "_a", "_x"], "__b"]]
28675
+ ],
28676
+ [
28677
+ "Multiply",
28678
+ ["Rational", 1, 2],
28679
+ ["Ln", ["Add", 1, ["Power", ["Add", ["Multiply", "_a", "_x"], "__b"], 2]]]
28437
28680
  ]
28438
28681
  ],
28439
28682
  "_a"
28440
28683
  ],
28441
28684
  condition: filter2
28442
28685
  },
28443
- // \arccos(ax + b) -> \frac{1}{a} \ln(ax + b + \sqrt{(ax + b)^2 - 1})
28686
+ // \arccos(ax + b) -> (1/a) * [(ax+b)*arccos(ax+b) - sqrt(1-(ax+b)^2)]
28444
28687
  {
28445
28688
  match: ["Arccos", ["Add", ["Multiply", "_a", "_x"], "__b"]],
28446
28689
  replace: [
28447
28690
  "Divide",
28448
28691
  [
28449
- "Ln",
28692
+ "Subtract",
28450
28693
  [
28451
- "Add",
28694
+ "Multiply",
28452
28695
  ["Add", ["Multiply", "_a", "_x"], "__b"],
28696
+ ["Arccos", ["Add", ["Multiply", "_a", "_x"], "__b"]]
28697
+ ],
28698
+ [
28699
+ "Sqrt",
28453
28700
  [
28454
- "Sqrt",
28455
- [
28456
- "Subtract",
28457
- ["Power", ["Add", ["Multiply", "_a", "_x"], "__b"], 2],
28458
- 1
28459
- ]
28701
+ "Subtract",
28702
+ 1,
28703
+ ["Power", ["Add", ["Multiply", "_a", "_x"], "__b"], 2]
28460
28704
  ]
28461
28705
  ]
28462
28706
  ],
@@ -28464,23 +28708,24 @@ var INTEGRATION_RULES = [
28464
28708
  ],
28465
28709
  condition: filter2
28466
28710
  },
28467
- // \arcsin(ax + b) -> \frac{1}{a} \ln(ax + b + \sqrt{1 - (ax + b)^2})
28711
+ // \arcsin(ax + b) -> (1/a) * [(ax+b)*arcsin(ax+b) + sqrt(1-(ax+b)^2)]
28468
28712
  {
28469
28713
  match: ["Arcsin", ["Add", ["Multiply", "_a", "_x"], "__b"]],
28470
28714
  replace: [
28471
28715
  "Divide",
28472
28716
  [
28473
- "Ln",
28717
+ "Add",
28474
28718
  [
28475
- "Add",
28719
+ "Multiply",
28476
28720
  ["Add", ["Multiply", "_a", "_x"], "__b"],
28721
+ ["Arcsin", ["Add", ["Multiply", "_a", "_x"], "__b"]]
28722
+ ],
28723
+ [
28724
+ "Sqrt",
28477
28725
  [
28478
- "Sqrt",
28479
- [
28480
- "Subtract",
28481
- 1,
28482
- ["Power", ["Add", ["Multiply", "_a", "_x"], "__b"], 2]
28483
- ]
28726
+ "Subtract",
28727
+ 1,
28728
+ ["Power", ["Add", ["Multiply", "_a", "_x"], "__b"], 2]
28484
28729
  ]
28485
28730
  ]
28486
28731
  ],
@@ -29294,6 +29539,8 @@ var CALCULUS_LIBRARY = [
29294
29539
  }
29295
29540
  f = f?.canonical;
29296
29541
  if (f?.operator === "D") return f;
29542
+ if (f?.operator === "Apply" && f.op1?.operator === "Derivative")
29543
+ return f;
29297
29544
  if (f && hasSymbolicTranscendental(f)) return f;
29298
29545
  return f?.evaluate();
29299
29546
  }
@@ -29420,7 +29667,7 @@ var CALCULUS_LIBRARY = [
29420
29667
  complexity: 5e3,
29421
29668
  broadcastable: false,
29422
29669
  lazy: true,
29423
- signature: "(index:symbol, lower:number, upper:number) -> tuple",
29670
+ signature: "(index:symbol, lower:value, upper:value) -> tuple",
29424
29671
  canonical: (ops, { engine }) => canonicalLimits(ops, { engine }) ?? null
29425
29672
  }
29426
29673
  },
@@ -30933,12 +31180,22 @@ var CORE_LIBRARY = [
30933
31180
  }
30934
31181
  if (op1.isIndexedCollection) return ce._fn("At", [op1, op2.canonical]);
30935
31182
  if (op1.symbol) {
30936
- const sub2 = op2.string ?? op2.symbol ?? asSmallInteger(op2)?.toString();
30937
- if (sub2) return ce.symbol(op1.symbol + "_" + sub2);
31183
+ const sub3 = op2.string ?? op2.symbol ?? asSmallInteger(op2)?.toString();
31184
+ if (sub3) return ce.symbol(op1.symbol + "_" + sub3);
31185
+ if (op2.operator === "InvisibleOperator" && op2.ops) {
31186
+ const parts = op2.ops.map(
31187
+ (x) => x.symbol ?? asSmallInteger(x)?.toString()
31188
+ );
31189
+ if (parts.every((p) => p !== void 0 && p !== null)) {
31190
+ return ce.symbol(op1.symbol + "_" + parts.join(""));
31191
+ }
31192
+ }
30938
31193
  }
30939
31194
  if (op2.operator === "Sequence")
30940
31195
  ce._fn("Subscript", [op1, ce._fn("List", op2.ops)]);
30941
- return ce._fn("Subscript", [op1, op2]);
31196
+ let sub2 = op2;
31197
+ if (op2.operator === "Delimiter" && op2.op1) sub2 = op2.op1.canonical;
31198
+ return ce._fn("Subscript", [op1, sub2]);
30942
31199
  }
30943
31200
  },
30944
31201
  Symbol: {
@@ -32668,6 +32925,539 @@ function canonicalMatrix(ops, { engine: ce }) {
32668
32925
  return ce._fn(operator2, [body]);
32669
32926
  }
32670
32927
 
32928
+ // src/compute-engine/library/logic-utils.ts
32929
+ function evaluateAnd(args, { engine: ce }) {
32930
+ if (args.length === 0) return ce.True;
32931
+ const ops = [];
32932
+ for (const arg of args) {
32933
+ if (arg.symbol === "False") return ce.False;
32934
+ if (arg.symbol !== "True") {
32935
+ let duplicate = false;
32936
+ for (const x of ops) {
32937
+ if (x.isSame(arg)) {
32938
+ duplicate = true;
32939
+ } else if (arg.operator === "Not" && arg.op1.isSame(x) || x.operator === "Not" && x.op1.isSame(arg)) {
32940
+ return ce.False;
32941
+ }
32942
+ }
32943
+ if (!duplicate) ops.push(arg);
32944
+ }
32945
+ }
32946
+ if (ops.length === 0) return ce.True;
32947
+ if (ops.length === 1) return ops[0];
32948
+ return ce._fn("And", ops);
32949
+ }
32950
+ function evaluateOr(args, { engine: ce }) {
32951
+ if (args.length === 0) return ce.True;
32952
+ const ops = [];
32953
+ for (const arg of args) {
32954
+ if (arg.symbol === "True") return ce.True;
32955
+ if (arg.symbol !== "False") {
32956
+ let duplicate = false;
32957
+ for (const x of ops) {
32958
+ if (x.isSame(arg)) {
32959
+ duplicate = true;
32960
+ } else if (arg.operator === "Not" && arg.op1.isSame(x) || x.operator === "Not" && x.op1.isSame(arg)) {
32961
+ return ce.True;
32962
+ }
32963
+ }
32964
+ if (!duplicate) ops.push(arg);
32965
+ }
32966
+ }
32967
+ if (ops.length === 0) return ce.False;
32968
+ if (ops.length === 1) return ops[0];
32969
+ return ce._fn("Or", ops);
32970
+ }
32971
+ function evaluateNot(args, { engine: ce }) {
32972
+ const op1 = args[0]?.symbol;
32973
+ if (op1 === "True") return ce.False;
32974
+ if (op1 === "False") return ce.True;
32975
+ return void 0;
32976
+ }
32977
+ function evaluateEquivalent(args, { engine: ce }) {
32978
+ const lhs = args[0].symbol;
32979
+ const rhs = args[1].symbol;
32980
+ if (lhs === "True" && rhs === "True" || lhs === "False" && rhs === "False")
32981
+ return ce.True;
32982
+ if (lhs === "True" && rhs === "False" || lhs === "False" && rhs === "True")
32983
+ return ce.False;
32984
+ return void 0;
32985
+ }
32986
+ function evaluateImplies(args, { engine: ce }) {
32987
+ const lhs = args[0].symbol;
32988
+ const rhs = args[1].symbol;
32989
+ if (lhs === "True" && rhs === "True" || lhs === "False" && rhs === "False" || lhs === "False" && rhs === "True")
32990
+ return ce.True;
32991
+ if (lhs === "True" && rhs === "False") return ce.False;
32992
+ return void 0;
32993
+ }
32994
+ function evaluateXor(args, { engine: ce }) {
32995
+ if (args.length === 0) return ce.False;
32996
+ let trueCount = 0;
32997
+ const unknowns = [];
32998
+ for (const arg of args) {
32999
+ if (arg.symbol === "True") {
33000
+ trueCount++;
33001
+ } else if (arg.symbol === "False") {
33002
+ } else {
33003
+ unknowns.push(arg);
33004
+ }
33005
+ }
33006
+ if (unknowns.length === 0) {
33007
+ return trueCount % 2 === 1 ? ce.True : ce.False;
33008
+ }
33009
+ if (unknowns.length === 1 && trueCount % 2 === 1) {
33010
+ return ce._fn("Not", [unknowns[0]]);
33011
+ }
33012
+ if (unknowns.length === 1 && trueCount % 2 === 0) {
33013
+ return unknowns[0];
33014
+ }
33015
+ return void 0;
33016
+ }
33017
+ function evaluateNand(args, { engine: ce }) {
33018
+ if (args.length === 0) return ce.False;
33019
+ for (const arg of args) {
33020
+ if (arg.symbol === "False") return ce.True;
33021
+ }
33022
+ let allTrue = true;
33023
+ for (const arg of args) {
33024
+ if (arg.symbol !== "True") {
33025
+ allTrue = false;
33026
+ break;
33027
+ }
33028
+ }
33029
+ if (allTrue) return ce.False;
33030
+ return void 0;
33031
+ }
33032
+ function evaluateNor(args, { engine: ce }) {
33033
+ if (args.length === 0) return ce.True;
33034
+ for (const arg of args) {
33035
+ if (arg.symbol === "True") return ce.False;
33036
+ }
33037
+ let allFalse = true;
33038
+ for (const arg of args) {
33039
+ if (arg.symbol !== "False") {
33040
+ allFalse = false;
33041
+ break;
33042
+ }
33043
+ }
33044
+ if (allFalse) return ce.True;
33045
+ return void 0;
33046
+ }
33047
+ function toNNF(expr, ce) {
33048
+ const op = expr.operator;
33049
+ if (!op) return expr;
33050
+ if (expr.symbol === "True" || expr.symbol === "False") return expr;
33051
+ if (op === "Not") {
33052
+ const inner = expr.op1;
33053
+ if (!inner) return expr;
33054
+ const innerOp = inner.operator;
33055
+ if (innerOp === "Not") {
33056
+ return toNNF(inner.op1, ce);
33057
+ }
33058
+ if (innerOp === "And") {
33059
+ const negatedOps = inner.ops.map((x) => toNNF(ce._fn("Not", [x]), ce));
33060
+ return ce._fn("Or", negatedOps);
33061
+ }
33062
+ if (innerOp === "Or") {
33063
+ const negatedOps = inner.ops.map((x) => toNNF(ce._fn("Not", [x]), ce));
33064
+ return ce._fn("And", negatedOps);
33065
+ }
33066
+ if (inner.symbol === "True") return ce.False;
33067
+ if (inner.symbol === "False") return ce.True;
33068
+ if (innerOp === "Implies") {
33069
+ const a = inner.op1;
33070
+ const b = inner.op2;
33071
+ return toNNF(ce._fn("And", [a, ce._fn("Not", [b])]), ce);
33072
+ }
33073
+ if (innerOp === "Equivalent") {
33074
+ const a = inner.op1;
33075
+ const b = inner.op2;
33076
+ return toNNF(
33077
+ ce._fn("Or", [
33078
+ ce._fn("And", [a, ce._fn("Not", [b])]),
33079
+ ce._fn("And", [ce._fn("Not", [a]), b])
33080
+ ]),
33081
+ ce
33082
+ );
33083
+ }
33084
+ if (innerOp === "Xor") {
33085
+ const ops = inner.ops;
33086
+ if (ops.length === 2) {
33087
+ const a = ops[0];
33088
+ const b = ops[1];
33089
+ return toNNF(
33090
+ ce._fn("Or", [
33091
+ ce._fn("And", [a, b]),
33092
+ ce._fn("And", [ce._fn("Not", [a]), ce._fn("Not", [b])])
33093
+ ]),
33094
+ ce
33095
+ );
33096
+ }
33097
+ return toNNF(ce._fn("Not", [toNNF(inner, ce)]), ce);
33098
+ }
33099
+ if (innerOp === "Nand") {
33100
+ return toNNF(ce._fn("And", inner.ops), ce);
33101
+ }
33102
+ if (innerOp === "Nor") {
33103
+ return toNNF(ce._fn("Or", inner.ops), ce);
33104
+ }
33105
+ return expr;
33106
+ }
33107
+ if (op === "Implies") {
33108
+ const a = expr.op1;
33109
+ const b = expr.op2;
33110
+ return toNNF(ce._fn("Or", [ce._fn("Not", [a]), b]), ce);
33111
+ }
33112
+ if (op === "Equivalent") {
33113
+ const a = expr.op1;
33114
+ const b = expr.op2;
33115
+ return toNNF(
33116
+ ce._fn("And", [
33117
+ ce._fn("Or", [ce._fn("Not", [a]), b]),
33118
+ ce._fn("Or", [ce._fn("Not", [b]), a])
33119
+ ]),
33120
+ ce
33121
+ );
33122
+ }
33123
+ if (op === "Xor") {
33124
+ const ops = expr.ops;
33125
+ if (ops.length === 2) {
33126
+ const a = ops[0];
33127
+ const b = ops[1];
33128
+ return toNNF(
33129
+ ce._fn("And", [
33130
+ ce._fn("Or", [a, b]),
33131
+ ce._fn("Or", [ce._fn("Not", [a]), ce._fn("Not", [b])])
33132
+ ]),
33133
+ ce
33134
+ );
33135
+ }
33136
+ if (ops.length > 2) {
33137
+ const first = ce._fn("Xor", [ops[0], ops[1]]);
33138
+ const rest = ops.slice(2);
33139
+ return toNNF(ce._fn("Xor", [first, ...rest]), ce);
33140
+ }
33141
+ if (ops.length === 1) return toNNF(ops[0], ce);
33142
+ return ce.False;
33143
+ }
33144
+ if (op === "Nand") {
33145
+ const ops = expr.ops;
33146
+ return toNNF(ce._fn("Not", [ce._fn("And", ops)]), ce);
33147
+ }
33148
+ if (op === "Nor") {
33149
+ const ops = expr.ops;
33150
+ return toNNF(ce._fn("Not", [ce._fn("Or", ops)]), ce);
33151
+ }
33152
+ if (op === "And" || op === "Or") {
33153
+ const nnfOps = expr.ops.map((x) => toNNF(x, ce));
33154
+ return ce._fn(op, nnfOps);
33155
+ }
33156
+ return expr;
33157
+ }
33158
+ function distributeOrOverAnd(expr, ce) {
33159
+ const op = expr.operator;
33160
+ if (op !== "Or") {
33161
+ if (op === "And") {
33162
+ return ce._fn(
33163
+ "And",
33164
+ expr.ops.map((x) => distributeOrOverAnd(x, ce))
33165
+ );
33166
+ }
33167
+ return expr;
33168
+ }
33169
+ const orOperands = [];
33170
+ for (const operand2 of expr.ops) {
33171
+ if (operand2.operator === "Or") {
33172
+ orOperands.push(...operand2.ops);
33173
+ } else {
33174
+ orOperands.push(operand2);
33175
+ }
33176
+ }
33177
+ const andIndex = orOperands.findIndex((x) => x.operator === "And");
33178
+ if (andIndex === -1) {
33179
+ return expr;
33180
+ }
33181
+ const andExpr = orOperands[andIndex];
33182
+ const otherOperands = [
33183
+ ...orOperands.slice(0, andIndex),
33184
+ ...orOperands.slice(andIndex + 1)
33185
+ ];
33186
+ const otherOr = otherOperands.length === 1 ? otherOperands[0] : ce._fn("Or", otherOperands);
33187
+ const distributed = ce._fn(
33188
+ "And",
33189
+ andExpr.ops.map((x) => ce._fn("Or", [x, otherOr]))
33190
+ );
33191
+ return distributeOrOverAnd(distributed, ce);
33192
+ }
33193
+ function toCNF(expr, ce) {
33194
+ const nnf = toNNF(expr, ce);
33195
+ const cnf = distributeOrOverAnd(nnf, ce);
33196
+ return cnf.simplify();
33197
+ }
33198
+ function distributeAndOverOr(expr, ce) {
33199
+ const op = expr.operator;
33200
+ if (op !== "And") {
33201
+ if (op === "Or") {
33202
+ return ce._fn(
33203
+ "Or",
33204
+ expr.ops.map((x) => distributeAndOverOr(x, ce))
33205
+ );
33206
+ }
33207
+ return expr;
33208
+ }
33209
+ const andOperands = [];
33210
+ for (const operand2 of expr.ops) {
33211
+ if (operand2.operator === "And") {
33212
+ andOperands.push(...operand2.ops);
33213
+ } else {
33214
+ andOperands.push(operand2);
33215
+ }
33216
+ }
33217
+ const orIndex = andOperands.findIndex((x) => x.operator === "Or");
33218
+ if (orIndex === -1) {
33219
+ return expr;
33220
+ }
33221
+ const orExpr = andOperands[orIndex];
33222
+ const otherOperands = [
33223
+ ...andOperands.slice(0, orIndex),
33224
+ ...andOperands.slice(orIndex + 1)
33225
+ ];
33226
+ const otherAnd = otherOperands.length === 1 ? otherOperands[0] : ce._fn("And", otherOperands);
33227
+ const distributed = ce._fn(
33228
+ "Or",
33229
+ orExpr.ops.map((x) => ce._fn("And", [x, otherAnd]))
33230
+ );
33231
+ return distributeAndOverOr(distributed, ce);
33232
+ }
33233
+ function toDNF(expr, ce) {
33234
+ const nnf = toNNF(expr, ce);
33235
+ const dnf = distributeAndOverOr(nnf, ce);
33236
+ return dnf.simplify();
33237
+ }
33238
+ function extractVariables(expr) {
33239
+ const variables = /* @__PURE__ */ new Set();
33240
+ function visit(e) {
33241
+ if (e.symbol === "True" || e.symbol === "False") return;
33242
+ if (e.symbol && e.operator === "Symbol") {
33243
+ variables.add(e.symbol);
33244
+ return;
33245
+ }
33246
+ if (e.ops) {
33247
+ for (const op of e.ops) {
33248
+ visit(op);
33249
+ }
33250
+ }
33251
+ }
33252
+ visit(expr);
33253
+ return Array.from(variables).sort();
33254
+ }
33255
+ function evaluateWithAssignment(expr, assignment, ce) {
33256
+ const subs = {};
33257
+ for (const [variable, value] of Object.entries(assignment)) {
33258
+ subs[variable] = value ? ce.True : ce.False;
33259
+ }
33260
+ const substituted = expr.subs(subs).canonical;
33261
+ return substituted.evaluate();
33262
+ }
33263
+ function* generateAssignments(variables) {
33264
+ const n = variables.length;
33265
+ const total = 1 << n;
33266
+ for (let i = 0; i < total; i++) {
33267
+ const assignment = {};
33268
+ for (let j = 0; j < n; j++) {
33269
+ assignment[variables[j]] = (i >> n - 1 - j & 1) === 1;
33270
+ }
33271
+ yield assignment;
33272
+ }
33273
+ }
33274
+
33275
+ // src/compute-engine/library/logic-analysis.ts
33276
+ function extractFiniteDomain(condition, ce) {
33277
+ if (condition.operator !== "Element") return null;
33278
+ const variable = condition.op1?.symbol;
33279
+ if (!variable) return null;
33280
+ const domain = condition.op2;
33281
+ if (!domain) return null;
33282
+ if (domain.operator === "Set" || domain.operator === "List") {
33283
+ const values = domain.ops;
33284
+ if (values && values.length <= 1e3) {
33285
+ return { variable, values: [...values] };
33286
+ }
33287
+ return null;
33288
+ }
33289
+ if (domain.operator === "Range") {
33290
+ const start = asSmallInteger(domain.op1);
33291
+ const end = asSmallInteger(domain.op2);
33292
+ const step = domain.ops && domain.ops.length >= 3 ? asSmallInteger(domain.op3) : 1;
33293
+ if (start !== null && end !== null && step !== null && step !== 0) {
33294
+ const count = Math.floor((end - start) / step) + 1;
33295
+ if (count > 0 && count <= 1e3) {
33296
+ const values = [];
33297
+ for (let i = start; step > 0 ? i <= end : i >= end; i += step) {
33298
+ values.push(ce.number(i));
33299
+ }
33300
+ return { variable, values };
33301
+ }
33302
+ }
33303
+ return null;
33304
+ }
33305
+ if (domain.operator === "Interval") {
33306
+ const start = asSmallInteger(domain.op1);
33307
+ const end = asSmallInteger(domain.op2);
33308
+ if (start !== null && end !== null) {
33309
+ const count = end - start + 1;
33310
+ if (count > 0 && count <= 1e3) {
33311
+ const values = [];
33312
+ for (let i = start; i <= end; i++) {
33313
+ values.push(ce.number(i));
33314
+ }
33315
+ return { variable, values };
33316
+ }
33317
+ }
33318
+ return null;
33319
+ }
33320
+ return null;
33321
+ }
33322
+ function bodyContainsVariable(expr, variable) {
33323
+ if (expr.symbol === variable) return true;
33324
+ if (expr.ops) {
33325
+ for (const op of expr.ops) {
33326
+ if (bodyContainsVariable(op, variable)) return true;
33327
+ }
33328
+ }
33329
+ return false;
33330
+ }
33331
+ function collectNestedDomains(body, ce) {
33332
+ const canonicalBody = body.canonical;
33333
+ const op = canonicalBody.operator;
33334
+ if (op !== "ForAll" && op !== "Exists") return [];
33335
+ const condition = canonicalBody.op1;
33336
+ const innerBody = canonicalBody.op2;
33337
+ if (!condition || !innerBody) return [];
33338
+ const domain = extractFiniteDomain(condition, ce);
33339
+ if (!domain) return [];
33340
+ const innerDomains = collectNestedDomains(innerBody, ce);
33341
+ return [{ variable: domain.variable, values: domain.values }, ...innerDomains];
33342
+ }
33343
+ function getInnermostBody(body) {
33344
+ const canonicalBody = body.canonical;
33345
+ const op = canonicalBody.operator;
33346
+ if (op === "ForAll" || op === "Exists") {
33347
+ const innerBody = canonicalBody.op2;
33348
+ if (innerBody) return getInnermostBody(innerBody);
33349
+ }
33350
+ return canonicalBody;
33351
+ }
33352
+ function evaluateForAllCartesian(domains, body, ce) {
33353
+ const indices = domains.map(() => 0);
33354
+ const lengths = domains.map((d) => d.values.length);
33355
+ if (lengths.some((l) => l === 0)) return ce.True;
33356
+ while (true) {
33357
+ const subs = {};
33358
+ for (let i = 0; i < domains.length; i++) {
33359
+ subs[domains[i].variable] = domains[i].values[indices[i]];
33360
+ }
33361
+ const substituted = body.subs(subs).canonical;
33362
+ const result = substituted.evaluate();
33363
+ if (result.symbol === "False") {
33364
+ return ce.False;
33365
+ }
33366
+ if (result.symbol !== "True") {
33367
+ return void 0;
33368
+ }
33369
+ let dim = domains.length - 1;
33370
+ while (dim >= 0) {
33371
+ indices[dim]++;
33372
+ if (indices[dim] < lengths[dim]) break;
33373
+ indices[dim] = 0;
33374
+ dim--;
33375
+ }
33376
+ if (dim < 0) break;
33377
+ }
33378
+ return ce.True;
33379
+ }
33380
+ function evaluateExistsCartesian(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.False;
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 === "True") {
33392
+ return ce.True;
33393
+ }
33394
+ let dim = domains.length - 1;
33395
+ while (dim >= 0) {
33396
+ indices[dim]++;
33397
+ if (indices[dim] < lengths[dim]) break;
33398
+ indices[dim] = 0;
33399
+ dim--;
33400
+ }
33401
+ if (dim < 0) break;
33402
+ }
33403
+ return ce.False;
33404
+ }
33405
+ function isSatisfiable(expr, ce) {
33406
+ const variables = extractVariables(expr);
33407
+ if (variables.length === 0) {
33408
+ const result = expr.evaluate();
33409
+ return result.symbol === "True" ? ce.True : ce.False;
33410
+ }
33411
+ if (variables.length > 20) {
33412
+ return ce._fn("IsSatisfiable", [expr]);
33413
+ }
33414
+ for (const assignment of generateAssignments(variables)) {
33415
+ const result = evaluateWithAssignment(expr, assignment, ce);
33416
+ if (result.symbol === "True") {
33417
+ return ce.True;
33418
+ }
33419
+ }
33420
+ return ce.False;
33421
+ }
33422
+ function isTautology(expr, ce) {
33423
+ const variables = extractVariables(expr);
33424
+ if (variables.length === 0) {
33425
+ const result = expr.evaluate();
33426
+ return result.symbol === "True" ? ce.True : ce.False;
33427
+ }
33428
+ if (variables.length > 20) {
33429
+ return ce._fn("IsTautology", [expr]);
33430
+ }
33431
+ for (const assignment of generateAssignments(variables)) {
33432
+ const result = evaluateWithAssignment(expr, assignment, ce);
33433
+ if (result.symbol !== "True") {
33434
+ return ce.False;
33435
+ }
33436
+ }
33437
+ return ce.True;
33438
+ }
33439
+ function generateTruthTable(expr, ce) {
33440
+ const variables = extractVariables(expr);
33441
+ if (variables.length > 10) {
33442
+ return ce._fn("TruthTable", [expr]);
33443
+ }
33444
+ const rows = [];
33445
+ const header = ce._fn("List", [
33446
+ ...variables.map((v) => ce.string(v)),
33447
+ ce.string("Result")
33448
+ ]);
33449
+ rows.push(header);
33450
+ for (const assignment of generateAssignments(variables)) {
33451
+ const result = evaluateWithAssignment(expr, assignment, ce);
33452
+ const row = ce._fn("List", [
33453
+ ...variables.map((v) => assignment[v] ? ce.True : ce.False),
33454
+ result
33455
+ ]);
33456
+ rows.push(row);
33457
+ }
33458
+ return ce._fn("List", rows);
33459
+ }
33460
+
32671
33461
  // src/compute-engine/library/logic.ts
32672
33462
  var LOGIC_LIBRARY = {
32673
33463
  True: {
@@ -32738,11 +33528,96 @@ var LOGIC_LIBRARY = {
32738
33528
  signature: "(boolean, boolean) -> boolean",
32739
33529
  evaluate: evaluateImplies
32740
33530
  },
32741
- Exists: { signature: "function", lazy: true },
32742
- NotExists: { signature: "function", lazy: true },
32743
- ExistsUnique: { signature: "function", lazy: true },
32744
- ForAll: { signature: "function", lazy: true },
32745
- NotForAll: { signature: "function", lazy: true },
33531
+ Xor: {
33532
+ description: "Exclusive or: true when an odd number of operands are true",
33533
+ wikidata: "Q498186",
33534
+ broadcastable: true,
33535
+ associative: true,
33536
+ commutative: true,
33537
+ complexity: 10200,
33538
+ signature: "(boolean+) -> boolean",
33539
+ evaluate: evaluateXor
33540
+ },
33541
+ Nand: {
33542
+ description: "Not-and: negation of conjunction",
33543
+ wikidata: "Q189550",
33544
+ broadcastable: true,
33545
+ commutative: true,
33546
+ complexity: 10200,
33547
+ signature: "(boolean+) -> boolean",
33548
+ evaluate: evaluateNand
33549
+ },
33550
+ Nor: {
33551
+ description: "Not-or: negation of disjunction",
33552
+ wikidata: "Q189561",
33553
+ broadcastable: true,
33554
+ commutative: true,
33555
+ complexity: 10200,
33556
+ signature: "(boolean+) -> boolean",
33557
+ evaluate: evaluateNor
33558
+ },
33559
+ // Quantifiers return boolean values (they are propositions)
33560
+ // They support evaluation over finite domains (e.g., ForAll with Element condition)
33561
+ // The first argument can be:
33562
+ // - a symbol (e.g., "x") for symbolic quantification
33563
+ // - an Element expression (e.g., ["Element", "x", ["Set", 1, 2, 3]]) for finite domain evaluation
33564
+ Exists: {
33565
+ signature: "(value, boolean) -> boolean",
33566
+ lazy: true,
33567
+ scoped: true,
33568
+ evaluate: evaluateExists
33569
+ },
33570
+ NotExists: {
33571
+ signature: "(value, boolean) -> boolean",
33572
+ lazy: true,
33573
+ scoped: true,
33574
+ evaluate: (args, options) => {
33575
+ const result = evaluateExists(args, options);
33576
+ if (result?.symbol === "True") return options.engine.False;
33577
+ if (result?.symbol === "False") return options.engine.True;
33578
+ return void 0;
33579
+ }
33580
+ },
33581
+ ExistsUnique: {
33582
+ signature: "(value, boolean) -> boolean",
33583
+ lazy: true,
33584
+ scoped: true,
33585
+ evaluate: evaluateExistsUnique
33586
+ },
33587
+ ForAll: {
33588
+ signature: "(value, boolean) -> boolean",
33589
+ lazy: true,
33590
+ scoped: true,
33591
+ evaluate: evaluateForAll
33592
+ },
33593
+ NotForAll: {
33594
+ signature: "(value, boolean) -> boolean",
33595
+ lazy: true,
33596
+ scoped: true,
33597
+ evaluate: (args, options) => {
33598
+ const result = evaluateForAll(args, options);
33599
+ if (result?.symbol === "True") return options.engine.False;
33600
+ if (result?.symbol === "False") return options.engine.True;
33601
+ return void 0;
33602
+ }
33603
+ },
33604
+ // Predicate application in First-Order Logic.
33605
+ // ["Predicate", "P", "x"] represents the predicate P applied to x.
33606
+ // This is semantically different from a function application: predicates
33607
+ // return boolean values and are used in logical formulas.
33608
+ // In LaTeX, P(x) inside a quantifier context parses to ["Predicate", "P", "x"].
33609
+ Predicate: {
33610
+ description: "Apply a predicate to arguments, returning a boolean",
33611
+ signature: "(symbol, value+) -> boolean",
33612
+ lazy: true,
33613
+ // Predicates remain symbolic unless explicitly defined
33614
+ evaluate: (args, { engine }) => {
33615
+ if (args.length === 0) return void 0;
33616
+ const pred = args[0];
33617
+ if (!pred.symbol) return void 0;
33618
+ return void 0;
33619
+ }
33620
+ },
32746
33621
  KroneckerDelta: {
32747
33622
  description: "Return 1 if the arguments are equal, 0 otherwise",
32748
33623
  signature: "(value+) -> integer",
@@ -32763,82 +33638,189 @@ var LOGIC_LIBRARY = {
32763
33638
  evaluate: (args, { engine: ce }) => args[0].symbol === "True" ? ce.One : ce.Zero
32764
33639
  }
32765
33640
  };
32766
- function evaluateAnd(args, { engine: ce }) {
32767
- if (args.length === 0) return ce.True;
32768
- const ops = [];
32769
- for (const arg of args) {
32770
- if (arg.symbol === "False") return ce.False;
32771
- if (arg.symbol !== "True") {
32772
- let duplicate = false;
32773
- for (const x of ops) {
32774
- if (x.isSame(arg)) {
32775
- duplicate = true;
32776
- } else if (arg.operator === "Not" && arg.op1.isSame(x) || x.operator === "Not" && x.op1.isSame(arg)) {
32777
- return ce.False;
32778
- }
32779
- }
32780
- if (!duplicate) ops.push(arg);
32781
- }
32782
- }
32783
- if (ops.length === 0) return ce.True;
32784
- if (ops.length === 1) return ops[0];
32785
- return ce._fn("And", ops);
33641
+ function simplifyLogicFunction(x) {
33642
+ const fn = {
33643
+ And: evaluateAnd,
33644
+ Or: evaluateOr,
33645
+ Not: evaluateNot,
33646
+ Equivalent: evaluateEquivalent,
33647
+ Implies: evaluateImplies,
33648
+ Xor: evaluateXor,
33649
+ Nand: evaluateNand,
33650
+ Nor: evaluateNor
33651
+ }[x.operator];
33652
+ if (!fn || !x.ops) return void 0;
33653
+ const value = fn(x.ops, { engine: x.engine });
33654
+ if (!value) return void 0;
33655
+ return { value, because: "logic" };
32786
33656
  }
32787
- function evaluateOr(args, { engine: ce }) {
32788
- if (args.length === 0) return ce.True;
32789
- const ops = [];
32790
- for (const arg of args) {
32791
- if (arg.symbol === "True") return ce.True;
32792
- if (arg.symbol !== "False") {
32793
- let duplicate = false;
32794
- for (const x of ops) {
32795
- if (x.isSame(arg)) {
32796
- duplicate = true;
32797
- } else if (arg.operator === "Not" && arg.op1.isSame(x) || x.operator === "Not" && x.op1.isSame(arg)) {
32798
- return ce.True;
32799
- }
33657
+ function evaluateForAll(args, { engine: ce }) {
33658
+ if (args.length < 2) return void 0;
33659
+ const condition = args[0];
33660
+ const body = args[1];
33661
+ const canonicalBody = body.canonical;
33662
+ if (canonicalBody.symbol === "True") return ce.True;
33663
+ if (canonicalBody.symbol === "False") return ce.False;
33664
+ const variable = condition.symbol ?? condition.op1?.symbol;
33665
+ if (variable && !bodyContainsVariable(canonicalBody, variable)) {
33666
+ return canonicalBody.evaluate();
33667
+ }
33668
+ const domain = extractFiniteDomain(condition, ce);
33669
+ if (domain) {
33670
+ const nestedDomains = collectNestedDomains(body, ce);
33671
+ if (nestedDomains.length > 0) {
33672
+ return evaluateForAllCartesian(
33673
+ [{ variable: domain.variable, values: domain.values }, ...nestedDomains],
33674
+ getInnermostBody(body),
33675
+ ce
33676
+ );
33677
+ }
33678
+ for (const value of domain.values) {
33679
+ const substituted = body.subs({ [domain.variable]: value }).canonical;
33680
+ const result = substituted.evaluate();
33681
+ if (result.symbol === "False") {
33682
+ return ce.False;
33683
+ }
33684
+ if (result.symbol !== "True") {
33685
+ return void 0;
32800
33686
  }
32801
- if (!duplicate) ops.push(arg);
32802
33687
  }
33688
+ return ce.True;
32803
33689
  }
32804
- if (ops.length === 0) return ce.False;
32805
- if (ops.length === 1) return ops[0];
32806
- return ce._fn("Or", ops);
32807
- }
32808
- function evaluateNot(args, { engine: ce }) {
32809
- const op1 = args[0]?.symbol;
32810
- if (op1 === "True") return ce.False;
32811
- if (op1 === "False") return ce.True;
33690
+ const bodyEval = canonicalBody.evaluate();
33691
+ if (bodyEval.symbol === "True") return ce.True;
33692
+ if (bodyEval.symbol === "False") return ce.False;
32812
33693
  return void 0;
32813
33694
  }
32814
- function evaluateEquivalent(args, { engine: ce }) {
32815
- const lhs = args[0].symbol;
32816
- const rhs = args[1].symbol;
32817
- if (lhs === "True" && rhs === "True" || lhs === "False" && rhs === "False")
32818
- return ce.True;
32819
- if (lhs === "True" && rhs === "False" || lhs === "False" && rhs === "True")
33695
+ function evaluateExists(args, { engine: ce }) {
33696
+ if (args.length < 2) return void 0;
33697
+ const condition = args[0];
33698
+ const body = args[1];
33699
+ const canonicalBody = body.canonical;
33700
+ if (canonicalBody.symbol === "True") return ce.True;
33701
+ if (canonicalBody.symbol === "False") return ce.False;
33702
+ const variable = condition.symbol ?? condition.op1?.symbol;
33703
+ if (variable && !bodyContainsVariable(canonicalBody, variable)) {
33704
+ return canonicalBody.evaluate();
33705
+ }
33706
+ const domain = extractFiniteDomain(condition, ce);
33707
+ if (domain) {
33708
+ const nestedDomains = collectNestedDomains(body, ce);
33709
+ if (nestedDomains.length > 0) {
33710
+ return evaluateExistsCartesian(
33711
+ [{ variable: domain.variable, values: domain.values }, ...nestedDomains],
33712
+ getInnermostBody(body),
33713
+ ce
33714
+ );
33715
+ }
33716
+ for (const value of domain.values) {
33717
+ const substituted = body.subs({ [domain.variable]: value }).canonical;
33718
+ const result = substituted.evaluate();
33719
+ if (result.symbol === "True") {
33720
+ return ce.True;
33721
+ }
33722
+ }
32820
33723
  return ce.False;
33724
+ }
33725
+ const bodyEval = canonicalBody.evaluate();
33726
+ if (bodyEval.symbol === "True") return ce.True;
33727
+ if (bodyEval.symbol === "False") return ce.False;
32821
33728
  return void 0;
32822
33729
  }
32823
- function evaluateImplies(args, { engine: ce }) {
32824
- const lhs = args[0].symbol;
32825
- const rhs = args[1].symbol;
32826
- if (lhs === "True" && rhs === "True" || lhs === "False" && rhs === "False" || lhs === "False" && rhs === "True")
32827
- return ce.True;
32828
- if (lhs === "True" && rhs === "False") return ce.False;
33730
+ function evaluateExistsUnique(args, { engine: ce }) {
33731
+ if (args.length < 2) return void 0;
33732
+ const condition = args[0];
33733
+ const body = args[1];
33734
+ const domain = extractFiniteDomain(condition, ce);
33735
+ if (domain) {
33736
+ let count = 0;
33737
+ for (const value of domain.values) {
33738
+ const substituted = body.subs({ [domain.variable]: value }).canonical;
33739
+ const result = substituted.evaluate();
33740
+ if (result.symbol === "True") {
33741
+ count++;
33742
+ if (count > 1) return ce.False;
33743
+ } else if (result.symbol !== "False") {
33744
+ return void 0;
33745
+ }
33746
+ }
33747
+ return count === 1 ? ce.True : ce.False;
33748
+ }
32829
33749
  return void 0;
32830
33750
  }
32831
- function simplifyLogicFunction(x) {
32832
- const value = {
32833
- And: evaluateAnd,
32834
- Or: evaluateOr,
32835
- Not: evaluateNot,
32836
- Equivalent: evaluateEquivalent,
32837
- Implies: evaluateImplies
32838
- }[x.operator]?.(x.engine, x.ops);
32839
- if (!value) return void 0;
32840
- return { value, because: "logic" };
32841
- }
33751
+ var LOGIC_FUNCTION_LIBRARY = {
33752
+ /**
33753
+ * Convert a boolean expression to Conjunctive Normal Form (CNF).
33754
+ * CNF is a conjunction (And) of disjunctions (Or) of literals.
33755
+ * A literal is either a variable or its negation.
33756
+ *
33757
+ * Example: (A ∨ B) ∧ (¬A ∨ C)
33758
+ */
33759
+ ToCNF: {
33760
+ signature: "(boolean) -> boolean",
33761
+ evaluate: ([expr], { engine: ce }) => {
33762
+ if (!expr) return void 0;
33763
+ return toCNF(expr.evaluate(), ce);
33764
+ }
33765
+ },
33766
+ /**
33767
+ * Convert a boolean expression to Disjunctive Normal Form (DNF).
33768
+ * DNF is a disjunction (Or) of conjunctions (And) of literals.
33769
+ * A literal is either a variable or its negation.
33770
+ *
33771
+ * Example: (A ∧ B) ∨ (¬A ∧ C)
33772
+ */
33773
+ ToDNF: {
33774
+ signature: "(boolean) -> boolean",
33775
+ evaluate: ([expr], { engine: ce }) => {
33776
+ if (!expr) return void 0;
33777
+ return toDNF(expr.evaluate(), ce);
33778
+ }
33779
+ },
33780
+ /**
33781
+ * Check if a boolean expression is satisfiable.
33782
+ * Returns True if there exists an assignment of truth values to variables
33783
+ * that makes the expression true.
33784
+ */
33785
+ IsSatisfiable: {
33786
+ signature: "(boolean) -> boolean",
33787
+ evaluate: ([expr], { engine: ce }) => {
33788
+ if (!expr) return void 0;
33789
+ return isSatisfiable(expr, ce);
33790
+ }
33791
+ },
33792
+ /**
33793
+ * Check if a boolean expression is a tautology.
33794
+ * Returns True if the expression is true for all possible assignments
33795
+ * of truth values to variables.
33796
+ */
33797
+ IsTautology: {
33798
+ signature: "(boolean) -> boolean",
33799
+ evaluate: ([expr], { engine: ce }) => {
33800
+ if (!expr) return void 0;
33801
+ return isTautology(expr, ce);
33802
+ }
33803
+ },
33804
+ /**
33805
+ * Generate a truth table for a boolean expression.
33806
+ * Returns a List of Lists, where each inner list contains the variable
33807
+ * assignments followed by the result.
33808
+ *
33809
+ * Example: TruthTable(["And", "A", "B"]) returns:
33810
+ * [["List", "A", "B", "Result"],
33811
+ * ["List", False, False, False],
33812
+ * ["List", False, True, False],
33813
+ * ["List", True, False, False],
33814
+ * ["List", True, True, True]]
33815
+ */
33816
+ TruthTable: {
33817
+ signature: "(boolean) -> list",
33818
+ evaluate: ([expr], { engine: ce }) => {
33819
+ if (!expr) return void 0;
33820
+ return generateTruthTable(expr, ce);
33821
+ }
33822
+ }
33823
+ };
32842
33824
 
32843
33825
  // src/compute-engine/library/number-theory.ts
32844
33826
  var NUMBER_THEORY_LIBRARY = [
@@ -35370,7 +36352,7 @@ var LIBRARIES = {
35370
36352
  "domains": [],
35371
36353
  // 'domains': getDomainsDictionary(),
35372
36354
  "linear-algebra": LINEAR_ALGEBRA_LIBRARY,
35373
- "logic": LOGIC_LIBRARY,
36355
+ "logic": [LOGIC_LIBRARY, LOGIC_FUNCTION_LIBRARY],
35374
36356
  "number-theory": NUMBER_THEORY_LIBRARY,
35375
36357
  "numeric": [],
35376
36358
  // @todo // 'numeric': [
@@ -35666,6 +36648,64 @@ function matchOnce(expr, pattern, substitution, options) {
35666
36648
  const ce = expr.engine;
35667
36649
  let result = null;
35668
36650
  const operator2 = pattern.operator;
36651
+ if (operator2 === "Divide" && expr.numericValue !== null && !expr.denominator.is(1)) {
36652
+ const divideExpr = ce.function(
36653
+ "Divide",
36654
+ [expr.numerator, expr.denominator],
36655
+ { canonical: false, structural: true }
36656
+ );
36657
+ return matchArguments(divideExpr, pattern.ops, substitution, options);
36658
+ }
36659
+ if (operator2 === "Divide" && expr.operator === "Multiply") {
36660
+ const ops = expr.ops;
36661
+ for (let i = 0; i < ops.length; i++) {
36662
+ const op = ops[i];
36663
+ if (op.numericValue !== null && op.numerator.is(1) && !op.denominator.is(1)) {
36664
+ const others = ops.filter((_, j) => j !== i);
36665
+ const numerator = others.length === 1 ? others[0] : ce.function("Multiply", others, { canonical: false });
36666
+ const divideExpr = ce.function(
36667
+ "Divide",
36668
+ [numerator, op.denominator],
36669
+ { canonical: false, structural: true }
36670
+ );
36671
+ const result2 = matchArguments(
36672
+ divideExpr,
36673
+ pattern.ops,
36674
+ substitution,
36675
+ options
36676
+ );
36677
+ if (result2 !== null) return result2;
36678
+ }
36679
+ }
36680
+ }
36681
+ if (operator2 === "Power" && expr.operator === "Divide" && expr.op1.is(1)) {
36682
+ const powerExpr = ce.function(
36683
+ "Power",
36684
+ [expr.op2, ce.number(-1)],
36685
+ { canonical: false, structural: true }
36686
+ );
36687
+ const result2 = matchArguments(
36688
+ powerExpr,
36689
+ pattern.ops,
36690
+ substitution,
36691
+ options
36692
+ );
36693
+ if (result2 !== null) return result2;
36694
+ }
36695
+ if (operator2 === "Power" && expr.operator === "Root") {
36696
+ const powerExpr = ce.function(
36697
+ "Power",
36698
+ [expr.op1, ce.box(["Divide", 1, expr.op2], { canonical: false })],
36699
+ { canonical: false, structural: true }
36700
+ );
36701
+ const result2 = matchArguments(
36702
+ powerExpr,
36703
+ pattern.ops,
36704
+ substitution,
36705
+ options
36706
+ );
36707
+ if (result2 !== null) return result2;
36708
+ }
35669
36709
  if (operator2.startsWith("_")) {
35670
36710
  result = captureWildcard(operator2, ce.box(expr.operator), substitution);
35671
36711
  if (result !== null)
@@ -38715,12 +39755,568 @@ var BoxedSymbol = class extends _BoxedExpression {
38715
39755
  }
38716
39756
  };
38717
39757
 
39758
+ // src/compute-engine/symbolic/simplify-sum.ts
39759
+ function simplifySum(x) {
39760
+ if (x.operator !== "Sum") return void 0;
39761
+ let body = x.op1;
39762
+ const limits = x.op2;
39763
+ if (!body || !limits || limits.operator !== "Limits") return void 0;
39764
+ const index = limits.op1?.symbol;
39765
+ const lower = limits.op2;
39766
+ const upper = limits.op3;
39767
+ if (!index || !lower || !upper) return void 0;
39768
+ const ce = x.engine;
39769
+ if (body.operator === "Sum" || body.operator === "Product") {
39770
+ const simplifiedBody = body.simplify();
39771
+ if (!simplifiedBody.isSame(body)) {
39772
+ const newSum = ce.function("Sum", [simplifiedBody, limits]);
39773
+ return { value: newSum, because: "simplified nested sum/product" };
39774
+ }
39775
+ }
39776
+ if (lower.isNumberLiteral && upper.isNumberLiteral) {
39777
+ const lowerVal = lower.numericValue;
39778
+ const upperVal = upper.numericValue;
39779
+ if (typeof lowerVal === "number" && typeof upperVal === "number" && Number.isInteger(lowerVal) && Number.isInteger(upperVal)) {
39780
+ if (upperVal < lowerVal) {
39781
+ return { value: ce.Zero, because: "empty sum" };
39782
+ }
39783
+ if (upperVal === lowerVal) {
39784
+ return {
39785
+ value: body.subs({ [index]: lower }).simplify(),
39786
+ because: "single term sum"
39787
+ };
39788
+ }
39789
+ }
39790
+ }
39791
+ const bodyUnknowns = new Set(body.unknowns);
39792
+ if (!bodyUnknowns.has(index)) {
39793
+ const count = upper.sub(lower).add(ce.One).simplify();
39794
+ if (count.isNumberLiteral && count.numericValue !== null) {
39795
+ const countVal = typeof count.numericValue === "number" ? count.numericValue : count.numericValue.re;
39796
+ if (countVal <= 0) {
39797
+ return { value: ce.Zero, because: "empty sum" };
39798
+ }
39799
+ }
39800
+ return {
39801
+ value: count.mul(body.simplify()),
39802
+ because: "sum of constant"
39803
+ };
39804
+ }
39805
+ if (body.symbol === index) {
39806
+ const a = lower;
39807
+ const b = upper;
39808
+ const result = b.mul(b.add(ce.One)).sub(a.mul(a.sub(ce.One))).div(2);
39809
+ return { value: result.simplify(), because: "triangular number" };
39810
+ }
39811
+ if (body.operator === "Power" && body.op1?.symbol === index && body.op2?.is(2) && lower.is(1)) {
39812
+ const b = upper;
39813
+ const result = b.mul(b.add(ce.One)).mul(b.mul(2).add(ce.One)).div(6);
39814
+ return { value: result, because: "sum of squares" };
39815
+ }
39816
+ if (body.operator === "Power" && body.op1?.symbol === index && body.op2?.is(3) && lower.is(1)) {
39817
+ const b = upper;
39818
+ const triangular = b.mul(b.add(ce.One)).div(2);
39819
+ return { value: triangular.pow(2), because: "sum of cubes" };
39820
+ }
39821
+ if (body.operator === "Power" && body.op1?.is(-1) && body.op2?.symbol === index && lower.is(0)) {
39822
+ const b = upper;
39823
+ const result = ce.One.add(ce.number(-1).pow(b)).div(2);
39824
+ return { value: result, because: "alternating unit series" };
39825
+ }
39826
+ if (body.operator === "Multiply" && body.ops && lower.is(0)) {
39827
+ let hasAlternating = false;
39828
+ let hasIndex = false;
39829
+ for (const op of body.ops) {
39830
+ if (op.operator === "Power" && op.op1?.is(-1) && op.op2?.symbol === index) {
39831
+ hasAlternating = true;
39832
+ } else if (op.symbol === index) {
39833
+ hasIndex = true;
39834
+ }
39835
+ }
39836
+ if (hasAlternating && hasIndex && body.ops.length === 2) {
39837
+ const b = upper;
39838
+ const result = ce.function("Multiply", [
39839
+ ce.function("Power", [ce.number(-1), b]),
39840
+ ce.function("Floor", [
39841
+ ce.function("Divide", [ce.function("Add", [b, ce.One]), ce.number(2)])
39842
+ ])
39843
+ ]);
39844
+ return { value: result, because: "alternating linear series" };
39845
+ }
39846
+ }
39847
+ if (body.operator === "Add" && body.ops) {
39848
+ let constant = null;
39849
+ let coefficient = null;
39850
+ for (const term of body.ops) {
39851
+ const termUnknowns = new Set(term.unknowns);
39852
+ if (!termUnknowns.has(index)) {
39853
+ constant = constant ? constant.add(term) : term;
39854
+ } else if (term.symbol === index) {
39855
+ coefficient = coefficient ? coefficient.add(ce.One) : ce.One;
39856
+ } else if (term.operator === "Multiply" && term.ops?.some((op) => op.symbol === index)) {
39857
+ const coef = term.ops.filter((op) => op.symbol !== index);
39858
+ if (coef.length === term.ops.length - 1) {
39859
+ const c = coef.length === 1 ? coef[0] : ce.function("Multiply", coef);
39860
+ coefficient = coefficient ? coefficient.add(c) : c;
39861
+ }
39862
+ } else {
39863
+ constant = null;
39864
+ coefficient = null;
39865
+ break;
39866
+ }
39867
+ }
39868
+ if (constant !== null && coefficient !== null) {
39869
+ const m = lower;
39870
+ const b = upper;
39871
+ if (lower.is(0)) {
39872
+ const bPlus1 = ce.function("Add", [b, ce.One]);
39873
+ const inner = ce.function("Add", [
39874
+ constant,
39875
+ ce.function("Divide", [
39876
+ ce.function("Multiply", [coefficient, b]),
39877
+ ce.number(2)
39878
+ ])
39879
+ ]);
39880
+ const result = ce.function("Multiply", [bPlus1, inner]);
39881
+ return { value: result, because: "arithmetic progression" };
39882
+ } else {
39883
+ const numTerms = ce.function("Add", [
39884
+ ce.function("Subtract", [b, m]),
39885
+ ce.One
39886
+ ]);
39887
+ const avgIndex = ce.function("Divide", [
39888
+ ce.function("Add", [m, b]),
39889
+ ce.number(2)
39890
+ ]);
39891
+ const avgValue = ce.function("Add", [
39892
+ constant,
39893
+ ce.function("Multiply", [coefficient, avgIndex])
39894
+ ]);
39895
+ const result = ce.function("Multiply", [numTerms, avgValue]);
39896
+ return { value: result, because: "arithmetic progression" };
39897
+ }
39898
+ }
39899
+ }
39900
+ if (body.operator === "Power" && body.op2?.symbol === index && !new Set(body.op1?.unknowns ?? []).has(index)) {
39901
+ const r = body.op1;
39902
+ const b = upper;
39903
+ if (lower.is(0)) {
39904
+ const numerator = ce.One.sub(r.pow(b.add(ce.One)));
39905
+ const denominator = ce.One.sub(r);
39906
+ return { value: numerator.div(denominator), because: "geometric series" };
39907
+ } else if (lower.is(1)) {
39908
+ const numerator = r.sub(r.pow(b.add(ce.One)));
39909
+ const denominator = ce.One.sub(r);
39910
+ return { value: numerator.div(denominator), because: "geometric series" };
39911
+ }
39912
+ }
39913
+ if (body.operator === "Binomial" && lower.is(0) && body.op2?.symbol === index) {
39914
+ const n = body.op1;
39915
+ if (n && upper.isSame(n)) {
39916
+ const result = ce.function("Power", [ce.number(2), n]);
39917
+ return { value: result, because: "sum of binomial coefficients" };
39918
+ }
39919
+ }
39920
+ if (body.operator === "Multiply" && body.ops && lower.is(0)) {
39921
+ let hasBinomial = false;
39922
+ let hasAlternating = false;
39923
+ let binomialN = null;
39924
+ for (const op of body.ops) {
39925
+ if (op.operator === "Binomial" && op.op2?.symbol === index) {
39926
+ hasBinomial = true;
39927
+ binomialN = op.op1 ?? null;
39928
+ } else if (op.operator === "Power" && op.op1?.is(-1) && op.op2?.symbol === index) {
39929
+ hasAlternating = true;
39930
+ }
39931
+ }
39932
+ if (hasBinomial && hasAlternating && binomialN && upper.isSame(binomialN)) {
39933
+ return { value: ce.Zero, because: "alternating binomial sum" };
39934
+ }
39935
+ let hasIndex = false;
39936
+ binomialN = null;
39937
+ hasBinomial = false;
39938
+ for (const op of body.ops) {
39939
+ if (op.symbol === index) {
39940
+ hasIndex = true;
39941
+ } else if (op.operator === "Binomial" && op.op2?.symbol === index) {
39942
+ hasBinomial = true;
39943
+ binomialN = op.op1 ?? null;
39944
+ }
39945
+ }
39946
+ if (hasIndex && hasBinomial && binomialN && upper.isSame(binomialN) && body.ops.length === 2) {
39947
+ const n = binomialN;
39948
+ const result = ce.function("Multiply", [
39949
+ n,
39950
+ ce.function("Power", [ce.number(2), n.sub(ce.One)])
39951
+ ]);
39952
+ return { value: result, because: "weighted binomial sum" };
39953
+ }
39954
+ let hasIndexSquared = false;
39955
+ binomialN = null;
39956
+ hasBinomial = false;
39957
+ for (const op of body.ops) {
39958
+ if (op.operator === "Power" && op.op1?.symbol === index && op.op2?.is(2)) {
39959
+ hasIndexSquared = true;
39960
+ } else if (op.operator === "Binomial" && op.op2?.symbol === index) {
39961
+ hasBinomial = true;
39962
+ binomialN = op.op1 ?? null;
39963
+ }
39964
+ }
39965
+ if (hasIndexSquared && hasBinomial && binomialN && upper.isSame(binomialN) && body.ops.length === 2) {
39966
+ const n = binomialN;
39967
+ const result = ce.function("Multiply", [
39968
+ n,
39969
+ n.add(ce.One),
39970
+ ce.function("Power", [ce.number(2), n.sub(ce.number(2))])
39971
+ ]);
39972
+ return { value: result, because: "weighted squared binomial sum" };
39973
+ }
39974
+ let hasIndexCubed = false;
39975
+ binomialN = null;
39976
+ hasBinomial = false;
39977
+ for (const op of body.ops) {
39978
+ if (op.operator === "Power" && op.op1?.symbol === index && op.op2?.is(3)) {
39979
+ hasIndexCubed = true;
39980
+ } else if (op.operator === "Binomial" && op.op2?.symbol === index) {
39981
+ hasBinomial = true;
39982
+ binomialN = op.op1 ?? null;
39983
+ }
39984
+ }
39985
+ if (hasIndexCubed && hasBinomial && binomialN && upper.isSame(binomialN) && body.ops.length === 2) {
39986
+ const n = binomialN;
39987
+ const result = ce.function("Multiply", [
39988
+ ce.function("Power", [n, ce.number(2)]),
39989
+ n.add(ce.number(3)),
39990
+ ce.function("Power", [ce.number(2), n.sub(ce.number(3))])
39991
+ ]);
39992
+ return { value: result, because: "weighted cubed binomial sum" };
39993
+ }
39994
+ let hasAltTerm = false;
39995
+ let hasIndexTerm = false;
39996
+ binomialN = null;
39997
+ hasBinomial = false;
39998
+ for (const op of body.ops) {
39999
+ if (op.operator === "Power" && op.op1?.is(-1) && op.op2?.symbol === index) {
40000
+ hasAltTerm = true;
40001
+ } else if (op.symbol === index) {
40002
+ hasIndexTerm = true;
40003
+ } else if (op.operator === "Binomial" && op.op2?.symbol === index) {
40004
+ hasBinomial = true;
40005
+ binomialN = op.op1 ?? null;
40006
+ }
40007
+ }
40008
+ if (hasAltTerm && hasIndexTerm && hasBinomial && binomialN && upper.isSame(binomialN) && body.ops.length === 3) {
40009
+ return { value: ce.Zero, because: "alternating weighted binomial sum" };
40010
+ }
40011
+ }
40012
+ if (body.operator === "Power" && body.op1?.operator === "Binomial" && body.op2?.is(2) && lower.is(0)) {
40013
+ const binomial2 = body.op1;
40014
+ const n = binomial2.op1;
40015
+ const k = binomial2.op2;
40016
+ if (n && k?.symbol === index && upper.isSame(n)) {
40017
+ const result = ce.function("Binomial", [
40018
+ ce.function("Multiply", [ce.number(2), n]),
40019
+ n
40020
+ ]);
40021
+ return { value: result, because: "sum of binomial squares" };
40022
+ }
40023
+ }
40024
+ if (body.operator === "Multiply" && body.ops?.length === 2 && lower.is(1)) {
40025
+ const [op1, op2] = body.ops;
40026
+ 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));
40027
+ if (isKTimesKPlus1) {
40028
+ const n = upper;
40029
+ const result = ce.function("Divide", [
40030
+ ce.function("Multiply", [
40031
+ n,
40032
+ ce.function("Add", [n, ce.One]),
40033
+ ce.function("Add", [n, ce.number(2)])
40034
+ ]),
40035
+ ce.number(3)
40036
+ ]);
40037
+ return { value: result, because: "sum of k*(k+1)" };
40038
+ }
40039
+ }
40040
+ if (body.operator === "Divide" && body.op1?.is(1) && body.op2?.operator === "Multiply") {
40041
+ const denom = body.op2;
40042
+ if (denom.ops?.length === 2) {
40043
+ const [d1, d2] = denom.ops;
40044
+ if (lower.is(1)) {
40045
+ 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));
40046
+ if (isKTimesKPlus1) {
40047
+ const n = upper;
40048
+ const result = n.div(n.add(ce.One));
40049
+ return { value: result, because: "partial fractions (telescoping)" };
40050
+ }
40051
+ }
40052
+ if (lower.is(2)) {
40053
+ 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));
40054
+ if (isKTimesKMinus1) {
40055
+ const n = upper;
40056
+ const result = n.sub(ce.One).div(n);
40057
+ return { value: result, because: "partial fractions (telescoping k*(k-1))" };
40058
+ }
40059
+ }
40060
+ }
40061
+ }
40062
+ if (body.operator === "Multiply" && body.ops) {
40063
+ const constantFactors = [];
40064
+ const indexFactors = [];
40065
+ for (const factor2 of body.ops) {
40066
+ const factorUnknowns = new Set(factor2.unknowns);
40067
+ if (factorUnknowns.has(index)) {
40068
+ indexFactors.push(factor2);
40069
+ } else {
40070
+ constantFactors.push(factor2);
40071
+ }
40072
+ }
40073
+ if (constantFactors.length > 0 && indexFactors.length > 0) {
40074
+ const constant = constantFactors.length === 1 ? constantFactors[0] : ce.function("Multiply", constantFactors);
40075
+ const indexPart = indexFactors.length === 1 ? indexFactors[0] : ce.function("Multiply", indexFactors);
40076
+ const newSum = ce.function("Sum", [indexPart, limits]);
40077
+ return {
40078
+ value: constant.mul(newSum),
40079
+ because: "factor out constant from sum"
40080
+ };
40081
+ }
40082
+ }
40083
+ return void 0;
40084
+ }
40085
+
40086
+ // src/compute-engine/symbolic/simplify-product.ts
40087
+ function simplifyProduct(x) {
40088
+ if (x.operator !== "Product") return void 0;
40089
+ let body = x.op1;
40090
+ const limits = x.op2;
40091
+ if (!body || !limits || limits.operator !== "Limits") return void 0;
40092
+ const index = limits.op1?.symbol;
40093
+ const lower = limits.op2;
40094
+ const upper = limits.op3;
40095
+ if (!index || !lower || !upper) return void 0;
40096
+ const ce = x.engine;
40097
+ if (body.operator === "Sum" || body.operator === "Product") {
40098
+ const simplifiedBody = body.simplify();
40099
+ if (!simplifiedBody.isSame(body)) {
40100
+ const newProduct = ce.function("Product", [simplifiedBody, limits]);
40101
+ return { value: newProduct, because: "simplified nested sum/product" };
40102
+ }
40103
+ }
40104
+ if (lower.isNumberLiteral && upper.isNumberLiteral) {
40105
+ const lowerVal = lower.numericValue;
40106
+ const upperVal = upper.numericValue;
40107
+ if (typeof lowerVal === "number" && typeof upperVal === "number" && Number.isInteger(lowerVal) && Number.isInteger(upperVal)) {
40108
+ if (upperVal < lowerVal) {
40109
+ return { value: ce.One, because: "empty product" };
40110
+ }
40111
+ if (upperVal === lowerVal) {
40112
+ return {
40113
+ value: body.subs({ [index]: lower }).simplify(),
40114
+ because: "single term product"
40115
+ };
40116
+ }
40117
+ }
40118
+ }
40119
+ const bodyUnknowns = new Set(body.unknowns);
40120
+ if (!bodyUnknowns.has(index)) {
40121
+ const count = upper.sub(lower).add(ce.One).simplify();
40122
+ if (count.isNumberLiteral && count.numericValue !== null) {
40123
+ const countVal = typeof count.numericValue === "number" ? count.numericValue : count.numericValue.re;
40124
+ if (countVal <= 0) {
40125
+ return { value: ce.One, because: "empty product" };
40126
+ }
40127
+ }
40128
+ return {
40129
+ value: body.simplify().pow(count),
40130
+ because: "product of constant"
40131
+ };
40132
+ }
40133
+ if (body.symbol === index && lower.is(1)) {
40134
+ return {
40135
+ value: ce.function("Factorial", [upper]),
40136
+ because: "factorial"
40137
+ };
40138
+ }
40139
+ if (body.operator === "Add" && body.ops?.length === 2 && lower.is(1)) {
40140
+ const [op1, op2] = body.ops;
40141
+ let indexTerm = null;
40142
+ let constTerm = null;
40143
+ if (op1.symbol === index && !new Set(op2.unknowns).has(index)) {
40144
+ indexTerm = op1;
40145
+ constTerm = op2;
40146
+ } else if (op2.symbol === index && !new Set(op1.unknowns).has(index)) {
40147
+ indexTerm = op2;
40148
+ constTerm = op1;
40149
+ }
40150
+ if (indexTerm && constTerm) {
40151
+ const b = upper;
40152
+ const c = constTerm;
40153
+ const result = ce.function("Divide", [
40154
+ ce.function("Factorial", [ce.function("Add", [b, c])]),
40155
+ ce.function("Factorial", [c])
40156
+ ]);
40157
+ return { value: result, because: "shifted factorial" };
40158
+ }
40159
+ }
40160
+ if (body.operator === "Divide" && lower.is(1)) {
40161
+ const num = body.op1;
40162
+ const denom = body.op2;
40163
+ 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))) {
40164
+ return { value: upper.add(ce.One), because: "telescoping product" };
40165
+ }
40166
+ }
40167
+ if (body.operator === "Add" && body.ops?.length === 2 && lower.is(2)) {
40168
+ let hasOne = false;
40169
+ let hasNegInvSq = false;
40170
+ for (const op of body.ops) {
40171
+ if (op.is(1)) {
40172
+ hasOne = true;
40173
+ } else if (op.operator === "Negate" && op.op1?.operator === "Power" && op.op1.op1?.symbol === index && op.op1.op2?.is(-2)) {
40174
+ hasNegInvSq = true;
40175
+ } else if (op.operator === "Power" && op.op1?.symbol === index && op.op2?.is(-2)) {
40176
+ } else if (op.operator === "Multiply" && op.ops?.some((o) => o.is(-1)) && op.ops?.some(
40177
+ (o) => o.operator === "Power" && o.op1?.symbol === index && o.op2?.is(-2)
40178
+ )) {
40179
+ hasNegInvSq = true;
40180
+ }
40181
+ }
40182
+ if (hasOne && hasNegInvSq) {
40183
+ const n = upper;
40184
+ const result = ce.function("Divide", [
40185
+ ce.function("Add", [n, ce.One]),
40186
+ ce.function("Multiply", [ce.number(2), n])
40187
+ ]);
40188
+ return { value: result, because: "Wallis-like product" };
40189
+ }
40190
+ }
40191
+ if (body.operator === "Add" && body.ops?.length === 2 && lower.is(1)) {
40192
+ const [op1, op2] = body.ops;
40193
+ let hasLinearTerm = false;
40194
+ let coefficient = 0;
40195
+ let constantTerm = 0;
40196
+ for (const op of body.ops) {
40197
+ if (op.isNumberLiteral && typeof op.numericValue === "number") {
40198
+ constantTerm = op.numericValue;
40199
+ } else if (op.operator === "Multiply" && op.ops?.length === 2) {
40200
+ const [a, b] = op.ops;
40201
+ if (a.isNumberLiteral && typeof a.numericValue === "number" && b.symbol === index) {
40202
+ coefficient = a.numericValue;
40203
+ hasLinearTerm = true;
40204
+ } else if (b.isNumberLiteral && typeof b.numericValue === "number" && a.symbol === index) {
40205
+ coefficient = b.numericValue;
40206
+ hasLinearTerm = true;
40207
+ }
40208
+ }
40209
+ }
40210
+ if (hasLinearTerm && coefficient === 2 && constantTerm === -1) {
40211
+ const b = upper;
40212
+ const result = ce.function("Factorial2", [
40213
+ ce.function("Subtract", [
40214
+ ce.function("Multiply", [ce.number(2), b]),
40215
+ ce.One
40216
+ ])
40217
+ ]);
40218
+ return { value: result, because: "odd double factorial" };
40219
+ }
40220
+ }
40221
+ if (body.operator === "Multiply" && body.ops?.length === 2 && lower.is(1)) {
40222
+ const [op1, op2] = body.ops;
40223
+ if (op1.is(2) && op2.symbol === index || op2.is(2) && op1.symbol === index) {
40224
+ const b = upper;
40225
+ const result = ce.function("Multiply", [
40226
+ ce.function("Power", [ce.number(2), b]),
40227
+ ce.function("Factorial", [b])
40228
+ ]);
40229
+ return { value: result, because: "even double factorial" };
40230
+ }
40231
+ }
40232
+ if (body.operator === "Add" && body.ops?.length === 2 && lower.is(0)) {
40233
+ let base = null;
40234
+ let hasIndex = false;
40235
+ for (const op of body.ops) {
40236
+ if (op.symbol === index) {
40237
+ hasIndex = true;
40238
+ } else if (!new Set(op.unknowns).has(index)) {
40239
+ base = op;
40240
+ }
40241
+ }
40242
+ if (hasIndex && base) {
40243
+ const n = upper.add(ce.One).simplify();
40244
+ const result = ce.function("Pochhammer", [base, n]);
40245
+ return { value: result, because: "rising factorial (Pochhammer)" };
40246
+ }
40247
+ }
40248
+ if (lower.is(0)) {
40249
+ let base = null;
40250
+ let hasNegIndex = false;
40251
+ if (body.operator === "Subtract" && body.ops?.length === 2) {
40252
+ const [op1, op2] = body.ops;
40253
+ if (op2.symbol === index && !new Set(op1.unknowns).has(index)) {
40254
+ base = op1;
40255
+ hasNegIndex = true;
40256
+ }
40257
+ } else if (body.operator === "Add" && body.ops?.length === 2) {
40258
+ for (const op of body.ops) {
40259
+ if (op.operator === "Negate" && op.op1?.symbol === index) {
40260
+ hasNegIndex = true;
40261
+ } else if (!new Set(op.unknowns).has(index)) {
40262
+ base = op;
40263
+ }
40264
+ }
40265
+ }
40266
+ if (hasNegIndex && base) {
40267
+ const n = upper.add(ce.One).simplify();
40268
+ const result = ce.function("Divide", [
40269
+ ce.function("Factorial", [base]),
40270
+ ce.function("Factorial", [base.sub(n)])
40271
+ ]);
40272
+ return { value: result, because: "falling factorial" };
40273
+ }
40274
+ }
40275
+ if (body.operator === "Multiply" && body.ops) {
40276
+ const constantFactors = [];
40277
+ const indexFactors = [];
40278
+ for (const factor2 of body.ops) {
40279
+ const factorUnknowns = new Set(factor2.unknowns);
40280
+ if (factorUnknowns.has(index)) {
40281
+ indexFactors.push(factor2);
40282
+ } else {
40283
+ constantFactors.push(factor2);
40284
+ }
40285
+ }
40286
+ if (constantFactors.length > 0 && indexFactors.length > 0) {
40287
+ const constant = constantFactors.length === 1 ? constantFactors[0] : ce.function("Multiply", constantFactors);
40288
+ const indexPart = indexFactors.length === 1 ? indexFactors[0] : ce.function("Multiply", indexFactors);
40289
+ const count = upper.sub(lower).add(ce.One).simplify();
40290
+ const newProduct = ce.function("Product", [indexPart, limits]);
40291
+ return {
40292
+ value: constant.pow(count).mul(newProduct),
40293
+ because: "factor out constant from product"
40294
+ };
40295
+ }
40296
+ }
40297
+ return void 0;
40298
+ }
40299
+
38718
40300
  // src/compute-engine/symbolic/simplify-rules.ts
38719
40301
  var SIMPLIFY_RULES = [
38720
40302
  // The Golden Ratio, a constant that can be simplified
38721
40303
  "\\varphi -> \\frac{1+\\sqrt{5}}{2}",
38722
40304
  simplifyRelationalOperator,
38723
40305
  simplifySystemOfEquations,
40306
+ //
40307
+ // Cancel common polynomial factors in Divide expressions
40308
+ // e.g., (x² - 1)/(x - 1) → x + 1
40309
+ // Must run before expand to preserve polynomial structure
40310
+ //
40311
+ (x) => {
40312
+ if (x.operator !== "Divide") return void 0;
40313
+ const unknowns = x.unknowns;
40314
+ if (unknowns.length !== 1) return void 0;
40315
+ const variable = unknowns[0];
40316
+ const result = cancelCommonFactors(x, variable);
40317
+ if (result.isSame(x)) return void 0;
40318
+ return { value: result, because: "cancel common polynomial factors" };
40319
+ },
38724
40320
  // Try to expand the expression:
38725
40321
  // x*(y+z) -> x*y + x*z
38726
40322
  // { replace: (x) => expand(x) ?? undefined, id: 'expand' },
@@ -38810,7 +40406,8 @@ var SIMPLIFY_RULES = [
38810
40406
  const ce = x.engine;
38811
40407
  if (s === void 0) return void 0;
38812
40408
  if (s === "positive") return { value: ce.One, because: "sign positive" };
38813
- if (s === "negative") return { value: ce.One, because: "sign negative" };
40409
+ if (s === "negative")
40410
+ return { value: ce.NegativeOne, because: "sign negative" };
38814
40411
  if (s === "zero") return { value: ce.Zero, because: "sign zero" };
38815
40412
  if (s === "unsigned") return { value: ce.NaN, because: "sign unsinged" };
38816
40413
  return void 0;
@@ -38893,14 +40490,10 @@ var SIMPLIFY_RULES = [
38893
40490
  because: "congruent"
38894
40491
  };
38895
40492
  },
38896
- //
38897
- // Product, Sum
38898
- //
38899
- (x) => {
38900
- if (x.operator === "Max") {
38901
- }
38902
- return void 0;
38903
- },
40493
+ // Sum simplification (extracted to simplify-sum.ts)
40494
+ simplifySum,
40495
+ // Product simplification (extracted to simplify-product.ts)
40496
+ simplifyProduct,
38904
40497
  //
38905
40498
  // Constructible values of trig functions
38906
40499
  //
@@ -41126,7 +42719,8 @@ var ComputeEngine = class _ComputeEngine {
41126
42719
  return BoxedType.unknown;
41127
42720
  },
41128
42721
  parseUnexpectedToken: (_lhs, _parser) => null,
41129
- preserveLatex: false
42722
+ preserveLatex: false,
42723
+ quantifierScope: "tight"
41130
42724
  };
41131
42725
  const result = parse2(
41132
42726
  asLatexString(latex) ?? latex,
@@ -41279,10 +42873,10 @@ function defToString(name, def, v) {
41279
42873
  }
41280
42874
 
41281
42875
  // src/compute-engine.ts
41282
- var version = "0.31.0";
42876
+ var version = "0.32.1";
41283
42877
  globalThis[Symbol.for("io.cortexjs.compute-engine")] = {
41284
42878
  ComputeEngine: ComputeEngine.prototype.constructor,
41285
- version: "0.31.0"
42879
+ version: "0.32.1"
41286
42880
  };
41287
42881
  export {
41288
42882
  BoxedType,