@cortex-js/compute-engine 0.56.0 → 0.58.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (259) hide show
  1. package/dist/compile.esm.js +1137 -92
  2. package/dist/compile.min.esm.js +261 -61
  3. package/dist/compile.min.umd.cjs +262 -62
  4. package/dist/compile.umd.cjs +1137 -92
  5. package/dist/compute-engine.esm.js +1751 -176
  6. package/dist/compute-engine.min.esm.js +273 -73
  7. package/dist/compute-engine.min.umd.cjs +272 -72
  8. package/dist/compute-engine.umd.cjs +1751 -176
  9. package/dist/core.esm.js +1750 -175
  10. package/dist/core.min.esm.js +271 -71
  11. package/dist/core.min.umd.cjs +271 -71
  12. package/dist/core.umd.cjs +1750 -175
  13. package/dist/interval.esm.js +357 -28
  14. package/dist/interval.min.esm.js +6 -6
  15. package/dist/interval.min.umd.cjs +6 -6
  16. package/dist/interval.umd.cjs +357 -28
  17. package/dist/latex-syntax.esm.js +398 -28
  18. package/dist/latex-syntax.min.esm.js +6 -6
  19. package/dist/latex-syntax.min.umd.cjs +6 -6
  20. package/dist/latex-syntax.umd.cjs +398 -28
  21. package/dist/math-json.esm.js +2 -2
  22. package/dist/math-json.min.esm.js +2 -2
  23. package/dist/math-json.min.umd.cjs +2 -2
  24. package/dist/math-json.umd.cjs +2 -2
  25. package/dist/numerics.esm.js +38 -3
  26. package/dist/numerics.min.esm.js +3 -3
  27. package/dist/numerics.min.umd.cjs +4 -4
  28. package/dist/numerics.umd.cjs +38 -3
  29. package/dist/types/big-decimal/big-decimal.d.ts +1 -1
  30. package/dist/types/big-decimal/index.d.ts +1 -1
  31. package/dist/types/big-decimal/transcendentals.d.ts +1 -1
  32. package/dist/types/big-decimal/utils.d.ts +1 -1
  33. package/dist/types/common/ansi-codes.d.ts +1 -1
  34. package/dist/types/common/configuration-change.d.ts +1 -1
  35. package/dist/types/common/fuzzy-string-match.d.ts +1 -1
  36. package/dist/types/common/grapheme-splitter.d.ts +1 -1
  37. package/dist/types/common/interruptible.d.ts +1 -1
  38. package/dist/types/common/one-of.d.ts +1 -1
  39. package/dist/types/common/signals.d.ts +1 -1
  40. package/dist/types/common/type/ast-nodes.d.ts +1 -1
  41. package/dist/types/common/type/boxed-type.d.ts +1 -1
  42. package/dist/types/common/type/lexer.d.ts +1 -1
  43. package/dist/types/common/type/parse.d.ts +1 -1
  44. package/dist/types/common/type/parser.d.ts +1 -1
  45. package/dist/types/common/type/primitive.d.ts +1 -1
  46. package/dist/types/common/type/reduce.d.ts +1 -1
  47. package/dist/types/common/type/serialize.d.ts +1 -1
  48. package/dist/types/common/type/subtype.d.ts +1 -1
  49. package/dist/types/common/type/type-builder.d.ts +1 -1
  50. package/dist/types/common/type/types.d.ts +1 -1
  51. package/dist/types/common/type/utils.d.ts +1 -1
  52. package/dist/types/common/utils.d.ts +1 -1
  53. package/dist/types/compile.d.ts +1 -1
  54. package/dist/types/compute-engine/assume.d.ts +1 -1
  55. package/dist/types/compute-engine/boxed-expression/abstract-boxed-expression.d.ts +8 -1
  56. package/dist/types/compute-engine/boxed-expression/apply.d.ts +1 -1
  57. package/dist/types/compute-engine/boxed-expression/arithmetic-add.d.ts +1 -1
  58. package/dist/types/compute-engine/boxed-expression/arithmetic-mul-div.d.ts +1 -1
  59. package/dist/types/compute-engine/boxed-expression/arithmetic-power.d.ts +1 -1
  60. package/dist/types/compute-engine/boxed-expression/ascii-math.d.ts +1 -1
  61. package/dist/types/compute-engine/boxed-expression/box.d.ts +1 -1
  62. package/dist/types/compute-engine/boxed-expression/boxed-dictionary.d.ts +1 -1
  63. package/dist/types/compute-engine/boxed-expression/boxed-function.d.ts +1 -1
  64. package/dist/types/compute-engine/boxed-expression/boxed-number.d.ts +1 -1
  65. package/dist/types/compute-engine/boxed-expression/boxed-operator-definition.d.ts +7 -1
  66. package/dist/types/compute-engine/boxed-expression/boxed-patterns.d.ts +1 -1
  67. package/dist/types/compute-engine/boxed-expression/boxed-string.d.ts +1 -1
  68. package/dist/types/compute-engine/boxed-expression/boxed-symbol.d.ts +1 -1
  69. package/dist/types/compute-engine/boxed-expression/boxed-tensor.d.ts +1 -1
  70. package/dist/types/compute-engine/boxed-expression/boxed-value-definition.d.ts +1 -1
  71. package/dist/types/compute-engine/boxed-expression/cache.d.ts +1 -1
  72. package/dist/types/compute-engine/boxed-expression/canonical-utils.d.ts +1 -1
  73. package/dist/types/compute-engine/boxed-expression/canonical.d.ts +1 -1
  74. package/dist/types/compute-engine/boxed-expression/compare.d.ts +1 -1
  75. package/dist/types/compute-engine/boxed-expression/constants.d.ts +1 -1
  76. package/dist/types/compute-engine/boxed-expression/expand.d.ts +1 -1
  77. package/dist/types/compute-engine/boxed-expression/expression-map.d.ts +1 -1
  78. package/dist/types/compute-engine/boxed-expression/factor.d.ts +1 -1
  79. package/dist/types/compute-engine/boxed-expression/flatten.d.ts +1 -1
  80. package/dist/types/compute-engine/boxed-expression/hold.d.ts +1 -1
  81. package/dist/types/compute-engine/boxed-expression/inequality-bounds.d.ts +22 -10
  82. package/dist/types/compute-engine/boxed-expression/init-lazy-refs.d.ts +1 -1
  83. package/dist/types/compute-engine/boxed-expression/invisible-operator.d.ts +1 -1
  84. package/dist/types/compute-engine/boxed-expression/match.d.ts +1 -1
  85. package/dist/types/compute-engine/boxed-expression/negate.d.ts +1 -1
  86. package/dist/types/compute-engine/boxed-expression/numerics.d.ts +1 -1
  87. package/dist/types/compute-engine/boxed-expression/order.d.ts +1 -1
  88. package/dist/types/compute-engine/boxed-expression/pattern-utils.d.ts +1 -1
  89. package/dist/types/compute-engine/boxed-expression/polynomial-degree.d.ts +1 -1
  90. package/dist/types/compute-engine/boxed-expression/polynomials.d.ts +1 -1
  91. package/dist/types/compute-engine/boxed-expression/predicates.d.ts +1 -1
  92. package/dist/types/compute-engine/boxed-expression/rules.d.ts +1 -1
  93. package/dist/types/compute-engine/boxed-expression/serialize.d.ts +1 -1
  94. package/dist/types/compute-engine/boxed-expression/sgn.d.ts +1 -1
  95. package/dist/types/compute-engine/boxed-expression/simplify.d.ts +1 -1
  96. package/dist/types/compute-engine/boxed-expression/solve-linear-system.d.ts +1 -1
  97. package/dist/types/compute-engine/boxed-expression/solve.d.ts +1 -1
  98. package/dist/types/compute-engine/boxed-expression/stochastic-equal.d.ts +1 -1
  99. package/dist/types/compute-engine/boxed-expression/trigonometry.d.ts +1 -1
  100. package/dist/types/compute-engine/boxed-expression/type-guards.d.ts +1 -1
  101. package/dist/types/compute-engine/boxed-expression/utils.d.ts +1 -1
  102. package/dist/types/compute-engine/boxed-expression/validate.d.ts +1 -1
  103. package/dist/types/compute-engine/collection-utils.d.ts +1 -1
  104. package/dist/types/compute-engine/compilation/base-compiler.d.ts +62 -6
  105. package/dist/types/compute-engine/compilation/compile-expression.d.ts +1 -1
  106. package/dist/types/compute-engine/compilation/constant-folding.d.ts +1 -1
  107. package/dist/types/compute-engine/compilation/glsl-target.d.ts +1 -1
  108. package/dist/types/compute-engine/compilation/gpu-target.d.ts +44 -1
  109. package/dist/types/compute-engine/compilation/interval-javascript-target.d.ts +1 -1
  110. package/dist/types/compute-engine/compilation/javascript-target.d.ts +1 -1
  111. package/dist/types/compute-engine/compilation/python-target.d.ts +1 -1
  112. package/dist/types/compute-engine/compilation/types.d.ts +1 -1
  113. package/dist/types/compute-engine/compilation/wgsl-target.d.ts +1 -1
  114. package/dist/types/compute-engine/cost-function.d.ts +1 -1
  115. package/dist/types/compute-engine/engine-assumptions.d.ts +1 -1
  116. package/dist/types/compute-engine/engine-cache.d.ts +1 -1
  117. package/dist/types/compute-engine/engine-common-symbols.d.ts +1 -1
  118. package/dist/types/compute-engine/engine-compilation-targets.d.ts +1 -1
  119. package/dist/types/compute-engine/engine-configuration-lifecycle.d.ts +1 -1
  120. package/dist/types/compute-engine/engine-declarations.d.ts +1 -1
  121. package/dist/types/compute-engine/engine-expression-entrypoints.d.ts +1 -1
  122. package/dist/types/compute-engine/engine-extension-contracts.d.ts +1 -1
  123. package/dist/types/compute-engine/engine-library-bootstrap.d.ts +1 -1
  124. package/dist/types/compute-engine/engine-numeric-configuration.d.ts +1 -1
  125. package/dist/types/compute-engine/engine-runtime-state.d.ts +4 -1
  126. package/dist/types/compute-engine/engine-scope.d.ts +1 -1
  127. package/dist/types/compute-engine/engine-sequences.d.ts +1 -1
  128. package/dist/types/compute-engine/engine-simplification-rules.d.ts +1 -1
  129. package/dist/types/compute-engine/engine-startup-coordinator.d.ts +1 -1
  130. package/dist/types/compute-engine/engine-type-resolver.d.ts +1 -1
  131. package/dist/types/compute-engine/engine-validation-entrypoints.d.ts +1 -1
  132. package/dist/types/compute-engine/free-functions.d.ts +1 -1
  133. package/dist/types/compute-engine/function-utils.d.ts +1 -1
  134. package/dist/types/compute-engine/global-types.d.ts +1 -1
  135. package/dist/types/compute-engine/index.d.ts +18 -2
  136. package/dist/types/compute-engine/interval/arithmetic.d.ts +1 -1
  137. package/dist/types/compute-engine/interval/comparison.d.ts +1 -1
  138. package/dist/types/compute-engine/interval/elementary.d.ts +1 -1
  139. package/dist/types/compute-engine/interval/index.d.ts +1 -1
  140. package/dist/types/compute-engine/interval/trigonometric.d.ts +1 -1
  141. package/dist/types/compute-engine/interval/types.d.ts +1 -1
  142. package/dist/types/compute-engine/interval/util.d.ts +1 -1
  143. package/dist/types/compute-engine/latex-syntax/dictionary/default-dictionary.d.ts +1 -1
  144. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-algebra.d.ts +1 -1
  145. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-arithmetic.d.ts +1 -1
  146. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-calculus.d.ts +1 -1
  147. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-colors.d.ts +1 -1
  148. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-complex.d.ts +1 -1
  149. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-core.d.ts +1 -1
  150. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-linear-algebra.d.ts +1 -1
  151. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-logic.d.ts +1 -1
  152. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-other.d.ts +1 -1
  153. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-relational-operators.d.ts +1 -1
  154. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-sets.d.ts +1 -1
  155. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-statistics.d.ts +1 -1
  156. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-symbols.d.ts +1 -1
  157. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-trigonometry.d.ts +1 -1
  158. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-units.d.ts +1 -1
  159. package/dist/types/compute-engine/latex-syntax/dictionary/definitions.d.ts +1 -1
  160. package/dist/types/compute-engine/latex-syntax/dictionary/indexed-types.d.ts +1 -1
  161. package/dist/types/compute-engine/latex-syntax/latex-syntax.d.ts +1 -1
  162. package/dist/types/compute-engine/latex-syntax/parse-number.d.ts +1 -1
  163. package/dist/types/compute-engine/latex-syntax/parse-symbol.d.ts +1 -1
  164. package/dist/types/compute-engine/latex-syntax/parse.d.ts +1 -1
  165. package/dist/types/compute-engine/latex-syntax/serialize-dms.d.ts +1 -1
  166. package/dist/types/compute-engine/latex-syntax/serialize-number.d.ts +1 -1
  167. package/dist/types/compute-engine/latex-syntax/serializer-style.d.ts +1 -1
  168. package/dist/types/compute-engine/latex-syntax/serializer.d.ts +1 -1
  169. package/dist/types/compute-engine/latex-syntax/tokenizer.d.ts +1 -1
  170. package/dist/types/compute-engine/latex-syntax/types.d.ts +40 -1
  171. package/dist/types/compute-engine/latex-syntax/utils.d.ts +1 -1
  172. package/dist/types/compute-engine/library/arithmetic.d.ts +1 -1
  173. package/dist/types/compute-engine/library/calculus.d.ts +1 -1
  174. package/dist/types/compute-engine/library/collections.d.ts +5 -3
  175. package/dist/types/compute-engine/library/colors.d.ts +1 -1
  176. package/dist/types/compute-engine/library/combinatorics.d.ts +1 -1
  177. package/dist/types/compute-engine/library/complex.d.ts +1 -1
  178. package/dist/types/compute-engine/library/control-structures.d.ts +1 -1
  179. package/dist/types/compute-engine/library/core.d.ts +1 -1
  180. package/dist/types/compute-engine/library/fractals.d.ts +1 -1
  181. package/dist/types/compute-engine/library/library.d.ts +1 -1
  182. package/dist/types/compute-engine/library/linear-algebra.d.ts +1 -1
  183. package/dist/types/compute-engine/library/logic-analysis.d.ts +1 -1
  184. package/dist/types/compute-engine/library/logic.d.ts +1 -1
  185. package/dist/types/compute-engine/library/number-theory.d.ts +1 -1
  186. package/dist/types/compute-engine/library/polynomials.d.ts +1 -1
  187. package/dist/types/compute-engine/library/quantity-arithmetic.d.ts +1 -1
  188. package/dist/types/compute-engine/library/random-expression.d.ts +1 -1
  189. package/dist/types/compute-engine/library/relational-operator.d.ts +1 -1
  190. package/dist/types/compute-engine/library/sets.d.ts +1 -1
  191. package/dist/types/compute-engine/library/statistics.d.ts +1 -1
  192. package/dist/types/compute-engine/library/trigonometry.d.ts +1 -1
  193. package/dist/types/compute-engine/library/type-handlers.d.ts +1 -1
  194. package/dist/types/compute-engine/library/unit-data.d.ts +1 -1
  195. package/dist/types/compute-engine/library/units.d.ts +1 -1
  196. package/dist/types/compute-engine/library/utils.d.ts +1 -1
  197. package/dist/types/compute-engine/numeric-value/big-numeric-value.d.ts +1 -1
  198. package/dist/types/compute-engine/numeric-value/exact-numeric-value.d.ts +1 -1
  199. package/dist/types/compute-engine/numeric-value/machine-numeric-value.d.ts +1 -1
  200. package/dist/types/compute-engine/numeric-value/types.d.ts +1 -1
  201. package/dist/types/compute-engine/numerics/bigint.d.ts +1 -1
  202. package/dist/types/compute-engine/numerics/expression.d.ts +1 -1
  203. package/dist/types/compute-engine/numerics/interval.d.ts +1 -1
  204. package/dist/types/compute-engine/numerics/linear-algebra.d.ts +1 -1
  205. package/dist/types/compute-engine/numerics/monte-carlo.d.ts +1 -1
  206. package/dist/types/compute-engine/numerics/numeric-bigint.d.ts +1 -1
  207. package/dist/types/compute-engine/numerics/numeric-bignum.d.ts +1 -1
  208. package/dist/types/compute-engine/numerics/numeric-complex.d.ts +1 -1
  209. package/dist/types/compute-engine/numerics/numeric.d.ts +1 -1
  210. package/dist/types/compute-engine/numerics/primes.d.ts +1 -1
  211. package/dist/types/compute-engine/numerics/random.d.ts +23 -0
  212. package/dist/types/compute-engine/numerics/rationals.d.ts +1 -1
  213. package/dist/types/compute-engine/numerics/richardson.d.ts +1 -1
  214. package/dist/types/compute-engine/numerics/special-functions.d.ts +1 -1
  215. package/dist/types/compute-engine/numerics/statistics.d.ts +1 -1
  216. package/dist/types/compute-engine/numerics/strings.d.ts +1 -1
  217. package/dist/types/compute-engine/numerics/types.d.ts +1 -1
  218. package/dist/types/compute-engine/numerics/unit-data.d.ts +1 -1
  219. package/dist/types/compute-engine/oeis.d.ts +1 -1
  220. package/dist/types/compute-engine/sequence.d.ts +1 -1
  221. package/dist/types/compute-engine/symbolic/antiderivative.d.ts +1 -1
  222. package/dist/types/compute-engine/symbolic/derivative.d.ts +1 -1
  223. package/dist/types/compute-engine/symbolic/distribute.d.ts +1 -1
  224. package/dist/types/compute-engine/symbolic/fu-cost.d.ts +1 -1
  225. package/dist/types/compute-engine/symbolic/fu-transforms.d.ts +1 -1
  226. package/dist/types/compute-engine/symbolic/fu.d.ts +1 -1
  227. package/dist/types/compute-engine/symbolic/logic-utils.d.ts +1 -1
  228. package/dist/types/compute-engine/symbolic/simplify-abs.d.ts +1 -1
  229. package/dist/types/compute-engine/symbolic/simplify-divide.d.ts +1 -1
  230. package/dist/types/compute-engine/symbolic/simplify-factorial.d.ts +1 -1
  231. package/dist/types/compute-engine/symbolic/simplify-hyperbolic.d.ts +1 -1
  232. package/dist/types/compute-engine/symbolic/simplify-infinity.d.ts +1 -1
  233. package/dist/types/compute-engine/symbolic/simplify-log.d.ts +1 -1
  234. package/dist/types/compute-engine/symbolic/simplify-logic.d.ts +1 -1
  235. package/dist/types/compute-engine/symbolic/simplify-power.d.ts +1 -1
  236. package/dist/types/compute-engine/symbolic/simplify-product.d.ts +1 -1
  237. package/dist/types/compute-engine/symbolic/simplify-rules.d.ts +1 -1
  238. package/dist/types/compute-engine/symbolic/simplify-sum.d.ts +1 -1
  239. package/dist/types/compute-engine/symbolic/simplify-trig.d.ts +1 -1
  240. package/dist/types/compute-engine/tensor/tensor-fields.d.ts +1 -1
  241. package/dist/types/compute-engine/tensor/tensors.d.ts +1 -1
  242. package/dist/types/compute-engine/types-definitions.d.ts +1 -1
  243. package/dist/types/compute-engine/types-engine.d.ts +51 -1
  244. package/dist/types/compute-engine/types-evaluation.d.ts +1 -1
  245. package/dist/types/compute-engine/types-expression.d.ts +69 -1
  246. package/dist/types/compute-engine/types-kernel-evaluation.d.ts +1 -1
  247. package/dist/types/compute-engine/types-kernel-serialization.d.ts +1 -1
  248. package/dist/types/compute-engine/types-serialization.d.ts +1 -1
  249. package/dist/types/compute-engine/types.d.ts +1 -1
  250. package/dist/types/compute-engine.d.ts +1 -1
  251. package/dist/types/core.d.ts +1 -1
  252. package/dist/types/interval.d.ts +1 -1
  253. package/dist/types/latex-syntax.d.ts +2 -2
  254. package/dist/types/math-json/symbols.d.ts +1 -1
  255. package/dist/types/math-json/types.d.ts +1 -1
  256. package/dist/types/math-json/utils.d.ts +1 -1
  257. package/dist/types/math-json.d.ts +2 -2
  258. package/dist/types/numerics.d.ts +1 -1
  259. package/package.json +1 -1
package/dist/core.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- /** Compute Engine 0.56.0 */
1
+ /** Compute Engine 0.58.0 */
2
2
 
3
3
  // node_modules/complex-esm/dist/src/complex.js
4
4
  var cosh = Math.cosh || function(x) {
@@ -4268,7 +4268,42 @@ function widen2(a, b) {
4268
4268
  if (b === "nothing") return a;
4269
4269
  if (isSubtype(a, b)) return b;
4270
4270
  if (isSubtype(b, a)) return a;
4271
- return superType(a, b);
4271
+ const sup = superType(a, b);
4272
+ if (LOSSY_SUPERTYPE.has(sup)) return unionTypes(a, b);
4273
+ return sup;
4274
+ }
4275
+ var LOSSY_SUPERTYPE = /* @__PURE__ */ new Set([
4276
+ "scalar",
4277
+ "value",
4278
+ "function",
4279
+ "expression",
4280
+ "collection",
4281
+ "indexed_collection",
4282
+ "list",
4283
+ "set",
4284
+ "tuple",
4285
+ "record",
4286
+ "dictionary",
4287
+ "map",
4288
+ "any"
4289
+ ]);
4290
+ function unionTypes(a, b) {
4291
+ const members = [];
4292
+ const push = (t) => {
4293
+ if (typeof t === "object" && t.kind === "union") {
4294
+ for (const m of t.types) push(m);
4295
+ return;
4296
+ }
4297
+ const key = typeof t === "string" ? t : JSON.stringify(t);
4298
+ if (!members.some(
4299
+ (m) => (typeof m === "string" ? m : JSON.stringify(m)) === key
4300
+ ))
4301
+ members.push(t);
4302
+ };
4303
+ push(a);
4304
+ push(b);
4305
+ if (members.length === 1) return members[0];
4306
+ return { kind: "union", types: members };
4272
4307
  }
4273
4308
  function narrow(...types) {
4274
4309
  if (types.length === 0) return "nothing";
@@ -4346,7 +4381,7 @@ function collectionElementType(type2) {
4346
4381
  if (type2.kind === "set") return type2.elements;
4347
4382
  if (type2.kind === "tuple") return widen(...type2.elements.map((x) => x.type));
4348
4383
  if (type2.kind === "dictionary")
4349
- return parseType(`tuple<string, ${type2.values}>`);
4384
+ return parseType(`tuple<string, ${typeToString(type2.values)}>`);
4350
4385
  if (type2.kind === "record") {
4351
4386
  return parseType(
4352
4387
  `tuple<string, ${typeToString(widen(...Object.values(type2.elements)))}>`
@@ -5892,6 +5927,84 @@ function sym(expr2) {
5892
5927
  }
5893
5928
 
5894
5929
  // src/compute-engine/boxed-expression/inequality-bounds.ts
5930
+ function extractIntervalBounds(expr2, symbol2) {
5931
+ if (isFunction2(expr2, "When")) {
5932
+ const cond = expr2.op2;
5933
+ if (!cond) return void 0;
5934
+ return extractIntervalBounds(cond, symbol2);
5935
+ }
5936
+ if (isFunction2(expr2, "Multiply")) {
5937
+ const ops = expr2.ops;
5938
+ if (!ops) return void 0;
5939
+ const merged = {};
5940
+ for (const sub2 of ops) {
5941
+ if (isFunction2(sub2, "When")) {
5942
+ const sub_ = extractIntervalBounds(sub2, symbol2);
5943
+ if (sub_ !== void 0) _mergeBounds(merged, sub_);
5944
+ }
5945
+ }
5946
+ return _hasAnyBound(merged) ? merged : void 0;
5947
+ }
5948
+ const result = {};
5949
+ if (isFunction2(expr2, "And")) {
5950
+ const ops = expr2.ops;
5951
+ if (!ops) return void 0;
5952
+ for (const sub2 of ops) {
5953
+ const subBounds = extractIntervalBounds(sub2, symbol2);
5954
+ if (subBounds === void 0) continue;
5955
+ _mergeBounds(result, subBounds);
5956
+ }
5957
+ return _hasAnyBound(result) ? result : void 0;
5958
+ }
5959
+ const op = expr2.operator;
5960
+ if ((op === "Less" || op === "LessEqual" || op === "Greater" || op === "GreaterEqual") && isFunction2(expr2)) {
5961
+ const isStrict = op === "Less" || op === "Greater";
5962
+ const ops = expr2.ops;
5963
+ if (!ops || ops.length < 2) return void 0;
5964
+ const flipped = op === "Greater" || op === "GreaterEqual" ? [...ops].reverse() : ops;
5965
+ for (let i = 0; i < flipped.length; i++) {
5966
+ if (isSymbol2(flipped[i], symbol2)) {
5967
+ if (i > 0) {
5968
+ const candidate = flipped[i - 1];
5969
+ if (result.lower === void 0 || candidate.isGreater(result.lower) === true) {
5970
+ result.lower = candidate;
5971
+ result.lowerStrict = isStrict;
5972
+ }
5973
+ }
5974
+ if (i < flipped.length - 1) {
5975
+ const candidate = flipped[i + 1];
5976
+ if (result.upper === void 0 || candidate.isLess(result.upper) === true) {
5977
+ result.upper = candidate;
5978
+ result.upperStrict = isStrict;
5979
+ }
5980
+ }
5981
+ }
5982
+ }
5983
+ return _hasAnyBound(result) ? result : void 0;
5984
+ }
5985
+ return void 0;
5986
+ }
5987
+ function _mergeBounds(into, from) {
5988
+ if (from.lower !== void 0) {
5989
+ if (into.lower === void 0 || from.lower.isGreater(into.lower) === true) {
5990
+ into.lower = from.lower;
5991
+ into.lowerStrict = from.lowerStrict;
5992
+ } else if (from.lower.isSame(into.lower)) {
5993
+ into.lowerStrict = into.lowerStrict || from.lowerStrict;
5994
+ }
5995
+ }
5996
+ if (from.upper !== void 0) {
5997
+ if (into.upper === void 0 || from.upper.isLess(into.upper) === true) {
5998
+ into.upper = from.upper;
5999
+ into.upperStrict = from.upperStrict;
6000
+ } else if (from.upper.isSame(into.upper)) {
6001
+ into.upperStrict = into.upperStrict || from.upperStrict;
6002
+ }
6003
+ }
6004
+ }
6005
+ function _hasAnyBound(b) {
6006
+ return b.lower !== void 0 || b.upper !== void 0;
6007
+ }
5895
6008
  function getInequalityBoundsFromAssumptions(ce, symbol2) {
5896
6009
  const result = {};
5897
6010
  const assumptions = ce.context?.assumptions;
@@ -5908,8 +6021,8 @@ function getInequalityBoundsFromAssumptions(ce, symbol2) {
5908
6021
  const isStrict = op === "Less";
5909
6022
  if (isFunction2(lhs, "Negate") && isSymbol2(lhs.op1, symbol2)) {
5910
6023
  const bound = ce.Zero;
5911
- if (result.lowerBound === void 0 || bound.isGreater(result.lowerBound) === true) {
5912
- result.lowerBound = bound;
6024
+ if (result.lower === void 0 || bound.isGreater(result.lower) === true) {
6025
+ result.lower = bound;
5913
6026
  result.lowerStrict = isStrict;
5914
6027
  }
5915
6028
  }
@@ -5928,16 +6041,16 @@ function getInequalityBoundsFromAssumptions(ce, symbol2) {
5928
6041
  }
5929
6042
  if (hasNegatedSymbol && constantSum !== 0) {
5930
6043
  const bound = ce.expr(constantSum);
5931
- if (result.lowerBound === void 0 || bound.isGreater(result.lowerBound) === true) {
5932
- result.lowerBound = bound;
6044
+ if (result.lower === void 0 || bound.isGreater(result.lower) === true) {
6045
+ result.lower = bound;
5933
6046
  result.lowerStrict = isStrict;
5934
6047
  }
5935
6048
  }
5936
6049
  }
5937
6050
  if (isSymbol2(lhs, symbol2)) {
5938
6051
  const bound = ce.Zero;
5939
- if (result.upperBound === void 0 || bound.isLess(result.upperBound) === true) {
5940
- result.upperBound = bound;
6052
+ if (result.upper === void 0 || bound.isLess(result.upper) === true) {
6053
+ result.upper = bound;
5941
6054
  result.upperStrict = isStrict;
5942
6055
  }
5943
6056
  }
@@ -5956,8 +6069,8 @@ function getInequalityBoundsFromAssumptions(ce, symbol2) {
5956
6069
  }
5957
6070
  if (hasSymbol && constantSum !== 0) {
5958
6071
  const bound = ce.expr(-constantSum);
5959
- if (result.upperBound === void 0 || bound.isLess(result.upperBound) === true) {
5960
- result.upperBound = bound;
6072
+ if (result.upper === void 0 || bound.isLess(result.upper) === true) {
6073
+ result.upper = bound;
5961
6074
  result.upperStrict = isStrict;
5962
6075
  }
5963
6076
  }
@@ -6177,8 +6290,8 @@ function cmp(a, b) {
6177
6290
  const bounds = getInequalityBoundsFromAssumptions(a.engine, b.symbol);
6178
6291
  const aNum = typeof a.numericValue === "number" ? a.numericValue : a.numericValue.re;
6179
6292
  if (aNum !== void 0 && Number.isFinite(aNum)) {
6180
- if (bounds.lowerBound !== void 0) {
6181
- const lb = bounds.lowerBound;
6293
+ if (bounds.lower !== void 0) {
6294
+ const lb = bounds.lower;
6182
6295
  const lowerNum = isNumber(lb) ? typeof lb.numericValue === "number" ? lb.numericValue : lb.numericValue.re : void 0;
6183
6296
  if (lowerNum !== void 0 && Number.isFinite(lowerNum)) {
6184
6297
  if (lowerNum > aNum) return "<";
@@ -6186,8 +6299,8 @@ function cmp(a, b) {
6186
6299
  if (lowerNum === aNum && !bounds.lowerStrict) return "<=";
6187
6300
  }
6188
6301
  }
6189
- if (bounds.upperBound !== void 0) {
6190
- const ub = bounds.upperBound;
6302
+ if (bounds.upper !== void 0) {
6303
+ const ub = bounds.upper;
6191
6304
  const upperNum = isNumber(ub) ? typeof ub.numericValue === "number" ? ub.numericValue : ub.numericValue.re : void 0;
6192
6305
  if (upperNum !== void 0 && Number.isFinite(upperNum)) {
6193
6306
  if (upperNum < aNum) return ">";
@@ -6217,8 +6330,8 @@ function cmp(a, b) {
6217
6330
  if (typeof b === "number") {
6218
6331
  if (isSymbol2(a)) {
6219
6332
  const bounds = getInequalityBoundsFromAssumptions(a.engine, a.symbol);
6220
- if (bounds.lowerBound !== void 0) {
6221
- const lb = bounds.lowerBound;
6333
+ if (bounds.lower !== void 0) {
6334
+ const lb = bounds.lower;
6222
6335
  const lowerNum = isNumber(lb) ? typeof lb.numericValue === "number" ? lb.numericValue : lb.numericValue.re : void 0;
6223
6336
  if (lowerNum !== void 0 && Number.isFinite(lowerNum)) {
6224
6337
  if (lowerNum > b) return ">";
@@ -6226,8 +6339,8 @@ function cmp(a, b) {
6226
6339
  if (lowerNum === b && !bounds.lowerStrict) return ">=";
6227
6340
  }
6228
6341
  }
6229
- if (bounds.upperBound !== void 0) {
6230
- const ub = bounds.upperBound;
6342
+ if (bounds.upper !== void 0) {
6343
+ const ub = bounds.upper;
6231
6344
  const upperNum = isNumber(ub) ? typeof ub.numericValue === "number" ? ub.numericValue : ub.numericValue.re : void 0;
6232
6345
  if (upperNum !== void 0 && Number.isFinite(upperNum)) {
6233
6346
  if (upperNum < b) return "<";
@@ -6283,8 +6396,8 @@ function cmp(a, b) {
6283
6396
  const bounds = getInequalityBoundsFromAssumptions(a.engine, a.symbol);
6284
6397
  const bNum = typeof b.numericValue === "number" ? b.numericValue : b.numericValue.re;
6285
6398
  if (bNum !== void 0 && Number.isFinite(bNum)) {
6286
- if (bounds.lowerBound !== void 0) {
6287
- const lb = bounds.lowerBound;
6399
+ if (bounds.lower !== void 0) {
6400
+ const lb = bounds.lower;
6288
6401
  const lowerNum = isNumber(lb) ? typeof lb.numericValue === "number" ? lb.numericValue : lb.numericValue.re : void 0;
6289
6402
  if (lowerNum !== void 0 && Number.isFinite(lowerNum)) {
6290
6403
  if (lowerNum > bNum) return ">";
@@ -6292,8 +6405,8 @@ function cmp(a, b) {
6292
6405
  if (lowerNum === bNum && !bounds.lowerStrict) return ">=";
6293
6406
  }
6294
6407
  }
6295
- if (bounds.upperBound !== void 0) {
6296
- const ub = bounds.upperBound;
6408
+ if (bounds.upper !== void 0) {
6409
+ const ub = bounds.upper;
6297
6410
  const upperNum = isNumber(ub) ? typeof ub.numericValue === "number" ? ub.numericValue : ub.numericValue.re : void 0;
6298
6411
  if (upperNum !== void 0 && Number.isFinite(upperNum)) {
6299
6412
  if (upperNum < bNum) return "<";
@@ -6831,6 +6944,12 @@ var _BoxedOperatorDefinition = class {
6831
6944
  scoped = false;
6832
6945
  signature;
6833
6946
  inferredSignature = true;
6947
+ /** True if this operator definition was created from a user-defined
6948
+ * function literal (e.g. via `ce.assign('f', ce.parse('x \\mapsto x^2'))`).
6949
+ * Used to enable auto-broadcasting when applied to indexed collections.
6950
+ * @internal
6951
+ */
6952
+ _isLambda = false;
6834
6953
  type;
6835
6954
  sgn;
6836
6955
  eq;
@@ -6988,6 +7107,8 @@ var _BoxedOperatorDefinition = class {
6988
7107
  this.engine._typeResolver
6989
7108
  );
6990
7109
  }
7110
+ if (isFunction2(boxedFn) && boxedFn.operator === "Function")
7111
+ this._isLambda = true;
6991
7112
  const fn = applicable(boxedFn);
6992
7113
  evaluate2 = (xs, _options) => fn(xs);
6993
7114
  Object.defineProperty(evaluate2, "toString", {
@@ -7838,8 +7959,15 @@ var _BoxedExpression = class __BoxedExpression {
7838
7959
  *
7839
7960
  * Numeric values are rounded to `ce.precision` significant digits
7840
7961
  * (via `fractionalDigits: 'auto'`).
7962
+ *
7963
+ * If `options.verbatim` is `true` and `verbatimLatex` is set on this
7964
+ * expression (i.e. it was parsed with `preserveLatex: true`), return
7965
+ * the verbatim source instead of re-serializing. Falls through to
7966
+ * re-serialization if no verbatim is available.
7841
7967
  */
7842
7968
  toLatex(options) {
7969
+ if (options?.verbatim === true && this.verbatimLatex !== void 0)
7970
+ return this.verbatimLatex;
7843
7971
  if (this.isLazyCollection) {
7844
7972
  const materialized = this.evaluate({
7845
7973
  materialization: options?.materialization ?? true
@@ -8297,6 +8425,29 @@ var _BoxedExpression = class __BoxedExpression {
8297
8425
  get isReal() {
8298
8426
  return void 0;
8299
8427
  }
8428
+ toSignedFunction() {
8429
+ const op = this.operator;
8430
+ if (op === void 0 || this.ops === void 0 || this.ops.length < 2) {
8431
+ return void 0;
8432
+ }
8433
+ const [lhs, rhs] = this.ops;
8434
+ const engine = this.engine;
8435
+ switch (op) {
8436
+ case "Equal":
8437
+ case "NotEqual":
8438
+ case "Less":
8439
+ case "LessEqual":
8440
+ return engine.function("Subtract", [lhs, rhs]);
8441
+ case "Greater":
8442
+ case "GreaterEqual":
8443
+ return engine.function("Subtract", [rhs, lhs]);
8444
+ default:
8445
+ return void 0;
8446
+ }
8447
+ }
8448
+ getInterval(symbol2) {
8449
+ return extractIntervalBounds(this, symbol2);
8450
+ }
8300
8451
  simplify(_options) {
8301
8452
  return this;
8302
8453
  }
@@ -9982,6 +10133,64 @@ function parseQuantifier(kind) {
9982
10133
  }
9983
10134
 
9984
10135
  // src/compute-engine/latex-syntax/dictionary/definitions-core.ts
10136
+ var COMPONENT_ACCESS_HEADS = {
10137
+ x: "First",
10138
+ y: "Second",
10139
+ z: "Third",
10140
+ real: "Real",
10141
+ re: "Real",
10142
+ imag: "Imaginary",
10143
+ im: "Imaginary",
10144
+ count: "Length",
10145
+ total: "Sum",
10146
+ max: "Max",
10147
+ min: "Min"
10148
+ };
10149
+ function memberHead(name) {
10150
+ return COMPONENT_ACCESS_HEADS[name] ?? null;
10151
+ }
10152
+ function parseComponentAccess(parser, lhs) {
10153
+ parser.skipVisualSpace();
10154
+ if (parser.match("\\operatorname")) {
10155
+ const name = parser.parseStringGroup();
10156
+ if (name === null) return null;
10157
+ const head = memberHead(name.trim());
10158
+ if (head === null) return null;
10159
+ return [head, lhs];
10160
+ }
10161
+ const tok = parser.peek;
10162
+ if (typeof tok === "string" && tok.startsWith("\\")) {
10163
+ const bare = tok.slice(1);
10164
+ const head = memberHead(bare);
10165
+ if (head !== null) {
10166
+ parser.nextToken();
10167
+ return [head, lhs];
10168
+ }
10169
+ return null;
10170
+ }
10171
+ if (typeof tok === "string" && /^[a-zA-Z]$/.test(tok)) {
10172
+ const head = memberHead(tok);
10173
+ if (head === null) return null;
10174
+ parser.nextToken();
10175
+ return [head, lhs];
10176
+ }
10177
+ return null;
10178
+ }
10179
+ function parseWhenRestriction(parser, lhs, close) {
10180
+ parser.addBoundary(close);
10181
+ parser.skipVisualSpace();
10182
+ const cond = parser.parseExpression({ minPrec: 0 });
10183
+ if (cond === null) {
10184
+ parser.removeBoundary();
10185
+ return null;
10186
+ }
10187
+ parser.skipVisualSpace();
10188
+ if (!parser.matchBoundary()) {
10189
+ parser.removeBoundary();
10190
+ return null;
10191
+ }
10192
+ return ["When", lhs, cond];
10193
+ }
9985
10194
  function parseSequence(parser, terminator, lhs, prec, sep) {
9986
10195
  if (terminator && terminator.minPrec >= prec) return null;
9987
10196
  const result = lhs ? [lhs] : ["Nothing"];
@@ -10257,15 +10466,16 @@ var DEFINITIONS_CORE = [
10257
10466
  precedence: ASSIGNMENT_PRECEDENCE,
10258
10467
  parse: parseAssign
10259
10468
  },
10260
- // General colon operator (type annotation, mapping notation)
10261
- // Precedence below assignment (260) so `:=` takes priority,
10262
- // and below arrows (270) so `f: A \to B` parses as `Colon(f, To(A, B))`
10469
+ // General colon operator (type annotation, mapping notation, Desmos piecewise)
10470
+ // Precedence below comparisons (245) so `cond : val` (Desmos compact piecewise)
10471
+ // parses as `Colon(cond, val)`, and below arrows (270) so
10472
+ // `f: A \to B` parses as `Colon(f, To(A, B))`.
10263
10473
  {
10264
10474
  name: "Colon",
10265
10475
  latexTrigger: ":",
10266
10476
  kind: "infix",
10267
10477
  associativity: "right",
10268
- precedence: 250,
10478
+ precedence: 240,
10269
10479
  serialize: (serializer, expr2) => joinLatex([
10270
10480
  serializer.serialize(operand(expr2, 1)),
10271
10481
  "\\colon",
@@ -10276,7 +10486,7 @@ var DEFINITIONS_CORE = [
10276
10486
  latexTrigger: "\\colon",
10277
10487
  kind: "infix",
10278
10488
  associativity: "right",
10279
- precedence: 250,
10489
+ precedence: 240,
10280
10490
  parse: "Colon"
10281
10491
  },
10282
10492
  {
@@ -10453,6 +10663,15 @@ var DEFINITIONS_CORE = [
10453
10663
  }
10454
10664
  },
10455
10665
  { name: "LatexTokens", serialize: serializeLatexTokens },
10666
+ // Component-access postfix: expr.member (C3)
10667
+ // The '.' trigger is consumed before the parse function is called.
10668
+ // Precedence 850 > 810 (At/indexing) so .x chains tightly.
10669
+ {
10670
+ kind: "postfix",
10671
+ precedence: 850,
10672
+ latexTrigger: ["."],
10673
+ parse: parseComponentAccess
10674
+ },
10456
10675
  {
10457
10676
  name: "At",
10458
10677
  kind: "postfix",
@@ -10473,6 +10692,29 @@ var DEFINITIONS_CORE = [
10473
10692
  latexTrigger: ["\\left", "\\lbrack"],
10474
10693
  parse: parseAt("\\right", "\\rbrack")
10475
10694
  },
10695
+ // When-restriction: `expr\left\{cond\right\}` → `When(expr, cond)` (D3)
10696
+ {
10697
+ name: "When",
10698
+ kind: "postfix",
10699
+ precedence: 800,
10700
+ latexTrigger: ["\\left", "\\{"],
10701
+ parse: (parser, lhs) => parseWhenRestriction(parser, lhs, ["\\right", "\\}"]),
10702
+ serialize: (serializer, expr2) => {
10703
+ const e = operand(expr2, 1);
10704
+ const cond = operand(expr2, 2);
10705
+ if (!e || !cond) return "";
10706
+ const clauses = operator(cond) === "And" ? operands(cond) ?? [] : [cond];
10707
+ const inner = clauses.map((c) => `\\left\\{${serializer.serialize(c)}\\right\\}`).join("");
10708
+ return `${serializer.serialize(e)}${inner}`;
10709
+ }
10710
+ },
10711
+ // When-restriction: bare `expr\{cond\}` → `When(expr, cond)`
10712
+ {
10713
+ kind: "postfix",
10714
+ precedence: 800,
10715
+ latexTrigger: ["\\{"],
10716
+ parse: (parser, lhs) => parseWhenRestriction(parser, lhs, ["\\}"])
10717
+ },
10476
10718
  {
10477
10719
  kind: "postfix",
10478
10720
  latexTrigger: ["_"],
@@ -10555,6 +10797,29 @@ var DEFINITIONS_CORE = [
10555
10797
  return "";
10556
10798
  }
10557
10799
  },
10800
+ // Additional triggers for Range: `...`, `\ldots`, and `\dots` are
10801
+ // equivalent to `..` when used as infix operators (e.g. `[1...9]`).
10802
+ // No `name` field here — names must be unique per the dictionary rules;
10803
+ // the first Range entry owns the name. When there is no LHS the symbol
10804
+ // entries near the top of the file still fire (ContinuationPlaceholder).
10805
+ {
10806
+ latexTrigger: [".", ".", "."],
10807
+ kind: "infix",
10808
+ precedence: 800,
10809
+ parse: parseRange
10810
+ },
10811
+ {
10812
+ latexTrigger: ["\\ldots"],
10813
+ kind: "infix",
10814
+ precedence: 800,
10815
+ parse: parseRange
10816
+ },
10817
+ {
10818
+ latexTrigger: ["\\dots"],
10819
+ kind: "infix",
10820
+ precedence: 800,
10821
+ parse: parseRange
10822
+ },
10558
10823
  {
10559
10824
  latexTrigger: [";"],
10560
10825
  kind: "infix",
@@ -10739,13 +11004,24 @@ var DEFINITIONS_CORE = [
10739
11004
  const args = operands(expr2);
10740
11005
  if (!args || args.length < 2) return "";
10741
11006
  const body = args[0];
10742
- const indexing = args[1];
10743
- if (operator(indexing) === "Element") {
10744
- const index = operand(indexing, 1);
10745
- const range2 = operand(indexing, 2);
10746
- if (operator(range2) === "Range") {
10747
- const lo = operand(range2, 1);
10748
- const hi = operand(range2, 2);
11007
+ const elements = args.slice(1);
11008
+ const allElements = elements.every((e) => operator(e) === "Element");
11009
+ if (!allElements) {
11010
+ return joinLatex([
11011
+ "\\operatorname{Loop}(",
11012
+ serializer.serialize(body),
11013
+ ", ",
11014
+ serializer.serialize(elements[0]),
11015
+ ")"
11016
+ ]);
11017
+ }
11018
+ if (elements.length === 1) {
11019
+ const elem = elements[0];
11020
+ const index = operand(elem, 1);
11021
+ const coll = operand(elem, 2);
11022
+ if (operator(coll) === "Range") {
11023
+ const lo = operand(coll, 1);
11024
+ const hi = operand(coll, 2);
10749
11025
  return joinLatex([
10750
11026
  "\\text{for }",
10751
11027
  serializer.serialize(index),
@@ -10757,13 +11033,27 @@ var DEFINITIONS_CORE = [
10757
11033
  serializer.serialize(body)
10758
11034
  ]);
10759
11035
  }
11036
+ return joinLatex([
11037
+ serializer.serialize(body),
11038
+ " \\operatorname{for} ",
11039
+ serializer.serialize(index),
11040
+ " = ",
11041
+ serializer.serialize(coll)
11042
+ ]);
10760
11043
  }
11044
+ const bindings = elements.map((elem) => {
11045
+ const name = operand(elem, 1);
11046
+ const coll = operand(elem, 2);
11047
+ return joinLatex([
11048
+ serializer.serialize(name),
11049
+ " = ",
11050
+ serializer.serialize(coll)
11051
+ ]);
11052
+ }).join(", ");
10761
11053
  return joinLatex([
10762
- "\\operatorname{Loop}(",
10763
11054
  serializer.serialize(body),
10764
- ", ",
10765
- serializer.serialize(indexing),
10766
- ")"
11055
+ " \\operatorname{for} ",
11056
+ bindings
10767
11057
  ]);
10768
11058
  }
10769
11059
  },
@@ -10796,6 +11086,18 @@ var DEFINITIONS_CORE = [
10796
11086
  precedence: 245,
10797
11087
  parse: (parser, until) => parseForExpression(parser, until)
10798
11088
  },
11089
+ // \operatorname{for} as postfix infix (list comprehension):
11090
+ // `body \operatorname{for} x = L_1, y = L_2`
11091
+ // Precedence 19 — just below comma (20) so the body is allowed to use
11092
+ // any operator (including comma sequencing) up to the keyword, and the
11093
+ // bindings can be comma-separated below us.
11094
+ {
11095
+ symbolTrigger: "for",
11096
+ kind: "infix",
11097
+ associativity: "none",
11098
+ precedence: 19,
11099
+ parse: (parser, lhs, until) => parseForComprehension(parser, lhs, until)
11100
+ },
10799
11101
  // \operatorname{break}
10800
11102
  {
10801
11103
  symbolTrigger: "break",
@@ -11000,7 +11302,10 @@ var DEFINITIONS_CORE = [
11000
11302
  if (!sym2 || !parser.getSymbolType(sym2).matches("function")) return null;
11001
11303
  parser.addBoundary([")"]);
11002
11304
  const expr2 = parser.parseExpression(until);
11003
- if (!parser.matchBoundary()) return null;
11305
+ if (!parser.matchBoundary()) {
11306
+ parser.removeBoundary();
11307
+ return null;
11308
+ }
11004
11309
  if (!parser.match("<}>")) return null;
11005
11310
  return ["Derivative", lhs, expr2];
11006
11311
  }
@@ -11441,7 +11746,12 @@ function parseBrackets(parser, body) {
11441
11746
  if (isEmptySequence(body)) return ["List"];
11442
11747
  const h = operator(body);
11443
11748
  if (h === "Range" || h === "Linspace") return body;
11444
- if (h === "Sequence") return ["List", ...operands(body)];
11749
+ if (h === "Sequence") {
11750
+ const elems = operands(body);
11751
+ const inferred = tryInferRangeFromElements(elems, parser);
11752
+ if (inferred) return inferred;
11753
+ return ["List", ...elems];
11754
+ }
11445
11755
  if (h === "Delimiter") {
11446
11756
  const delim = stringValue(operand(body, 2)) ?? "...";
11447
11757
  if (delim === ";" || delim === ".;.") {
@@ -11454,12 +11764,37 @@ function parseBrackets(parser, body) {
11454
11764
  }
11455
11765
  if (delim === "," || delim === ".,.") {
11456
11766
  body = operand(body, 1);
11457
- if (operator(body) === "Sequence") return ["List", ...operands(body)];
11767
+ if (operator(body) === "Sequence") {
11768
+ const elems = operands(body);
11769
+ const inferred = tryInferRangeFromElements(elems, parser);
11770
+ if (inferred) return inferred;
11771
+ return ["List", ...elems];
11772
+ }
11458
11773
  return ["List", body ?? "Nothing"];
11459
11774
  }
11460
11775
  }
11461
11776
  return ["List", body];
11462
11777
  }
11778
+ function tryInferRangeFromElements(elems, parser) {
11779
+ if (elems.length < 4) return null;
11780
+ const penultimate = elems[elems.length - 2];
11781
+ if (symbol(penultimate) !== "ContinuationPlaceholder") return null;
11782
+ const samples = elems.slice(0, -2);
11783
+ const endExpr = elems[elems.length - 1];
11784
+ if (samples.length < 2) return null;
11785
+ const sampleNums = samples.map(machineValue);
11786
+ if (sampleNums.some((n) => n === null)) return null;
11787
+ const nums = sampleNums;
11788
+ const step = nums[nums.length - 1] - nums[nums.length - 2];
11789
+ const tol = parser.options.tolerance;
11790
+ if (Math.abs(step) < tol)
11791
+ return parser.error("degenerate-range-step", parser.index);
11792
+ for (let i = 1; i < nums.length; i++) {
11793
+ if (Math.abs(nums[i] - nums[i - 1] - step) > tol)
11794
+ return parser.error("inconsistent-range-samples", parser.index);
11795
+ }
11796
+ return ["Range", nums[0], endExpr, step];
11797
+ }
11463
11798
  function serializeList(serializer, expr2) {
11464
11799
  if (nops(expr2) > 1 && operands(expr2).every((x) => {
11465
11800
  const op = operator(x);
@@ -11711,6 +12046,38 @@ function parseForExpression(parser, until) {
11711
12046
  ["Element", index, ["Range", lower, upper]]
11712
12047
  ];
11713
12048
  }
12049
+ function parseForComprehension(parser, lhs, until) {
12050
+ const bindingTerminator = {
12051
+ minPrec: 21,
12052
+ // Above comma (20) and ; (19), so `x = L_1` is captured whole
12053
+ condition: (p) => {
12054
+ if (until?.condition?.(p)) return true;
12055
+ const saved = p.index;
12056
+ p.skipVisualSpace();
12057
+ const isComma = p.peek === ",";
12058
+ p.index = saved;
12059
+ if (isComma) return true;
12060
+ if (peekKeyword(p, "where")) return true;
12061
+ if (peekKeyword(p, "with")) return true;
12062
+ return false;
12063
+ }
12064
+ };
12065
+ const elements = [];
12066
+ do {
12067
+ parser.skipVisualSpace();
12068
+ const binding = parser.parseExpression(bindingTerminator);
12069
+ if (binding === null) break;
12070
+ const op = operator(binding);
12071
+ if (op !== "Equal" && op !== "Assign") return null;
12072
+ const name = operand(binding, 1);
12073
+ const list = operand(binding, 2);
12074
+ if (!name || !list) return null;
12075
+ elements.push(["Element", name, list]);
12076
+ parser.skipVisualSpace();
12077
+ } while (parser.match(","));
12078
+ if (elements.length === 0) return null;
12079
+ return ["Loop", lhs, ...elements];
12080
+ }
11714
12081
  function parseWhereExpression(parser, lhs, until) {
11715
12082
  const bindingTerminator = {
11716
12083
  minPrec: 21,
@@ -11733,6 +12100,25 @@ function parseWhereExpression(parser, lhs, until) {
11733
12100
  parser.skipVisualSpace();
11734
12101
  } while (parser.match(","));
11735
12102
  if (bindings.length === 0) return null;
12103
+ const forStart = parser.index;
12104
+ if (matchKeyword(parser, "for")) {
12105
+ const loop = parseForComprehension(parser, lhs, until);
12106
+ if (loop) {
12107
+ const block2 = [];
12108
+ for (const b of bindings) {
12109
+ const normalized = normalizeLocalAssign(b);
12110
+ if (operator(normalized) === "Assign") {
12111
+ block2.push(["Declare", operand(normalized, 1)]);
12112
+ block2.push(normalized);
12113
+ } else {
12114
+ block2.push(normalized);
12115
+ }
12116
+ }
12117
+ block2.push(loop);
12118
+ return ["Block", ...block2];
12119
+ }
12120
+ parser.index = forStart;
12121
+ }
11736
12122
  const block = [];
11737
12123
  for (const b of bindings) {
11738
12124
  const normalized = normalizeLocalAssign(b);
@@ -11946,6 +12332,17 @@ function parseIntervalBody(body, openLeft, openRight) {
11946
12332
  const upperExpr = openRight ? ["Open", upper] : upper;
11947
12333
  return ["Interval", lowerExpr, upperExpr];
11948
12334
  }
12335
+ var COMPARISON_HEADS = /* @__PURE__ */ new Set([
12336
+ "Less",
12337
+ "LessEqual",
12338
+ "Greater",
12339
+ "GreaterEqual",
12340
+ "Equal",
12341
+ "NotEqual",
12342
+ "And",
12343
+ "Or",
12344
+ "Not"
12345
+ ]);
11949
12346
  var DEFINITIONS_SETS = [
11950
12347
  //
11951
12348
  // Constants
@@ -12204,18 +12601,58 @@ var DEFINITIONS_SETS = [
12204
12601
  closeTrigger: "}",
12205
12602
  parse: (_parser, body) => {
12206
12603
  if (isEmptySequence(body)) return "EmptySet";
12604
+ if (operator(body) == "Delimiter" && stringValue(operand(body, 2)) === ",") {
12605
+ body = operand(body, 1);
12606
+ }
12207
12607
  const h = operator(body);
12208
- if (h === "Divides" || h === "Colon") {
12608
+ if (h === "Divides") {
12209
12609
  const expr2 = operand(body, 1);
12210
12610
  const condition = operand(body, 2);
12211
12611
  if (expr2 !== null && condition !== null)
12212
12612
  return ["Set", expr2, ["Condition", condition]];
12213
12613
  }
12214
- if (operator(body) == "Delimiter" && stringValue(operand(body, 2)) === ",") {
12215
- body = operand(body, 1);
12614
+ if (h === "Colon") {
12615
+ const lhs = operand(body, 1);
12616
+ const rhs = operand(body, 2);
12617
+ if (lhs !== null && rhs !== null) {
12618
+ const lhsOp = operator(lhs);
12619
+ if (lhsOp !== null && COMPARISON_HEADS.has(lhsOp)) {
12620
+ return ["Which", lhs, rhs];
12621
+ }
12622
+ return ["Set", lhs, ["Condition", rhs]];
12623
+ }
12624
+ }
12625
+ if (h === "Sequence") {
12626
+ const elements = operands(body);
12627
+ const colonElements = elements.filter((el) => operator(el) === "Colon");
12628
+ const allPiecewise = colonElements.length > 0 && colonElements.every((el) => {
12629
+ const lhs = operand(el, 1);
12630
+ const lhsOp = lhs !== null ? operator(lhs) : null;
12631
+ return lhsOp !== null && COMPARISON_HEADS.has(lhsOp);
12632
+ });
12633
+ if (allPiecewise) {
12634
+ const whichOps = [];
12635
+ for (let i = 0; i < elements.length; i++) {
12636
+ const el = elements[i];
12637
+ if (operator(el) === "Colon") {
12638
+ const cond = operand(el, 1);
12639
+ const val = operand(el, 2);
12640
+ if (cond === null || val === null) {
12641
+ return ["Set", ...elements];
12642
+ }
12643
+ whichOps.push(cond, val);
12644
+ } else {
12645
+ if (i !== elements.length - 1) {
12646
+ return ["Set", ...elements];
12647
+ }
12648
+ whichOps.push("True", el);
12649
+ }
12650
+ }
12651
+ return ["Which", ...whichOps];
12652
+ }
12653
+ return ["Set", ...elements];
12216
12654
  }
12217
- if (operator(body) !== "Sequence") return ["Set", body];
12218
- return ["Set", ...operands(body)];
12655
+ return ["Set", body];
12219
12656
  },
12220
12657
  serialize: (serializer, expr2) => {
12221
12658
  if (nops(expr2) === 2 && operator(operand(expr2, 2)) === "Condition") {
@@ -14219,7 +14656,8 @@ function parseTrig(op) {
14219
14656
  minPrec: MULTIPLICATION_PRECEDENCE,
14220
14657
  condition: (parser2) => trigCommands[parser2.peek] || (until?.condition?.(parser2) ?? false)
14221
14658
  });
14222
- const appliedFn = args === null ? fn : typeof fn === "string" ? [fn, ...args] : ["Apply", fn, ...args];
14659
+ const head = fn === "Arctan" && args?.length === 2 ? "Arctan2" : fn;
14660
+ const appliedFn = args === null ? fn : typeof head === "string" ? [head, ...args] : ["Apply", head, ...args];
14223
14661
  return sup === null ? appliedFn : ["Power", appliedFn, sup];
14224
14662
  };
14225
14663
  }
@@ -16435,10 +16873,17 @@ var DEFINITIONS_OTHERS = [
16435
16873
  // The capitalized library entries already exist; these are pure parse
16436
16874
  // aliases so the lowercase names don't land in `unsupported-operator`.
16437
16875
  // ---------------------------------------------------------------------------
16876
+ { latexTrigger: "\\operatorname{count}", parse: "Length" },
16438
16877
  { latexTrigger: "\\operatorname{random}", parse: "Random" },
16439
16878
  { latexTrigger: "\\operatorname{shuffle}", parse: "Shuffle" },
16440
16879
  { latexTrigger: "\\operatorname{repeat}", parse: "Repeat" },
16441
16880
  { latexTrigger: "\\operatorname{join}", parse: "Join" },
16881
+ { latexTrigger: "\\operatorname{range}", parse: "Range" },
16882
+ // Note: `\operatorname{with}` (Desmos's local-binding clause) is intentionally
16883
+ // NOT registered here. Use the math-notation equivalent `\operatorname{where}`
16884
+ // (with `\coloneq` for bindings), or register `with` as a custom dictionary
16885
+ // entry at the integration layer — see the "Desmos-Specific Syntax — Prefer
16886
+ // Custom LaTeX Dictionary" section in COMPUTE_ENGINE.md for a worked example.
16442
16887
  // ---------------------------------------------------------------------------
16443
16888
  // Geometric primitive heads. Registered as known typed heads so consumers
16444
16889
  // can branch on the operator name; CE itself doesn't render them. The
@@ -19482,6 +19927,19 @@ var _Parser = class __Parser {
19482
19927
  } while (postfix !== null);
19483
19928
  }
19484
19929
  if (result !== null) result = this.parseSupsub(result);
19930
+ if (result !== null) {
19931
+ let postfix = null;
19932
+ let index = this.index;
19933
+ do {
19934
+ postfix = this.parsePostfixOperator(result, until);
19935
+ result = postfix ?? result;
19936
+ if (this.index === index && postfix !== null) {
19937
+ console.assert(this.index !== index, "No token consumed");
19938
+ break;
19939
+ }
19940
+ index = this.index;
19941
+ } while (postfix !== null);
19942
+ }
19485
19943
  if (result === null) {
19486
19944
  result = this.options.parseUnexpectedToken?.(null, this) ?? null;
19487
19945
  if (result === null && this.peek.startsWith("\\")) {
@@ -19990,6 +20448,28 @@ function toDecimalNumber(wholePart, fractionalPart, exp3) {
19990
20448
  }
19991
20449
 
19992
20450
  // src/compute-engine/latex-syntax/serializer.ts
20451
+ var DOT_NOTATION_MAP = {
20452
+ First: ".x",
20453
+ Second: ".y",
20454
+ Third: ".z",
20455
+ Real: ".\\operatorname{real}",
20456
+ Imaginary: ".\\operatorname{imag}",
20457
+ Length: ".\\operatorname{count}",
20458
+ Sum: ".\\operatorname{total}",
20459
+ Max: ".\\max",
20460
+ Min: ".\\min"
20461
+ };
20462
+ function trySerializeDotNotation(serializer, expr2) {
20463
+ if (!serializer.options.dotNotation) return null;
20464
+ const ops = operands(expr2);
20465
+ if (!ops || ops.length !== 1) return null;
20466
+ const head = operator(expr2);
20467
+ if (!head) return null;
20468
+ const suffix = DOT_NOTATION_MAP[head];
20469
+ if (suffix === void 0) return null;
20470
+ const lhs = serializer.wrap(ops[0], 810);
20471
+ return `${lhs}${suffix}`;
20472
+ }
19993
20473
  var ACCENT_MODIFIERS = {
19994
20474
  deg: (s) => `${s}\\degree`,
19995
20475
  prime: (s) => `${s}^{\\prime}`,
@@ -20033,6 +20513,7 @@ var Serializer5 = class {
20033
20513
  constructor(dictionary, options) {
20034
20514
  this.dictionary = dictionary;
20035
20515
  this.options = {
20516
+ dotNotation: false,
20036
20517
  dmsFormat: false,
20037
20518
  angleNormalization: "none",
20038
20519
  ...options
@@ -20131,6 +20612,8 @@ var Serializer5 = class {
20131
20612
  return def?.serialize?.(this, expr2) ?? serializeSymbol2(symbol(expr2)) ?? "";
20132
20613
  }
20133
20614
  serializeFunction(expr2, def) {
20615
+ const dotResult = trySerializeDotNotation(this, expr2);
20616
+ if (dotResult !== null) return dotResult;
20134
20617
  if (def?.serialize) return def.serialize(this, expr2);
20135
20618
  const h = operator(expr2);
20136
20619
  return serializeSymbol2(h, "auto") + this.wrapArguments(expr2);
@@ -20373,6 +20856,8 @@ function defaultParseOptions(opts) {
20373
20856
  preserveLatex: opts.preserveLatex ?? false,
20374
20857
  quantifierScope: opts.quantifierScope ?? "tight",
20375
20858
  timeDerivativeVariable: opts.timeDerivativeVariable ?? "t",
20859
+ // Standalone mode has no engine; use the same default as ComputeEngine
20860
+ tolerance: 1e-7,
20376
20861
  // Callbacks -- standalone mode has no engine, so these are stubs
20377
20862
  getSymbolType: (_id) => BoxedType.unknown,
20378
20863
  hasSubscriptEvaluate: (_id) => false,
@@ -20405,6 +20890,7 @@ function defaultSerializeOptions(opts) {
20405
20890
  invisiblePlus: "",
20406
20891
  multiply: "\\times",
20407
20892
  missingSymbol: "\\blacksquare",
20893
+ dotNotation: false,
20408
20894
  dmsFormat: false,
20409
20895
  angleNormalization: "none",
20410
20896
  // Style callbacks -- use same defaults as the engine
@@ -22239,6 +22725,15 @@ function expressionTensorInfo(operator2, rows) {
22239
22725
  }
22240
22726
  } else {
22241
22727
  for (const item of t) {
22728
+ const op = item.operator;
22729
+ if (op === "Tuple" || op === "Pair" || op === "Single" || op === "Triple" || op === "Quadruple" || op === "KeyValuePair" || op === "Dictionary" || op === "Set" || op === "Record") {
22730
+ valid = false;
22731
+ return;
22732
+ }
22733
+ if (item.type.type === "string") {
22734
+ valid = false;
22735
+ return;
22736
+ }
22242
22737
  dtype = getSupertype(dtype, getExpressionDatatype(item));
22243
22738
  }
22244
22739
  }
@@ -27274,6 +27769,15 @@ function interval(expr2) {
27274
27769
  return void 0;
27275
27770
  }
27276
27771
 
27772
+ // src/compute-engine/numerics/random.ts
27773
+ function deterministicRandom(seed) {
27774
+ const v = Math.sin(seed * 12.9898) * 43758.5453;
27775
+ return v - Math.floor(v);
27776
+ }
27777
+ function nextSeed(seed) {
27778
+ return seed + 0.6180339887498949;
27779
+ }
27780
+
27277
27781
  // src/compute-engine/boxed-expression/canonical-utils.ts
27278
27782
  function canonical(ce, xs, scope) {
27279
27783
  if (xs.every((x) => x.isCanonical)) return xs;
@@ -27323,6 +27827,19 @@ var COLLECTIONS_LIBRARY = {
27323
27827
  indexWhere: void 0
27324
27828
  }
27325
27829
  },
27830
+ Length: {
27831
+ description: "Number of elements in a collection. Returns undefined for non-collections and for infinite collections.",
27832
+ complexity: 4e3,
27833
+ signature: "(any) -> integer",
27834
+ type: () => "integer",
27835
+ evaluate: ([xs], { engine }) => {
27836
+ if (!xs.isCollection) return void 0;
27837
+ if (xs.isEmptyCollection) return engine.Zero;
27838
+ const n = xs.count;
27839
+ if (n === void 0 || !isFinite(n)) return void 0;
27840
+ return engine.number(n);
27841
+ }
27842
+ },
27326
27843
  Tuple: {
27327
27844
  description: "A fixed number of heterogeneous elements",
27328
27845
  complexity: 8200,
@@ -27375,7 +27892,11 @@ var COLLECTIONS_LIBRARY = {
27375
27892
  //
27376
27893
  Range: {
27377
27894
  complexity: 8200,
27378
- signature: "(number, number?, step: number?) -> indexed_collection<integer>",
27895
+ signature: "(number, number?, step: number?) -> indexed_collection<number>",
27896
+ type: (ops) => {
27897
+ const allInt = ops.every((op) => op.isInteger);
27898
+ return allInt ? parseType("indexed_collection<integer>") : parseType("indexed_collection<number>");
27899
+ },
27379
27900
  canonical: (ops, { engine: ce }) => {
27380
27901
  if (ops.length === 0) return null;
27381
27902
  if (ops.length === 1) return ce._fn("Range", [ce.One, ops[0].canonical]);
@@ -27399,19 +27920,26 @@ var COLLECTIONS_LIBRARY = {
27399
27920
  const [lower, upper, step] = range(expr2);
27400
27921
  if (step === 0) return 0;
27401
27922
  if (!isFinite(lower) || !isFinite(upper)) return Infinity;
27402
- return 1 + Math.max(0, Math.floor((upper - lower) / step));
27923
+ return Math.max(0, Math.floor((upper - lower) / step) + 1);
27403
27924
  },
27404
27925
  contains: (expr2, target) => {
27405
- if (!target.type.matches("integer")) return false;
27406
27926
  const t = target.re;
27927
+ if (!isFinite(t)) return false;
27407
27928
  const [lower, upper, step] = range(expr2);
27408
27929
  if (step === 0) return false;
27409
- if (step > 0) return t >= lower && t <= upper;
27410
- return t <= lower && t >= upper;
27930
+ if (step > 0) {
27931
+ if (t < lower || t > upper) return false;
27932
+ } else {
27933
+ if (t > lower || t < upper) return false;
27934
+ }
27935
+ const k = (t - lower) / step;
27936
+ const tol = expr2.engine.tolerance;
27937
+ const kRounded = Math.round(k);
27938
+ return kRounded >= 0 && Math.abs(k - kRounded) < tol;
27411
27939
  },
27412
27940
  iterator: (expr2) => {
27413
27941
  const [lower, upper, step] = range(expr2);
27414
- const maxCount = step === 0 ? 0 : Math.floor((upper - lower) / step) + 1;
27942
+ const maxCount = step === 0 ? 0 : Math.max(0, Math.floor((upper - lower) / step) + 1);
27415
27943
  let index = 1;
27416
27944
  return {
27417
27945
  next: () => {
@@ -27429,7 +27957,9 @@ var COLLECTIONS_LIBRARY = {
27429
27957
  at: (expr2, index) => {
27430
27958
  if (typeof index !== "number") return void 0;
27431
27959
  const [lower, upper, step] = range(expr2);
27432
- if (index < 1 || index > 1 + (upper - lower) / step) return void 0;
27960
+ if (step === 0) return void 0;
27961
+ const maxCount = Math.max(0, Math.floor((upper - lower) / step) + 1);
27962
+ if (index < 1 || index > maxCount) return void 0;
27433
27963
  return expr2.engine.number(lower + step * (index - 1));
27434
27964
  },
27435
27965
  indexWhere: void 0,
@@ -27454,7 +27984,13 @@ var COLLECTIONS_LIBRARY = {
27454
27984
  if (step > 0) return lower <= upper ? "positive" : "negative";
27455
27985
  return lower >= upper ? "positive" : "negative";
27456
27986
  },
27457
- elttype: (_expr) => "finite_integer"
27987
+ elttype: (expr2) => {
27988
+ if (!isFunction2(expr2)) return "finite_integer";
27989
+ for (let i = 1; i <= expr2.nops; i++) {
27990
+ if (!expr2[`op${i}`].isInteger) return "finite_real";
27991
+ }
27992
+ return "finite_integer";
27993
+ }
27458
27994
  }
27459
27995
  },
27460
27996
  Interval: {
@@ -27557,10 +28093,12 @@ var COLLECTIONS_LIBRARY = {
27557
28093
  const upper = expr2.op2.re;
27558
28094
  let count = expr2.op3.re;
27559
28095
  if (!isFinite(count)) count = DEFAULT_LINSPACE_COUNT;
28096
+ count = Math.floor(count);
27560
28097
  if (!isFinite(lower) || !isFinite(upper)) return void 0;
27561
28098
  if (index < 1 || index > count) return void 0;
28099
+ if (count === 1) return expr2.engine.number(lower);
27562
28100
  return expr2.engine.number(
27563
- lower + (upper - lower) * (index - 1) / count
28101
+ lower + (upper - lower) * (index - 1) / (count - 1)
27564
28102
  );
27565
28103
  },
27566
28104
  iterator: (expr2) => {
@@ -27579,6 +28117,8 @@ var COLLECTIONS_LIBRARY = {
27579
28117
  !isFinite(expr2.op3.re) ? DEFAULT_LINSPACE_COUNT : expr2.op3.re
27580
28118
  );
27581
28119
  }
28120
+ totalCount = Math.floor(totalCount);
28121
+ const denom = totalCount > 1 ? totalCount - 1 : 1;
27582
28122
  let index = 1;
27583
28123
  return {
27584
28124
  next: () => {
@@ -27587,7 +28127,7 @@ var COLLECTIONS_LIBRARY = {
27587
28127
  index += 1;
27588
28128
  return {
27589
28129
  value: expr2.engine.number(
27590
- lower + (upper - lower) * (index - 1 - 1) / totalCount
28130
+ lower + (upper - lower) * (index - 1 - 1) / denom
27591
28131
  ),
27592
28132
  done: false
27593
28133
  };
@@ -27603,9 +28143,14 @@ var COLLECTIONS_LIBRARY = {
27603
28143
  if (t < lower || t > upper) return false;
27604
28144
  let count = expr2.op3.re;
27605
28145
  if (!isFinite(count)) count = DEFAULT_LINSPACE_COUNT;
28146
+ count = Math.floor(count);
27606
28147
  if (count === 0) return false;
27607
- const step = (upper - lower) / count;
27608
- return (t - lower) % step === 0;
28148
+ if (count === 1) return t === lower;
28149
+ const step = (upper - lower) / (count - 1);
28150
+ const k = (t - lower) / step;
28151
+ const tol = expr2.engine.tolerance;
28152
+ const kRounded = Math.round(k);
28153
+ return kRounded >= 0 && kRounded <= count - 1 && Math.abs(k - kRounded) < tol;
27609
28154
  }
27610
28155
  }
27611
28156
  },
@@ -27930,10 +28475,12 @@ var COLLECTIONS_LIBRARY = {
27930
28475
  description: [
27931
28476
  "Access an element of an indexed collection.",
27932
28477
  "If the index is negative, it is counted from the end.",
27933
- "Multiple indices can be provided to access nested collections (e.g., matrices)."
28478
+ "Multiple indices can be provided to access nested collections (e.g., matrices).",
28479
+ "If the index is a finite collection of booleans, returns the elements where the mask is True.",
28480
+ "If the index is a finite collection of integers, returns the elements at those indices."
27934
28481
  ],
27935
28482
  complexity: 8200,
27936
- signature: "(value: indexed_collection, index: (number|string)+) -> unknown",
28483
+ signature: "(value: indexed_collection, index: (number|string|indexed_collection)+) -> unknown",
27937
28484
  type: ([xs]) => xs.operatorDefinition?.collection?.elttype?.(xs) ?? collectionElementType(xs.type.type) ?? "any",
27938
28485
  evaluate: (ops, { engine: ce }) => {
27939
28486
  let expr2 = ops[0];
@@ -27944,12 +28491,39 @@ var COLLECTIONS_LIBRARY = {
27944
28491
  if (!at) return void 0;
27945
28492
  const opAtIndex = ops[index];
27946
28493
  const s = isString(opAtIndex) ? opAtIndex.string : void 0;
27947
- if (s !== void 0) expr2 = at(expr2, s) ?? ce.Nothing;
27948
- else {
27949
- const i = ops[index].re;
27950
- if (!Number.isInteger(i)) return void 0;
27951
- expr2 = at(expr2, i) ?? ce.Nothing;
28494
+ if (s !== void 0) {
28495
+ expr2 = at(expr2, s) ?? ce.Nothing;
28496
+ index += 1;
28497
+ continue;
27952
28498
  }
28499
+ if (opAtIndex.isCollection && opAtIndex.isFiniteCollection) {
28500
+ const indices = Array.from(opAtIndex.each());
28501
+ const isMask = indices.every((m) => {
28502
+ const name = sym(m);
28503
+ return name === "True" || name === "False";
28504
+ });
28505
+ const picked = [];
28506
+ if (isMask) {
28507
+ indices.forEach((m, i2) => {
28508
+ if (sym(m) !== "True") return;
28509
+ const v = at(expr2, i2 + 1);
28510
+ if (v !== void 0) picked.push(v);
28511
+ });
28512
+ } else {
28513
+ for (const m of indices) {
28514
+ const k = m.re;
28515
+ if (!Number.isInteger(k)) return void 0;
28516
+ const v = at(expr2, k);
28517
+ if (v !== void 0) picked.push(v);
28518
+ }
28519
+ }
28520
+ expr2 = ce._fn("List", picked);
28521
+ index += 1;
28522
+ continue;
28523
+ }
28524
+ const i = opAtIndex.re;
28525
+ if (!Number.isInteger(i)) return void 0;
28526
+ expr2 = at(expr2, i) ?? ce.Nothing;
27953
28527
  index += 1;
27954
28528
  }
27955
28529
  return expr2;
@@ -27960,7 +28534,7 @@ var COLLECTIONS_LIBRARY = {
27960
28534
  description: ["Return `n` elements from a collection."],
27961
28535
  complexity: 8200,
27962
28536
  signature: "(xs: indexed_collection, count: number) -> indexed_collection",
27963
- type: ([xs]) => `list<${collectionElementType(xs.type.type)}>`,
28537
+ type: ([xs]) => `list<${typeToString(collectionElementType(xs.type.type) ?? "any")}>`,
27964
28538
  evaluate: (ops, { engine, materialization: eager }) => {
27965
28539
  if (!eager) return void 0;
27966
28540
  const takeExpr = engine._fn("Take", ops);
@@ -28007,7 +28581,7 @@ var COLLECTIONS_LIBRARY = {
28007
28581
  description: ["Return the collection without the first n elements."],
28008
28582
  complexity: 8200,
28009
28583
  signature: "(xs: indexed_collection, count: number) -> indexed_collection",
28010
- type: ([xs]) => `list<${collectionElementType(xs.type.type)}>`,
28584
+ type: ([xs]) => `list<${typeToString(collectionElementType(xs.type.type) ?? "any")}>`,
28011
28585
  collection: {
28012
28586
  isLazy: (_expr) => true,
28013
28587
  count: (expr2) => {
@@ -28052,15 +28626,45 @@ var COLLECTIONS_LIBRARY = {
28052
28626
  },
28053
28627
  First: {
28054
28628
  complexity: 8200,
28055
- signature: "(collection) -> any",
28629
+ signature: "(any) -> any",
28056
28630
  type: ([xs]) => xs.operatorDefinition?.collection?.elttype?.(xs) ?? "any",
28057
- evaluate: ([xs], { engine: ce }) => xs.at(1) ?? ce.Nothing
28631
+ evaluate: ([xs], { engine: ce }) => {
28632
+ if (!xs.isCollection)
28633
+ return ce.error([
28634
+ "incompatible-type",
28635
+ `'collection'`,
28636
+ xs.type.toString()
28637
+ ]);
28638
+ return xs.at(1) ?? ce.Nothing;
28639
+ }
28058
28640
  },
28059
28641
  Second: {
28060
28642
  complexity: 8200,
28061
- signature: "(collection) -> any",
28643
+ signature: "(any) -> any",
28644
+ type: ([xs]) => xs.operatorDefinition?.collection?.elttype?.(xs) ?? "any",
28645
+ evaluate: ([xs], { engine: ce }) => {
28646
+ if (!xs.isCollection)
28647
+ return ce.error([
28648
+ "incompatible-type",
28649
+ `'collection'`,
28650
+ xs.type.toString()
28651
+ ]);
28652
+ return xs.at(2) ?? ce.Nothing;
28653
+ }
28654
+ },
28655
+ Third: {
28656
+ complexity: 8200,
28657
+ signature: "(any) -> any",
28062
28658
  type: ([xs]) => xs.operatorDefinition?.collection?.elttype?.(xs) ?? "any",
28063
- evaluate: ([xs], { engine: ce }) => xs.at(2) ?? ce.Nothing
28659
+ evaluate: ([xs], { engine: ce }) => {
28660
+ if (!xs.isCollection)
28661
+ return ce.error([
28662
+ "incompatible-type",
28663
+ `'collection'`,
28664
+ xs.type.toString()
28665
+ ]);
28666
+ return xs.at(3) ?? ce.Nothing;
28667
+ }
28064
28668
  },
28065
28669
  Last: {
28066
28670
  complexity: 8200,
@@ -28173,7 +28777,9 @@ var COLLECTIONS_LIBRARY = {
28173
28777
  ],
28174
28778
  complexity: 8200,
28175
28779
  signature: "(value: indexed_collection, start: number, end: number) -> list",
28176
- type: ([xs]) => parseType(`list<${collectionElementType(xs.type.type)}>`),
28780
+ type: ([xs]) => parseType(
28781
+ `list<${typeToString(collectionElementType(xs.type.type) ?? "any")}>`
28782
+ ),
28177
28783
  collection: {
28178
28784
  isLazy: (_expr) => true,
28179
28785
  count: (expr2) => {
@@ -28505,16 +29111,26 @@ var COLLECTIONS_LIBRARY = {
28505
29111
  },
28506
29112
  // Randomize the order of the elements in the collection.
28507
29113
  Shuffle: {
28508
- description: "Randomize the order of the elements in the collection.",
29114
+ description: "Randomize the order of the elements in the collection. With an optional `seed` argument, the shuffle is deterministic.",
28509
29115
  complexity: 8200,
28510
- signature: "(indexed_collection) -> indexed_collection",
29116
+ signature: "(indexed_collection, real?) -> indexed_collection",
28511
29117
  type: (ops) => ops[0].type,
28512
- evaluate: ([xs], { engine: ce }) => {
29118
+ evaluate: ([xs, seedOp], { engine: ce }) => {
28513
29119
  if (!xs.isFiniteCollection) return void 0;
28514
29120
  const data = Array.from(xs.each());
28515
- for (let i = data.length - 1; i > 0; i--) {
28516
- const j = Math.floor(Math.random() * (i + 1));
28517
- [data[i], data[j]] = [data[j], data[i]];
29121
+ const seed = seedOp?.re;
29122
+ if (seed !== void 0 && !Number.isNaN(seed)) {
29123
+ let s = seed;
29124
+ for (let i = data.length - 1; i > 0; i--) {
29125
+ const j = Math.floor(deterministicRandom(s) * (i + 1));
29126
+ [data[i], data[j]] = [data[j], data[i]];
29127
+ s = nextSeed(s);
29128
+ }
29129
+ } else {
29130
+ for (let i = data.length - 1; i > 0; i--) {
29131
+ const j = Math.floor(Math.random() * (i + 1));
29132
+ [data[i], data[j]] = [data[j], data[i]];
29133
+ }
28518
29134
  }
28519
29135
  return ce.function(xs.operator, data);
28520
29136
  }
@@ -28581,7 +29197,9 @@ var COLLECTIONS_LIBRARY = {
28581
29197
  if (t === "string")
28582
29198
  return parseType(`tuple<list<string>, list<integer>>`);
28583
29199
  return parseType(
28584
- `tuple<list<${collectionElementType(t)}>, list<integer>>`
29200
+ `tuple<list<${typeToString(
29201
+ collectionElementType(t) ?? "any"
29202
+ )}>, list<integer>>`
28585
29203
  );
28586
29204
  },
28587
29205
  evaluate: (ops, { engine: ce }) => {
@@ -28597,7 +29215,7 @@ var COLLECTIONS_LIBRARY = {
28597
29215
  description: "Return a list of the unique elements of the collection.",
28598
29216
  complexity: 8200,
28599
29217
  signature: "(collection) -> list",
28600
- type: ([xs]) => `list<${collectionElementType(xs.type.type)}>`,
29218
+ type: ([xs]) => `list<${typeToString(collectionElementType(xs.type.type) ?? "any")}>`,
28601
29219
  evaluate: (ops, { engine: ce }) => {
28602
29220
  if (!ops[0].isFiniteCollection) return void 0;
28603
29221
  const [values, _counts] = tally(ops[0]);
@@ -28609,7 +29227,7 @@ var COLLECTIONS_LIBRARY = {
28609
29227
  wikidata: "Q381060",
28610
29228
  complexity: 8200,
28611
29229
  signature: "(collection, integer | function) -> list",
28612
- type: ([xs]) => `list<${collectionElementType(xs.type.type)}>`,
29230
+ type: ([xs]) => `list<${typeToString(collectionElementType(xs.type.type) ?? "any")}>`,
28613
29231
  evaluate: ([xs, arg], { engine: ce }) => {
28614
29232
  if (!xs.isFiniteCollection) return void 0;
28615
29233
  const k = toInteger(arg);
@@ -28783,32 +29401,74 @@ var COLLECTIONS_LIBRARY = {
28783
29401
  }
28784
29402
  }
28785
29403
  },
28786
- // Repeat(x) -> [x, x, ...]
28787
- // This is an infinite series. Can use Take(Repeat(x), n) to get a finite series
28788
- // x is evaluated once. Although could use Hold()?
28789
- // So that First(Repeat(Hold(Random(5))), 10) would return 10 random numbers...
29404
+ // Repeat(x) -> [x, x, ...] — infinite sequence
29405
+ // Repeat(x, n) -> [x, x, ..., x] — finite list of n copies
28790
29406
  Repeat: {
28791
- description: "Produce an infinite sequence by repeating a single value.",
29407
+ description: "Produce a sequence by repeating a single value. With 1 argument, returns an infinite sequence; with 2 arguments (value, count), returns a finite list of `count` copies.",
28792
29408
  complexity: 8200,
28793
- signature: "(value: any) -> list",
29409
+ signature: "(value: any, count: integer?) -> list",
29410
+ evaluate: (ops, { engine }) => {
29411
+ if (ops.length !== 2) return void 0;
29412
+ const raw = toInteger(ops[1]);
29413
+ if (raw === null) return void 0;
29414
+ const n = Math.max(0, raw);
29415
+ if (n > engine.maxCollectionSize) return void 0;
29416
+ return engine._fn("List", Array(n).fill(ops[0]));
29417
+ },
28794
29418
  collection: {
28795
- isLazy: (_expr) => true,
28796
- count: () => Infinity,
28797
- isEmpty: (_expr) => false,
28798
- // Never empty
28799
- isFinite: () => false,
28800
- // Infinite collection
29419
+ isLazy: (expr2) => isFunction2(expr2) && expr2.ops?.length === 1,
29420
+ count: (expr2) => {
29421
+ if (!isFunction2(expr2)) return void 0;
29422
+ if (expr2.ops?.length === 2) {
29423
+ const n = toInteger(expr2.op2);
29424
+ return n !== null ? Math.max(0, n) : void 0;
29425
+ }
29426
+ return Infinity;
29427
+ },
29428
+ isEmpty: (expr2) => {
29429
+ if (!isFunction2(expr2)) return void 0;
29430
+ if (expr2.ops?.length === 2) {
29431
+ const n = toInteger(expr2.op2);
29432
+ return n !== null ? n <= 0 : void 0;
29433
+ }
29434
+ return false;
29435
+ },
29436
+ isFinite: (expr2) => isFunction2(expr2) && expr2.ops?.length === 2,
28801
29437
  contains: (expr2, target) => {
28802
29438
  if (!isFunction2(expr2)) return false;
29439
+ if (expr2.ops?.length === 2) {
29440
+ const n = toInteger(expr2.op2);
29441
+ if (n !== null && n <= 0) return false;
29442
+ }
28803
29443
  return expr2.op1.isSame(target);
28804
29444
  },
28805
29445
  iterator: (expr2) => {
28806
29446
  if (!isFunction2(expr2))
28807
29447
  return { next: () => ({ value: void 0, done: true }) };
29448
+ if (expr2.ops?.length === 2) {
29449
+ const n = toInteger(expr2.op2);
29450
+ if (n === null) {
29451
+ return { next: () => ({ value: void 0, done: true }) };
29452
+ }
29453
+ const count = Math.max(0, n);
29454
+ let i = 0;
29455
+ return {
29456
+ next: () => i++ < count ? { value: expr2.op1, done: false } : { value: void 0, done: true }
29457
+ };
29458
+ }
28808
29459
  return { next: () => ({ value: expr2.op1, done: false }) };
28809
29460
  },
28810
- at: (expr2, _index) => {
29461
+ // at is 1-based (consistent with Range, Take, and other collection handlers)
29462
+ at: (expr2, index) => {
28811
29463
  if (!isFunction2(expr2)) return void 0;
29464
+ if (typeof index !== "number") return void 0;
29465
+ if (expr2.ops?.length === 2) {
29466
+ const n = toInteger(expr2.op2);
29467
+ const count = n !== null ? Math.max(0, n) : 0;
29468
+ if (index < 1 || index > count) return void 0;
29469
+ } else {
29470
+ if (index < 1) return void 0;
29471
+ }
28812
29472
  return expr2.op1;
28813
29473
  }
28814
29474
  }
@@ -29039,17 +29699,14 @@ function range(expr2) {
29039
29699
  if (!isFunction2(expr2)) return [1, 0, 0];
29040
29700
  if (expr2.nops === 0) return [1, 0, 0];
29041
29701
  let op1 = expr2.op1.re;
29042
- if (!isFinite(op1)) op1 = 1;
29043
- else op1 = Math.round(op1);
29702
+ if (!isFinite(op1) && !op1) op1 = 1;
29044
29703
  if (expr2.nops === 1) return [1, op1, 1];
29045
29704
  let op2 = expr2.op2.re;
29046
29705
  if (!isFinite(op2) && !op2) op2 = 1;
29047
- else if (isFinite(op2)) op2 = Math.round(op2);
29048
- if (expr2.nops === 2) return [op1, op2, op2 > op1 ? 1 : -1];
29706
+ if (expr2.nops === 2) return [op1, op2, op2 >= op1 ? 1 : -1];
29049
29707
  let op3 = expr2.op3.re;
29050
- if (!isFinite(op3)) op3 = 1;
29051
- else op3 = Math.abs(Math.round(op3));
29052
- return [op1, op2, op1 < op2 ? op3 : -op3];
29708
+ if (!isFinite(op3) && !op3) op3 = 1;
29709
+ return [op1, op2, op3];
29053
29710
  }
29054
29711
  function rangeLast(r) {
29055
29712
  const [lower, upper, step] = r;
@@ -31097,11 +31754,12 @@ var ARITHMETIC_LIBRARY = [
31097
31754
  );
31098
31755
  }
31099
31756
  },
31100
- // Complex: {
31101
- // // This function is converted during boxing, so unlikely to encounter
31102
- // wikidata: 'Q11567',
31103
- // complexity: 500,
31104
- // },
31757
+ Complex: {
31758
+ description: 'Construct a complex number from real and imaginary parts. Converted directly to a BoxedNumber during boxing; this entry exists so `operatorInfo("Complex")` returns a signature.',
31759
+ wikidata: "Q11567",
31760
+ complexity: 500,
31761
+ signature: "(real: number, imaginary: number) -> complex"
31762
+ },
31105
31763
  Divide: {
31106
31764
  description: "Quotient of a numerator and one or more denominators.",
31107
31765
  wikidata: "Q1226939",
@@ -32451,48 +33109,83 @@ var ARITHMETIC_LIBRARY = [
32451
33109
  }
32452
33110
  },
32453
33111
  Sum: {
32454
- description: "`Sum(f, [a, b])` computes the sum of `f` from `a` to `b`",
33112
+ description: "`Sum(f, [a, b])` computes the sum of `f` from `a` to `b`; `Sum(L)` sums the elements of a collection `L`",
32455
33113
  wikidata: "Q218005",
32456
33114
  complexity: 1e3,
32457
33115
  broadcastable: false,
32458
33116
  scoped: true,
32459
33117
  lazy: true,
32460
- signature: "((number) -> number, bounds:tuple+) -> number",
32461
- canonical: ([body, ...bounds], { scope }) => canonicalBigop("Sum", body, bounds, scope),
32462
- evaluate: ([body, ...indexes], { engine, numericApproximation: numericApproximation2 }) => {
33118
+ signature: "(any, tuple*) -> number",
33119
+ canonical: ([body, ...bounds], { scope, engine: ce }) => {
33120
+ if (bounds.length === 0) {
33121
+ const canon = body?.canonical;
33122
+ if (canon?.isCollection) return ce._fn("Sum", [canon]);
33123
+ }
33124
+ return canonicalBigop("Sum", body, bounds, scope);
33125
+ },
33126
+ evaluate: ([first, ...rest], { engine, numericApproximation: numericApproximation2 }) => {
33127
+ if (rest.length === 0 && first?.isCollection) {
33128
+ if (first.isFiniteCollection !== true) return void 0;
33129
+ const result2 = run(
33130
+ reduceCollection2(
33131
+ first,
33132
+ engine.Zero,
33133
+ (acc, x) => acc.add(x.evaluate({ numericApproximation: numericApproximation2 }))
33134
+ ),
33135
+ engine._timeRemaining
33136
+ );
33137
+ return result2?.evaluate({ numericApproximation: numericApproximation2 }) ?? engine.NaN;
33138
+ }
32463
33139
  const result = run(
32464
33140
  reduceBigOp(
32465
- body,
32466
- indexes,
33141
+ first,
33142
+ rest,
32467
33143
  (acc, x) => acc.add(x.evaluate({ numericApproximation: numericApproximation2 })),
32468
33144
  engine.Zero
32469
33145
  ),
32470
33146
  engine._timeRemaining
32471
33147
  );
32472
- if (result === NON_ENUMERABLE_DOMAIN) {
32473
- return void 0;
32474
- }
33148
+ if (result === NON_ENUMERABLE_DOMAIN) return void 0;
32475
33149
  return result?.evaluate({ numericApproximation: numericApproximation2 }) ?? engine.NaN;
32476
33150
  },
32477
- evaluateAsync: async (xs, { engine, signal, numericApproximation: numericApproximation2 }) => {
33151
+ evaluateAsync: async ([first, ...rest], { engine, signal, numericApproximation: numericApproximation2 }) => {
33152
+ if (rest.length === 0 && first?.isCollection) {
33153
+ if (first.isFiniteCollection !== true) return void 0;
33154
+ const result2 = await runAsync(
33155
+ reduceCollection2(
33156
+ first,
33157
+ engine.Zero,
33158
+ (acc, x) => acc.add(x.evaluate({ numericApproximation: numericApproximation2 }))
33159
+ ),
33160
+ engine._timeRemaining,
33161
+ signal
33162
+ );
33163
+ return result2?.evaluate({ numericApproximation: numericApproximation2 }) ?? engine.NaN;
33164
+ }
32478
33165
  const result = await runAsync(
32479
33166
  reduceBigOp(
32480
- xs[0],
32481
- xs.slice(1),
33167
+ first,
33168
+ rest,
32482
33169
  (acc, x) => acc.add(x.evaluate({ numericApproximation: numericApproximation2 })),
32483
33170
  engine.Zero
32484
33171
  ),
32485
33172
  engine._timeRemaining,
32486
33173
  signal
32487
33174
  );
32488
- if (result === NON_ENUMERABLE_DOMAIN) {
32489
- return void 0;
32490
- }
33175
+ if (result === NON_ENUMERABLE_DOMAIN) return void 0;
32491
33176
  return result?.evaluate({ numericApproximation: numericApproximation2 }) ?? engine.NaN;
32492
33177
  }
32493
33178
  }
32494
33179
  }
32495
33180
  ];
33181
+ function* reduceCollection2(collection, init, combine) {
33182
+ let acc = init;
33183
+ for (const x of collection.each()) {
33184
+ acc = combine(acc, x);
33185
+ yield acc;
33186
+ }
33187
+ return acc;
33188
+ }
32496
33189
  function evaluateAbs(arg) {
32497
33190
  const ce = arg.engine;
32498
33191
  if (isNumber(arg)) {
@@ -42729,7 +43422,7 @@ var COLORS_LIBRARY = {
42729
43422
  var CONTROL_STRUCTURES_LIBRARY = [
42730
43423
  {
42731
43424
  Block: {
42732
- description: "Evaluate a sequence of expressions in a local scope.",
43425
+ description: "Evaluate a sequence of expressions in a local scope, **sequentially**. Each operand is evaluated in order; later operands observe side effects (`Assign`, `Declare`) of earlier operands. The block's value is the value of the last expression. Short-circuiting heads (`Return`, `Break`, `Continue`) terminate the sequence early.\n\nIMPORTANT \u2014 consumers translating *simultaneous* action tuples (e.g. Desmos `(a \u2192 1, b \u2192 a + 1)` where `b` reads the *pre-action* `a`) must rewrite to a snapshot-then-commit Block: bind each RHS to a fresh temp first, then assign the temps to the LHS symbols. See `docs/architecture/actions-and-randomness.md` for the canonical recipe.",
42733
43426
  lazy: true,
42734
43427
  scoped: true,
42735
43428
  signature: "(unknown*) -> unknown",
@@ -42779,12 +43472,42 @@ var CONTROL_STRUCTURES_LIBRARY = [
42779
43472
  }
42780
43473
  },
42781
43474
  Loop: {
42782
- description: "Evaluate a body expression over elements of a collection.",
43475
+ description: "Evaluate a body expression in nested iteration over Element clauses. Later clauses see earlier bindings; independent clauses produce a Cartesian product.",
42783
43476
  lazy: true,
42784
- signature: "(body:expression, collection:expression) -> any",
42785
- type: ([body]) => body.type,
42786
- evaluate: ([body, collection], { engine: ce }) => run(runLoop(body, collection, ce), ce._timeRemaining),
42787
- evaluateAsync: async ([body, collection], { engine: ce, signal }) => runAsync(runLoop(body, collection, ce), ce._timeRemaining, signal)
43477
+ signature: "(body:expression, iterators:expression*) -> any",
43478
+ type: ([body]) => {
43479
+ if (!body) return "nothing";
43480
+ return parseType(`indexed_collection<${String(body.type)}>`);
43481
+ },
43482
+ canonical: canonicalLoop,
43483
+ evaluate: (ops, { engine: ce }) => run(runLoop(ops[0], ops.slice(1), ce), ce._timeRemaining),
43484
+ evaluateAsync: async (ops, { engine: ce, signal }) => runAsync(runLoop(ops[0], ops.slice(1), ce), ce._timeRemaining, signal)
43485
+ },
43486
+ When: {
43487
+ description: 'Conditional/restriction value. `When(e, cond)` evaluates to:\n - `e` when `cond` evaluates to `True`\n - `Undefined` when `cond` evaluates to `False` (the "masking rule"; consumers like 2D plotters skip masked points)\n - `When(e, cond_simplified)` when `cond` is indeterminate (holds)\nStacked restrictions canonicalize: `When(When(e, c1), c2)` \u2192 `When(e, And(c1, c2))`.\nCompiles to ternary `(cond) ? (e) : NaN` in JS and GLSL.',
43488
+ lazy: true,
43489
+ signature: "(expression, boolean) -> any",
43490
+ type: ([expr2]) => expr2.type,
43491
+ canonical: (args, { engine: ce }) => {
43492
+ if (args.length !== 2) return null;
43493
+ const [expr2, cond] = args;
43494
+ if (isFunction2(expr2, "When")) {
43495
+ const inner = expr2.op1.canonical;
43496
+ const innerCond = expr2.op2.canonical;
43497
+ return ce._fn("When", [
43498
+ inner,
43499
+ ce._fn("And", [innerCond, cond.canonical])
43500
+ ]);
43501
+ }
43502
+ return ce._fn("When", [expr2.canonical, cond.canonical]);
43503
+ },
43504
+ evaluate: ([expr2, cond], { engine: ce }) => {
43505
+ const c = cond.evaluate();
43506
+ const cs = sym(c);
43507
+ if (cs === "True") return expr2.evaluate();
43508
+ if (cs === "False") return ce.symbol("Undefined");
43509
+ return ce._fn("When", [expr2, c]);
43510
+ }
42788
43511
  },
42789
43512
  Which: {
42790
43513
  description: "Return the value for the first condition that is true.",
@@ -42844,9 +43567,141 @@ function canonicalBlock(ops, options) {
42844
43567
  );
42845
43568
  return result;
42846
43569
  }
42847
- function* runLoop(body, collection, ce) {
43570
+ function canonicalLoop(ops, options) {
43571
+ const { engine: ce, scope } = options;
43572
+ if (ops.length === 0) return null;
43573
+ if (ops.length === 1) {
43574
+ return ce._fn("Loop", [ops[0].canonical]);
43575
+ }
43576
+ const body = ops[0];
43577
+ const iterators = ops.slice(1);
43578
+ const allElement = iterators.every((it) => it.operator === "Element");
43579
+ if (!allElement) {
43580
+ return ce._fn(
43581
+ "Loop",
43582
+ ops.map((op) => op.canonical)
43583
+ );
43584
+ }
43585
+ const loopScope = scope ?? {
43586
+ parent: ce.context.lexicalScope,
43587
+ bindings: /* @__PURE__ */ new Map()
43588
+ };
43589
+ loopScope.noAutoDeclare = true;
43590
+ ce.pushScope(loopScope);
43591
+ let canonicalIterators;
43592
+ let canonicalBody;
43593
+ try {
43594
+ canonicalIterators = iterators.map((it) => {
43595
+ if (!isFunction2(it, "Element")) {
43596
+ return ce._fn("Element", [
43597
+ ce.error("missing").canonical,
43598
+ ce.error("missing").canonical
43599
+ ]);
43600
+ }
43601
+ const indexExpr = it.ops[0];
43602
+ const collExpr = it.ops[1];
43603
+ if (!indexExpr || !collExpr) {
43604
+ return ce._fn("Element", [
43605
+ (indexExpr ?? ce.error("missing")).canonical,
43606
+ (collExpr ?? ce.error("missing")).canonical
43607
+ ]);
43608
+ }
43609
+ if (isSymbol2(indexExpr) && indexExpr.symbol !== "Nothing") {
43610
+ if (!ce.context.lexicalScope.bindings.has(indexExpr.symbol))
43611
+ ce.declare(indexExpr.symbol, "unknown");
43612
+ }
43613
+ return ce._fn("Element", [indexExpr.canonical, collExpr.canonical]);
43614
+ });
43615
+ canonicalBody = body.canonical;
43616
+ } finally {
43617
+ ce.popScope();
43618
+ loopScope.noAutoDeclare = false;
43619
+ }
43620
+ return ce._fn("Loop", [canonicalBody, ...canonicalIterators], {
43621
+ scope: loopScope
43622
+ });
43623
+ }
43624
+ function* runLoop(body, elements, ce) {
42848
43625
  body ??= ce.Nothing;
42849
43626
  if (sym(body) === "Nothing") return body;
43627
+ if (elements.length === 0) {
43628
+ const result = body.evaluate();
43629
+ yield result;
43630
+ return result;
43631
+ }
43632
+ if (elements.length === 1 && elements[0].operator !== "Element") {
43633
+ return yield* runLoopLegacy(body, elements[0], ce);
43634
+ }
43635
+ const results = [];
43636
+ const state = { stopped: false, broke: false, count: 0 };
43637
+ const freshScope = {
43638
+ parent: ce.context.lexicalScope,
43639
+ bindings: /* @__PURE__ */ new Map()
43640
+ };
43641
+ ce._pushEvalContext(freshScope);
43642
+ try {
43643
+ for (const elem of elements) {
43644
+ if (!isFunction2(elem, "Element")) continue;
43645
+ const idx = elem.ops[0];
43646
+ if (idx && isSymbol2(idx) && idx.symbol !== "Nothing") {
43647
+ if (!freshScope.bindings.has(idx.symbol))
43648
+ ce.declare(idx.symbol, "unknown");
43649
+ }
43650
+ }
43651
+ yield* runLoopNested(body, elements, 0, ce, results, state);
43652
+ } finally {
43653
+ ce._popEvalContext();
43654
+ }
43655
+ if (state.stopped && state.value !== void 0) {
43656
+ if (!state.broke) return state.value;
43657
+ return state.value;
43658
+ }
43659
+ return ce.function("List", results);
43660
+ }
43661
+ function* runLoopNested(body, elements, index, ce, results, state) {
43662
+ if (state.stopped) return;
43663
+ if (index === elements.length) {
43664
+ const result = body.evaluate();
43665
+ state.count += 1;
43666
+ if (state.count > ce.iterationLimit)
43667
+ throw new CancellationError({ cause: "iteration-limit-exceeded" });
43668
+ if (isFunction2(result, "Break")) {
43669
+ state.stopped = true;
43670
+ state.broke = true;
43671
+ state.value = result.op1;
43672
+ return;
43673
+ }
43674
+ if (result.operator === "Return") {
43675
+ state.stopped = true;
43676
+ state.value = result;
43677
+ return;
43678
+ }
43679
+ results.push(result);
43680
+ yield result;
43681
+ return;
43682
+ }
43683
+ const elem = elements[index];
43684
+ if (!isFunction2(elem, "Element")) {
43685
+ return;
43686
+ }
43687
+ const indexExpr = elem.ops[0];
43688
+ const collExpr = elem.ops[1];
43689
+ if (!indexExpr || !isSymbol2(indexExpr) || !collExpr) {
43690
+ return;
43691
+ }
43692
+ const name = indexExpr.symbol;
43693
+ const collection = collExpr.evaluate();
43694
+ if (!collection?.isCollection) {
43695
+ return;
43696
+ }
43697
+ const skipAssign = name === "Nothing";
43698
+ for (const value of collection.each()) {
43699
+ if (!skipAssign) ce.assign(name, value);
43700
+ yield* runLoopNested(body, elements, index + 1, ce, results, state);
43701
+ if (state.stopped) return;
43702
+ }
43703
+ }
43704
+ function* runLoopLegacy(body, collection, ce) {
42850
43705
  if (collection?.isCollection) {
42851
43706
  let result = void 0;
42852
43707
  const fn = applicable(body);
@@ -44488,7 +45343,7 @@ var CORE_LIBRARY = [
44488
45343
  evaluate: (ops) => apply(ops[0], ops.slice(1))
44489
45344
  },
44490
45345
  Assign: {
44491
- description: "Assign a value to a symbol or define a sequence",
45346
+ description: "Assign a value to a symbol or define a sequence. The RHS is evaluated immediately and `ce.assign(name, val)` mutates the binding in the current scope chain. When used inside a `Block`, the assignment is visible to subsequent statements in the block (sequential semantics).",
44492
45347
  lazy: true,
44493
45348
  pure: false,
44494
45349
  signature: "(symbol | expression, any) -> any",
@@ -44619,7 +45474,12 @@ var CORE_LIBRARY = [
44619
45474
  evaluate: (ops, { engine: ce }) => {
44620
45475
  const symbolName2 = sym(ops[0].evaluate());
44621
45476
  if (!symbolName2) return void 0;
45477
+ const currentScope = ce.context.lexicalScope;
45478
+ const existing = currentScope.bindings.get(symbolName2);
45479
+ const existingValueDef = existing && isValueDef(existing) ? existing : void 0;
45480
+ const isAutoDeclareHere = !!existingValueDef && existingValueDef.value.inferredType && existingValueDef.value.value === void 0;
44622
45481
  if (!ops[1]) {
45482
+ if (isAutoDeclareHere) return ce.Nothing;
44623
45483
  ce.declare(symbolName2, { inferred: true, type: "unknown" });
44624
45484
  return ce.Nothing;
44625
45485
  }
@@ -44628,6 +45488,11 @@ var CORE_LIBRARY = [
44628
45488
  (isString(t) ? t.string : void 0) ?? sym(t) ?? void 0
44629
45489
  );
44630
45490
  if (!isValidType(type2)) return void 0;
45491
+ if (isAutoDeclareHere && existingValueDef) {
45492
+ existingValueDef.value.type = ce.type(type2);
45493
+ existingValueDef.value.inferredType = false;
45494
+ return ce.Nothing;
45495
+ }
44631
45496
  ce.declare(symbolName2, type2);
44632
45497
  return ce.Nothing;
44633
45498
  }
@@ -44745,33 +45610,41 @@ var CORE_LIBRARY = [
44745
45610
  },
44746
45611
  Random: {
44747
45612
  description: [
44748
- "Random(): Return a random number between 0 and 1",
44749
- "Random(n): Return a random integer between 0 and n-1",
44750
- "Random(m, n): Return a random integer between m and n-1"
45613
+ "Random(): non-deterministic float in [0, 1)",
45614
+ "Random(seed: real): deterministic float in [0, 1) from a real seed",
45615
+ "Random(n: integer): non-deterministic integer in [0, n)",
45616
+ "Random(m: integer, n: integer): non-deterministic integer in [m, n)"
44751
45617
  ],
44752
45618
  pure: false,
44753
- signature: "(lower:integer?, upper:integer?) -> finite_number",
44754
- type: ([lower, upper]) => {
44755
- if (lower === void 0 && upper === void 0) return "finite_number";
44756
- return "finite_integer";
45619
+ // Signature accepts: nothing, one number, or two integers.
45620
+ // Use `number` (not `integer`) for the single-arg case so float seeds
45621
+ // type-check; runtime dispatch differentiates integer vs real.
45622
+ signature: "(number?, integer?) -> finite_number",
45623
+ type: ([first, second]) => {
45624
+ if (first === void 0) return "finite_number";
45625
+ if (second !== void 0) return "finite_integer";
45626
+ if (first.type.matches("integer")) return "finite_integer";
45627
+ return "finite_number";
44757
45628
  },
44758
45629
  sgn: () => "non-negative",
44759
45630
  evaluate: (ops, { engine: ce }) => {
44760
45631
  if (ops.length === 0) return ce.number(Math.random());
44761
- const [lowerOp, upperOp] = ops;
44762
- let lower;
44763
- let upper;
44764
- if (upperOp === void 0) {
44765
- lower = 0;
44766
- upper = Math.floor(lowerOp.re - 1);
44767
- if (isNaN(upper)) upper = 0;
44768
- } else {
44769
- lower = Math.floor(lowerOp.re);
44770
- upper = Math.floor(upperOp.re);
45632
+ const [firstOp, secondOp] = ops;
45633
+ if (secondOp !== void 0) {
45634
+ let lower = Math.floor(firstOp.re);
45635
+ let upper = Math.floor(secondOp.re);
44771
45636
  if (isNaN(lower)) lower = 0;
44772
45637
  if (isNaN(upper)) upper = 0;
45638
+ return ce.number(lower + Math.floor(Math.random() * (upper - lower)));
45639
+ }
45640
+ if (firstOp.type.matches("integer")) {
45641
+ let n = Math.floor(firstOp.re);
45642
+ if (isNaN(n)) n = 0;
45643
+ return ce.number(Math.floor(Math.random() * n));
44773
45644
  }
44774
- return ce.number(lower + Math.floor(Math.random() * (upper - lower)));
45645
+ const seed = firstOp.re;
45646
+ if (isNaN(seed)) return ce.number(0);
45647
+ return ce.number(deterministicRandom(seed));
44775
45648
  }
44776
45649
  },
44777
45650
  // @todo: need review
@@ -45227,6 +46100,14 @@ var CORE_LIBRARY = [
45227
46100
  To: {
45228
46101
  description: "Action arrow / mapping (`a \\to b`) \u2014 opaque typed head.",
45229
46102
  signature: "(any, any) -> nothing"
46103
+ },
46104
+ Colon: {
46105
+ description: "Type annotation (`a : b`) \u2014 opaque typed head.",
46106
+ signature: "(any, any) -> expression"
46107
+ },
46108
+ Prime: {
46109
+ description: "Derivative or prime notation (`f'`, `f^{(n)}`) \u2014 opaque typed head until a derivative library handler runs.",
46110
+ signature: "(any, integer?) -> expression"
45230
46111
  }
45231
46112
  }
45232
46113
  ];
@@ -50267,18 +51148,28 @@ var STATISTICS_LIBRARY = [
50267
51148
  },
50268
51149
  {
50269
51150
  Sample: {
50270
- description: "Return a random sample of k elements from the collection, without replacement.",
51151
+ description: "Return a random sample of k elements from the collection, without replacement. With an optional `seed` argument, the sample is deterministic.",
50271
51152
  complexity: 8200,
50272
- signature: "(collection, integer) -> list",
50273
- evaluate: ([xs, nArg], { engine: ce }) => {
51153
+ signature: "(collection, integer, real?) -> list",
51154
+ evaluate: ([xs, nArg, seedArg], { engine: ce }) => {
50274
51155
  if (!xs.isFiniteCollection) return void 0;
50275
51156
  const k = toInteger(nArg);
50276
51157
  if (k === null || k < 0) return void 0;
50277
51158
  const data = Array.from(xs.each());
50278
51159
  if (k > data.length) return void 0;
50279
- for (let i = data.length - 1; i > 0; i--) {
50280
- const j = Math.floor(Math.random() * (i + 1));
50281
- [data[i], data[j]] = [data[j], data[i]];
51160
+ const seed = seedArg?.re;
51161
+ if (seed !== void 0 && !Number.isNaN(seed)) {
51162
+ let s = seed;
51163
+ for (let i = data.length - 1; i > 0; i--) {
51164
+ const j = Math.floor(deterministicRandom(s) * (i + 1));
51165
+ [data[i], data[j]] = [data[j], data[i]];
51166
+ s = nextSeed(s);
51167
+ }
51168
+ } else {
51169
+ for (let i = data.length - 1; i > 0; i--) {
51170
+ const j = Math.floor(Math.random() * (i + 1));
51171
+ [data[i], data[j]] = [data[j], data[i]];
51172
+ }
50282
51173
  }
50283
51174
  const sample = data.slice(0, k);
50284
51175
  return ce.function("List", sample);
@@ -53099,6 +53990,20 @@ var BoxedFunction = class extends _BoxedExpression {
53099
53990
  if (results.length === 1) return results[0];
53100
53991
  return this.engine._fn("List", results);
53101
53992
  }
53993
+ if (def instanceof _BoxedOperatorDefinition && def._isLambda && this.ops.some((x) => isFiniteIndexedCollection(x)) && paramsAreScalar(def)) {
53994
+ const items = zip(this._ops);
53995
+ if (items) {
53996
+ const results = [];
53997
+ while (true) {
53998
+ const { done, value } = items.next();
53999
+ if (done) break;
54000
+ results.push(
54001
+ this.engine._fn(this.operator, value).evaluate(options)
54002
+ );
54003
+ }
54004
+ return this.engine._fn("List", results);
54005
+ }
54006
+ }
53102
54007
  if (materialization !== false && !def.evaluate && this.isLazyCollection)
53103
54008
  return materialize(this, def, options);
53104
54009
  const tail = holdMap(this, (x) => x.evaluate(options));
@@ -53145,6 +54050,22 @@ var BoxedFunction = class extends _BoxedExpression {
53145
54050
  (resolved) => this.engine._fn("List", resolved)
53146
54051
  );
53147
54052
  }
54053
+ if (def instanceof _BoxedOperatorDefinition && def._isLambda && this.ops.some((x) => isFiniteIndexedCollection(x)) && paramsAreScalar(def)) {
54054
+ const items = zip(this._ops);
54055
+ if (items) {
54056
+ const results = [];
54057
+ while (true) {
54058
+ const { done, value } = items.next();
54059
+ if (done) break;
54060
+ results.push(
54061
+ this.engine._fn(this.operator, value).evaluateAsync(options)
54062
+ );
54063
+ }
54064
+ return Promise.all(results).then(
54065
+ (resolved) => this.engine._fn("List", resolved)
54066
+ );
54067
+ }
54068
+ }
53148
54069
  const tail = await holdMapAsync(
53149
54070
  this,
53150
54071
  async (x) => await x.evaluateAsync(options)
@@ -53359,8 +54280,47 @@ function applyFunctionLiteral(expr2, def, options) {
53359
54280
  const ops = expr2.ops.map((x) => x.evaluate(options));
53360
54281
  if (!value || value.type.isUnknown)
53361
54282
  return expr2.engine.function(expr2.operator, ops);
54283
+ if (ops.some((x) => isFiniteIndexedCollection(x)) && paramsAreScalar(value.type.type)) {
54284
+ const items = zip(ops);
54285
+ if (items) {
54286
+ const results = [];
54287
+ while (true) {
54288
+ const { done, value: zipped } = items.next();
54289
+ if (done) break;
54290
+ results.push(apply(value, zipped).evaluate(options));
54291
+ }
54292
+ return expr2.engine._fn("List", results);
54293
+ }
54294
+ }
53362
54295
  return apply(value, ops);
53363
54296
  }
54297
+ function paramsAreScalar(source) {
54298
+ const sigType = isOperatorDefinition(source) ? source.signature?.type : source;
54299
+ if (!sigType || typeof sigType === "string") return true;
54300
+ if (sigType.kind !== "signature") return true;
54301
+ const args = [
54302
+ ...sigType.args ?? [],
54303
+ ...sigType.optArgs ?? [],
54304
+ ...sigType.variadicArg ? [sigType.variadicArg] : []
54305
+ ];
54306
+ return args.every((arg) => isScalarType(arg.type));
54307
+ }
54308
+ function isOperatorDefinition(source) {
54309
+ return typeof source === "object" && source !== null && "signature" in source;
54310
+ }
54311
+ function isScalarType(t) {
54312
+ if (typeof t === "string") {
54313
+ if (t === "collection" || t === "indexed_collection" || t === "list" || t === "tuple" || t === "set" || t === "dictionary" || t === "record" || t === "function")
54314
+ return false;
54315
+ return true;
54316
+ }
54317
+ if (t.kind === "collection" || t.kind === "indexed_collection" || t.kind === "list" || t.kind === "tuple" || t.kind === "set" || t.kind === "dictionary" || t.kind === "record" || t.kind === "signature")
54318
+ return false;
54319
+ if (t.kind === "union" || t.kind === "intersection")
54320
+ return t.types.every((x) => isScalarType(x));
54321
+ if (t.kind === "negation") return isScalarType(t.type);
54322
+ return true;
54323
+ }
53364
54324
  function materialize(expr2, def, options) {
53365
54325
  if (!expr2.isValid || options?.materialization === false) return expr2;
53366
54326
  let materialization = options?.materialization ?? false;
@@ -53368,6 +54328,11 @@ function materialize(expr2, def, options) {
53368
54328
  materialization = DEFAULT_MATERIALIZATION;
53369
54329
  const isIndexed = expr2.isIndexedCollection;
53370
54330
  const isFinite2 = expr2.isFiniteCollection;
54331
+ if (isIndexed && isFinite2) {
54332
+ const count = expr2.count;
54333
+ if (count !== void 0 && count > expr2.engine.maxCollectionSize)
54334
+ return expr2;
54335
+ }
53371
54336
  const xs = [];
53372
54337
  if (!expr2.isEmptyCollection) {
53373
54338
  if (!isIndexed || !isFinite2) {
@@ -53595,7 +54560,7 @@ var BoxedDictionary = class _BoxedDictionary extends _BoxedExpression {
53595
54560
  const eltType = widen(
53596
54561
  ...Object.values(this._keyValues).map((op) => op.type.type)
53597
54562
  );
53598
- this._type = this.engine.type(`dictionary<${eltType}>`);
54563
+ this._type = new BoxedType({ kind: "dictionary", values: eltType });
53599
54564
  return this._type;
53600
54565
  }
53601
54566
  get isPure() {
@@ -53873,6 +54838,7 @@ function box(ce, expr2, options) {
53873
54838
  }
53874
54839
  if (typeof expr2 === "number" || expr2 instanceof BigDecimal || expr2 instanceof Complex)
53875
54840
  return ce.number(expr2);
54841
+ if (typeof expr2 === "boolean") return ce.symbol(expr2 ? "True" : "False");
53876
54842
  if (typeof expr2 === "string") {
53877
54843
  if (matchesSymbol(expr2)) {
53878
54844
  const sym2 = symbol(expr2);
@@ -54929,6 +55895,23 @@ var BaseCompiler = class _BaseCompiler {
54929
55895
  };
54930
55896
  return compilePair(0);
54931
55897
  }
55898
+ if (h === "When") {
55899
+ if (args.length !== 2)
55900
+ throw new Error("When: expected exactly 2 arguments (expr, cond)");
55901
+ const fn2 = target.functions?.(h);
55902
+ if (fn2) {
55903
+ if (typeof fn2 === "function") {
55904
+ return fn2(args, (expr2) => _BaseCompiler.compile(expr2, target), target);
55905
+ }
55906
+ return `${fn2}(${args.map((x) => _BaseCompiler.compile(x, target)).join(", ")})`;
55907
+ }
55908
+ if (isSymbol2(args[1], "True"))
55909
+ return `(${_BaseCompiler.compile(args[0], target)})`;
55910
+ if (isSymbol2(args[1], "False")) return "NaN";
55911
+ const val = _BaseCompiler.compile(args[0], target);
55912
+ const cond = _BaseCompiler.compile(args[1], target);
55913
+ return `((${cond}) ? (${val}) : NaN)`;
55914
+ }
54932
55915
  if (h === "Block") {
54933
55916
  return _BaseCompiler.compileBlock(args, target);
54934
55917
  }
@@ -55003,17 +55986,98 @@ var BaseCompiler = class _BaseCompiler {
55003
55986
  )}${target.ws("\n")}})()`;
55004
55987
  }
55005
55988
  /**
55006
- * Compile a Loop expression with Element(index, Range(lo, hi)) indexing.
55007
- * Generates: (() => { for (let i = lo; i <= hi; i++) { body } })()
55989
+ * Compile a Loop expression.
55990
+ *
55991
+ * Two forms are supported:
55992
+ *
55993
+ * 1. **Imperative / single-Element form** (existing behaviour):
55994
+ * `Loop(body, Element(i, Range(lo, hi)))`
55995
+ * Generates a raw `for (let i = lo; i <= hi; i++) { body }` loop wrapped
55996
+ * in an IIFE. The loop counter is always a plain number. For targets
55997
+ * that wrap numeric values (e.g. interval-js uses `_IA.point()`),
55998
+ * references to the loop index inside the body are re-wrapped via
55999
+ * `target.number`. `break` / `continue` / `return` are preserved.
55008
56000
  *
55009
- * The loop counter is always a raw number. For targets that wrap numeric
55010
- * values (e.g. interval-js wraps with `_IA.point()`), references to the
55011
- * loop index inside the body are wrapped via `target.number`.
56001
+ * 2. **Comprehension / variadic-Element form** (new):
56002
+ * `Loop(body, Element(x, coll1), Element(y, coll2), )`
56003
+ * When two or more `Element` clauses are present or when the single
56004
+ * Element's collection is not a `Range` — the loop is compiled as a
56005
+ * comprehension that collects results into an array. Each clause
56006
+ * produces a `for (const name of collection)` loop, nested
56007
+ * outermost-to-innermost, and the innermost body pushes into `result`.
56008
+ *
56009
+ * Example output (JS):
56010
+ * ```js
56011
+ * (() => { const result = [];
56012
+ * for (const x of [1,2]) { for (const y of [3,4]) { result.push(body); } }
56013
+ * return result; })()
56014
+ * ```
56015
+ *
56016
+ * GLSL: multi-Element comprehension is not trivially representable in
56017
+ * GLSL (no dynamic arrays, no push). A compile-time error is thrown.
56018
+ * TODO(E3-GLSL): support GLSL multi-Element via a pre-declared fixed-size
56019
+ * array or by unrolling when bounds are known at compile time.
56020
+ *
56021
+ * Known issue (imperative form): the IIFE generated by form (1) has no
56022
+ * `return` statement, so `Loop(body, Element(i, Range(lo, hi)))` compiled
56023
+ * to JS evaluates to `undefined` at runtime, while CE evaluation returns a
56024
+ * `List` of body values. See `test/compute-engine/a1-c1-compile-parity.test.ts`
56025
+ * ("Loop compiles in JS") for the verify-only test that locks in the
56026
+ * current behavior.
55012
56027
  */
55013
56028
  static compileForLoop(args, target) {
55014
56029
  if (!args[0]) throw new Error("Loop: no body");
55015
56030
  if (!args[1]) throw new Error("Loop: no indexing set");
55016
- const indexing = args[1];
56031
+ const body = args[0];
56032
+ const elements = args.slice(1);
56033
+ const useComprehension = elements.length > 1 || elements.length === 1 && isFunction2(elements[0], "Element") && !_BaseCompiler.isLegacyCompatibleRange(elements[0].ops[1]);
56034
+ if (useComprehension) {
56035
+ const lang = target.language ?? "";
56036
+ if (lang === "glsl" || lang === "wgsl") {
56037
+ throw new Error(
56038
+ `${lang.toUpperCase()}: multi-Element Loop comprehension is not yet supported. TODO(E3-GLSL): unroll or use a fixed-size array.`
56039
+ );
56040
+ }
56041
+ const narrowedElements = [];
56042
+ for (let i = 0; i < elements.length; i++) {
56043
+ const elem = elements[i];
56044
+ if (!isFunction2(elem, "Element"))
56045
+ throw new Error(
56046
+ `Loop: argument ${i + 1} must be an Element clause, got ${elem.operator ?? "?"}`
56047
+ );
56048
+ if (!isSymbol2(elem.ops[0]))
56049
+ throw new Error(
56050
+ `Loop: Element index (argument ${i + 1}) must be a symbol`
56051
+ );
56052
+ narrowedElements.push(elem);
56053
+ }
56054
+ const loopVarSet = new Set(
56055
+ narrowedElements.map(
56056
+ (e) => e.ops[0].symbol
56057
+ )
56058
+ );
56059
+ const needsWrap2 = target.number(0) !== "0";
56060
+ const bodyTarget2 = needsWrap2 ? {
56061
+ ...target,
56062
+ var: (id) => loopVarSet.has(id) ? target.number(0).replace("0", id) : target.var(id)
56063
+ } : target;
56064
+ const bodyCode = _BaseCompiler.compile(body, bodyTarget2);
56065
+ let inner = `result.push(${bodyCode});`;
56066
+ for (let i = narrowedElements.length - 1; i >= 0; i--) {
56067
+ const elem = narrowedElements[i];
56068
+ const name = elem.ops[0].symbol;
56069
+ const collExpr = elem.ops[1];
56070
+ let collection;
56071
+ if (isFunction2(collExpr, "Range")) {
56072
+ collection = _BaseCompiler.compileRangeIterable(collExpr, bodyTarget2);
56073
+ } else {
56074
+ collection = _BaseCompiler.compile(collExpr, bodyTarget2);
56075
+ }
56076
+ inner = `for (const ${name} of ${collection}) { ${inner} }`;
56077
+ }
56078
+ return `(() => { const result = []; ${inner} return result; })()`;
56079
+ }
56080
+ const indexing = elements[0];
55017
56081
  if (!isFunction2(indexing, "Element"))
55018
56082
  throw new Error("Loop: expected Element(index, Range(lo, hi))");
55019
56083
  const indexExpr = indexing.ops[0];
@@ -55031,13 +56095,72 @@ var BaseCompiler = class _BaseCompiler {
55031
56095
  ...target,
55032
56096
  var: (id) => id === index ? needsWrap ? target.number(0).replace("0", index) : index : target.var(id)
55033
56097
  };
55034
- const bodyStmts = _BaseCompiler.compileLoopBody(args[0], bodyTarget);
56098
+ const bodyStmts = _BaseCompiler.compileLoopBody(body, bodyTarget);
55035
56099
  return `(() => {${target.ws(
55036
56100
  "\n"
55037
56101
  )}for (let ${index} = ${lower}; ${index} <= ${upper}; ${index}++) {${target.ws(
55038
56102
  "\n"
55039
56103
  )}${bodyStmts}${target.ws("\n")}}${target.ws("\n")}})()`;
55040
56104
  }
56105
+ /**
56106
+ * Returns `true` when the given collection expression is a `Range` whose
56107
+ * runtime semantics match the legacy imperative for-loop shape
56108
+ * `for (let i = lo; i <= hi; i++)`.
56109
+ *
56110
+ * Concretely: integer-ascending bounds and step omitted-or-1. When bounds
56111
+ * are not statically numeric we accept the Range (the historical
56112
+ * behaviour) — runtime mismatch in the descending-unknown-bounds case is
56113
+ * left as a known limitation; callers can force the iterable path by
56114
+ * supplying an explicit step.
56115
+ */
56116
+ static isLegacyCompatibleRange(coll) {
56117
+ if (!isFunction2(coll, "Range")) return false;
56118
+ if (coll.ops.length >= 3) {
56119
+ const stepExpr = coll.ops[2];
56120
+ if (!isNumber(stepExpr) || stepExpr.re !== 1) return false;
56121
+ }
56122
+ const lo = coll.ops[0];
56123
+ const hi = coll.ops[1];
56124
+ if (isNumber(lo) && !Number.isInteger(lo.re)) return false;
56125
+ if (isNumber(hi) && !Number.isInteger(hi.re)) return false;
56126
+ if (isNumber(lo) && isNumber(hi) && lo.re > hi.re) return false;
56127
+ return true;
56128
+ }
56129
+ /**
56130
+ * Compile a `Range(lo, hi)` or `Range(lo, hi, step)` expression into a JS
56131
+ * iterable expression. Mirrors the runtime semantics in
56132
+ * `library/collections.ts` Range:
56133
+ * count = step === 0 ? 0 : max(0, floor((hi - lo) / step) + 1)
56134
+ * element = lo + step * k (0-indexed)
56135
+ * Default step is 1 when omitted. Bounds and step may be fractional.
56136
+ *
56137
+ * Only used from the comprehension path in `compileForLoop`.
56138
+ * Caller must have already verified `isFunction(rangeExpr, 'Range')`.
56139
+ */
56140
+ static compileRangeIterable(rangeExpr, target) {
56141
+ const loExpr = rangeExpr.ops[0];
56142
+ const hiExpr = rangeExpr.ops[1];
56143
+ const stepExpr = rangeExpr.ops[2];
56144
+ if (isNumber(loExpr) && isNumber(hiExpr) && (stepExpr === void 0 || isNumber(stepExpr))) {
56145
+ const lo2 = loExpr.re;
56146
+ const hi2 = hiExpr.re;
56147
+ const step2 = stepExpr === void 0 ? hi2 >= lo2 ? 1 : -1 : stepExpr.re;
56148
+ if (step2 === 0) return "[]";
56149
+ const len = Math.max(0, Math.floor((hi2 - lo2) / step2) + 1);
56150
+ if (step2 === 1) {
56151
+ if (lo2 === 0) return `Array.from({length:${len}},(_,k)=>k)`;
56152
+ return `Array.from({length:${len}},(_,k)=>${lo2}+k)`;
56153
+ }
56154
+ return `Array.from({length:${len}},(_,k)=>${lo2}+(${step2})*k)`;
56155
+ }
56156
+ const lo = _BaseCompiler.compile(loExpr, target);
56157
+ const hi = _BaseCompiler.compile(hiExpr, target);
56158
+ if (stepExpr === void 0) {
56159
+ return `((_lo,_hi)=>{const _st=_hi>=_lo?1:-1;return Array.from({length:Math.max(0,Math.floor((_hi-_lo)/_st)+1)},(_,k)=>_lo+_st*k);})(${lo},${hi})`;
56160
+ }
56161
+ const step = _BaseCompiler.compile(stepExpr, target);
56162
+ return `((_lo,_hi,_st)=>_st===0?[]:Array.from({length:Math.max(0,Math.floor((_hi-_lo)/_st)+1)},(_,k)=>_lo+_st*k))(${lo},${hi},${step})`;
56163
+ }
55041
56164
  /**
55042
56165
  * Compile a loop body expression as statements (not wrapped in IIFE).
55043
56166
  * Handles Break, Continue, Return as statements, and If as if-else when
@@ -59537,8 +60660,8 @@ function assumeInequality(proposition) {
59537
60660
  }
59538
60661
  if (effectiveOp === "greater" || effectiveOp === "greaterEqual") {
59539
60662
  const isStrict = effectiveOp === "greater";
59540
- if (bounds.lowerBound !== void 0) {
59541
- const lowerVal = isNumber(bounds.lowerBound) ? bounds.lowerBound.numericValue : void 0;
60663
+ if (bounds.lower !== void 0) {
60664
+ const lowerVal = isNumber(bounds.lower) ? bounds.lower.numericValue : void 0;
59542
60665
  if (typeof lowerVal === "number" && isFinite(lowerVal)) {
59543
60666
  if (isStrict) {
59544
60667
  if (lowerVal > k) return "tautology";
@@ -59550,8 +60673,8 @@ function assumeInequality(proposition) {
59550
60673
  }
59551
60674
  }
59552
60675
  }
59553
- if (bounds.upperBound !== void 0) {
59554
- const upperVal = isNumber(bounds.upperBound) ? bounds.upperBound.numericValue : void 0;
60676
+ if (bounds.upper !== void 0) {
60677
+ const upperVal = isNumber(bounds.upper) ? bounds.upper.numericValue : void 0;
59555
60678
  if (typeof upperVal === "number" && isFinite(upperVal)) {
59556
60679
  if (isStrict) {
59557
60680
  if (upperVal < k) return "contradiction";
@@ -59566,8 +60689,8 @@ function assumeInequality(proposition) {
59566
60689
  }
59567
60690
  } else {
59568
60691
  const isStrict = effectiveOp === "less";
59569
- if (bounds.upperBound !== void 0) {
59570
- const upperVal = isNumber(bounds.upperBound) ? bounds.upperBound.numericValue : void 0;
60692
+ if (bounds.upper !== void 0) {
60693
+ const upperVal = isNumber(bounds.upper) ? bounds.upper.numericValue : void 0;
59571
60694
  if (typeof upperVal === "number" && isFinite(upperVal)) {
59572
60695
  if (isStrict) {
59573
60696
  if (upperVal < k) return "tautology";
@@ -59578,8 +60701,8 @@ function assumeInequality(proposition) {
59578
60701
  }
59579
60702
  }
59580
60703
  }
59581
- if (bounds.lowerBound !== void 0) {
59582
- const lowerVal = isNumber(bounds.lowerBound) ? bounds.lowerBound.numericValue : void 0;
60704
+ if (bounds.lower !== void 0) {
60705
+ const lowerVal = isNumber(bounds.lower) ? bounds.lower.numericValue : void 0;
59583
60706
  if (typeof lowerVal === "number" && isFinite(lowerVal)) {
59584
60707
  if (isStrict) {
59585
60708
  if (lowerVal > k) return "contradiction";
@@ -59807,7 +60930,7 @@ function ask(ce, pattern) {
59807
60930
  const patOp1B2 = pat.op1;
59808
60931
  if (isSymbol2(patOp1B2)) {
59809
60932
  const bounds = getInequalityBoundsFromAssumptions(ce, patOp1B2.symbol);
59810
- const bound = isLower ? bounds.lowerBound : bounds.upperBound;
60933
+ const bound = isLower ? bounds.lower : bounds.upper;
59811
60934
  const strictOk = isLower ? bounds.lowerStrict : bounds.upperStrict;
59812
60935
  if (bound !== void 0 && (!isStrict || strictOk === true))
59813
60936
  pushResult({ [boundWildcard]: bound });
@@ -59817,7 +60940,7 @@ function ask(ce, pattern) {
59817
60940
  if (symbolWildcard && !symbolWildcard.startsWith("__")) {
59818
60941
  for (const s of candidatesFromAssumptions()) {
59819
60942
  const bounds = getInequalityBoundsFromAssumptions(ce, s);
59820
- const bound = isLower ? bounds.lowerBound : bounds.upperBound;
60943
+ const bound = isLower ? bounds.lower : bounds.upper;
59821
60944
  const strictOk = isLower ? bounds.lowerStrict : bounds.upperStrict;
59822
60945
  if (bound === void 0 || isStrict && strictOk !== true)
59823
60946
  continue;
@@ -60849,6 +61972,7 @@ var JAVASCRIPT_FUNCTIONS = {
60849
61972
  return `_SYS.cexp(${compile3(args[0])})`;
60850
61973
  return `Math.exp(${compile3(args[0])})`;
60851
61974
  },
61975
+ First: (args, compile3) => `${compile3(args[0])}[0]`,
60852
61976
  Floor: (args, compile3) => {
60853
61977
  if (BaseCompiler.isIntegerValued(args[0])) return compile3(args[0]);
60854
61978
  return `Math.floor(${compile3(args[0])})`;
@@ -61007,7 +62131,20 @@ var JAVASCRIPT_FUNCTIONS = {
61007
62131
  if (nConst !== void 0) return `Math.pow(${compile3(arg)}, ${1 / nConst})`;
61008
62132
  return `Math.pow(${compile3(arg)}, 1 / (${compile3(exp3)}))`;
61009
62133
  },
61010
- Random: "Math.random",
62134
+ Random: (args, compile3) => {
62135
+ if (args.length === 0) return "Math.random()";
62136
+ if (args.length === 2) {
62137
+ const m = compile3(args[0]);
62138
+ const n = compile3(args[1]);
62139
+ return `((${m}) + Math.floor(Math.random() * ((${n}) - (${m}))))`;
62140
+ }
62141
+ const arg = args[0];
62142
+ if (BaseCompiler.isIntegerValued(arg)) {
62143
+ return `Math.floor(Math.random() * (${compile3(arg)}))`;
62144
+ }
62145
+ const a = compile3(arg);
62146
+ return `(() => { const _s = (${a}) * 12.9898; const _v = Math.sin(_s) * 43758.5453; return _v - Math.floor(_v); })()`;
62147
+ },
61011
62148
  Round: (args, compile3) => {
61012
62149
  if (BaseCompiler.isIntegerValued(args[0])) return compile3(args[0]);
61013
62150
  return `Math.round(${compile3(args[0])})`;
@@ -61035,6 +62172,7 @@ var JAVASCRIPT_FUNCTIONS = {
61035
62172
  if (BaseCompiler.isComplexValued(arg)) return `_SYS.csech(${compile3(arg)})`;
61036
62173
  return `1 / Math.cosh(${compile3(arg)})`;
61037
62174
  },
62175
+ Second: (args, compile3) => `${compile3(args[0])}[1]`,
61038
62176
  Heaviside: "_SYS.heaviside",
61039
62177
  Sign: "Math.sign",
61040
62178
  Sinc: "_SYS.sinc",
@@ -61067,6 +62205,7 @@ var JAVASCRIPT_FUNCTIONS = {
61067
62205
  return `_SYS.ctanh(${compile3(args[0])})`;
61068
62206
  return `Math.tanh(${compile3(args[0])})`;
61069
62207
  },
62208
+ Third: (args, compile3) => `${compile3(args[0])}[2]`,
61070
62209
  Mod: ([a, b], compile3) => {
61071
62210
  if (a === null || b === null) throw new Error("Mod: missing argument");
61072
62211
  const ca = compile3(a);
@@ -62346,6 +63485,14 @@ var GPU_FUNCTIONS = {
62346
63485
  return `exp(${compile3(args[0])})`;
62347
63486
  },
62348
63487
  Exp2: "exp2",
63488
+ // Component access — assumes the argument compiles to a vec2/vec3/vec4
63489
+ // (the common case for 2D/3D points). For 5+-element tuples that compile
63490
+ // to `float[N]` arrays, swizzle access is invalid GLSL and the shader
63491
+ // will fail to compile; that's an edge case `First`/`Second`/`Third`
63492
+ // aren't designed for. Vec swizzles are identical between GLSL and WGSL.
63493
+ First: (args, compile3) => `${compile3(args[0])}.x`,
63494
+ Second: (args, compile3) => `${compile3(args[0])}.y`,
63495
+ Third: (args, compile3) => `${compile3(args[0])}.z`,
62349
63496
  Floor: (args, compile3) => {
62350
63497
  if (BaseCompiler.isIntegerValued(args[0])) return compile3(args[0]);
62351
63498
  return `floor(${compile3(args[0])})`;
@@ -62823,6 +63970,39 @@ var GPU_FUNCTIONS = {
62823
63970
  // Sum/Product — unrolled or for-loop
62824
63971
  Sum: (args, compile3, target) => compileGPUSumProduct("Sum", args, compile3, target),
62825
63972
  Product: (args, compile3, target) => compileGPUSumProduct("Product", args, compile3, target),
63973
+ // Range — inline constant array literal (bounds must be compile-time constants)
63974
+ Range: (args, _compile2, target) => {
63975
+ if (args.length < 2 || args.length > 3) {
63976
+ throw new Error(
63977
+ "Range: GPU compile expects 2 or 3 arguments (lo, hi, step?)"
63978
+ );
63979
+ }
63980
+ const lo = args[0].re;
63981
+ const hi = args[1].re;
63982
+ const step = args.length === 3 ? args[2].re : 1;
63983
+ if (!Number.isFinite(lo) || !Number.isFinite(hi) || !Number.isFinite(step)) {
63984
+ throw new Error(
63985
+ "Range: GPU compile requires constant numeric bounds (non-constant ranges must be materialized at JS host then uploaded as a uniform)"
63986
+ );
63987
+ }
63988
+ if (step === 0) throw new Error("Range: step cannot be zero");
63989
+ const count = Math.max(0, Math.floor((hi - lo) / step) + 1);
63990
+ if (count === 0) {
63991
+ throw new Error(
63992
+ "Range: empty range (lo > hi for positive step, or lo < hi for negative step)"
63993
+ );
63994
+ }
63995
+ if (count > 256) {
63996
+ throw new Error(
63997
+ `Range: GPU compile inlines ranges up to 256 elements (got ${count})`
63998
+ );
63999
+ }
64000
+ const values = [];
64001
+ for (let i = 0; i < count; i++) values.push(lo + i * step);
64002
+ const isWGSL = target.language === "wgsl";
64003
+ const arrayType = isWGSL ? `array<f32, ${count}>` : `float[${count}]`;
64004
+ return `${arrayType}(${values.map(formatGPUNumber).join(", ")})`;
64005
+ },
62826
64006
  // Loop — GPU for-loop (no IIFE, no let)
62827
64007
  Loop: (args, _compile2, target) => {
62828
64008
  if (!args[0]) throw new Error("Loop: no body");
@@ -62851,6 +64031,134 @@ var GPU_FUNCTIONS = {
62851
64031
  ${bodyCode};
62852
64032
  }`;
62853
64033
  },
64034
+ // Statistical functions
64035
+ /**
64036
+ * GCD of two scalar arguments.
64037
+ *
64038
+ * Uses a preamble helper `_gpu_gcd` (Euclidean algorithm via `mod`).
64039
+ * Only two-argument form is supported in GPU targets.
64040
+ */
64041
+ GCD: (args, compile3) => {
64042
+ if (args.length < 2) throw new Error("GCD: need at least two arguments");
64043
+ if (args.length > 2)
64044
+ throw new Error("GCD: GPU target supports only two-argument GCD");
64045
+ const a = args[0];
64046
+ const b = args[1];
64047
+ if (a === null || b === null) throw new Error("GCD: missing argument");
64048
+ return `_gpu_gcd(${compile3(a)}, ${compile3(b)})`;
64049
+ },
64050
+ /**
64051
+ * Variance of a compile-time-known list.
64052
+ *
64053
+ * Accepts either a single `List(...)` argument or N scalar arguments.
64054
+ * Generates fully inline code: computes mean then sum of squared deviations,
64055
+ * divided by (N-1) for sample variance (matches JS `_SYS.variance`).
64056
+ */
64057
+ Variance: (args, compile3) => {
64058
+ let elems;
64059
+ if (args.length === 1 && isFunction2(args[0], "List")) {
64060
+ elems = args[0].ops;
64061
+ } else if (args.length >= 2) {
64062
+ elems = args;
64063
+ } else {
64064
+ throw new Error(
64065
+ "Variance: GPU target requires a List argument or at least 2 scalar arguments"
64066
+ );
64067
+ }
64068
+ const n = elems.length;
64069
+ if (n < 2) throw new Error("Variance: need at least 2 elements");
64070
+ const compiled = elems.map((e) => compile3(e));
64071
+ const sum = compiled.join(" + ");
64072
+ const mean2 = `((${sum}) / ${formatGPUNumber(n)})`;
64073
+ const sqDiffs = compiled.map((c) => `(${c} - ${mean2}) * (${c} - ${mean2})`).join(" + ");
64074
+ return `((${sqDiffs}) / ${formatGPUNumber(n - 1)})`;
64075
+ },
64076
+ /**
64077
+ * Median of a compile-time-known list.
64078
+ *
64079
+ * Accepts either a single `List(...)` argument or N scalar arguments.
64080
+ * For N ≤ 8: generates a fully unrolled inline sorting network followed by
64081
+ * a middle-element pick. For larger N, throws (too large to inline cleanly).
64082
+ *
64083
+ * The sorting network uses the "odd-even merge sort" comparator pattern
64084
+ * inlined as `min`/`max` calls — no GPU statements required.
64085
+ */
64086
+ Median: (args, compile3) => {
64087
+ let elems;
64088
+ if (args.length === 1 && isFunction2(args[0], "List")) {
64089
+ elems = args[0].ops;
64090
+ } else if (args.length >= 1) {
64091
+ elems = args;
64092
+ } else {
64093
+ throw new Error(
64094
+ "Median: GPU target requires a List argument or at least 1 scalar argument"
64095
+ );
64096
+ }
64097
+ const n = elems.length;
64098
+ if (n === 0) throw new Error("Median: empty list");
64099
+ if (n > 8) {
64100
+ throw new Error(
64101
+ `Median: GPU target supports up to 8 elements via inline sorting network (got ${n}). For larger lists, compute on the CPU and pass the result as a uniform.`
64102
+ );
64103
+ }
64104
+ const compiled = elems.map((e) => compile3(e));
64105
+ if (n === 1) return compiled[0];
64106
+ return `_gpu_median_${n}(${compiled.join(", ")})`;
64107
+ },
64108
+ /**
64109
+ * Deterministic pseudorandom for GPU.
64110
+ *
64111
+ * All emitted forms return a GLSL `float` (or WGSL `f32`) so the result
64112
+ * composes with surrounding float arithmetic without explicit casts. The
64113
+ * "integer-bound" forms return an integer-valued float (the result of
64114
+ * `floor`), matching the convention used by `Floor` and other ostensibly
64115
+ * integer-returning operators in this target.
64116
+ *
64117
+ * - 0 args (GLSL only): fall back to a fragment-coord-derived seed.
64118
+ * Only meaningful in fragment shaders (gl_FragCoord is FS-only).
64119
+ * - 0 args (WGSL): throws — WGSL has no built-in fragment coordinate;
64120
+ * caller must provide an explicit seed.
64121
+ * - 1 arg, real-typed: `_gpu_random(seed)` — deterministic float in [0, 1)
64122
+ * - 1 arg, integer-typed: `floor(_gpu_random(float(n)) * float(n))` —
64123
+ * integer-valued float in {0, 1, ..., n-1}. The seed is derived from
64124
+ * `n` itself, so the result is per-pixel-and-n deterministic in GLSL.
64125
+ * - 2 args (integer m, n): float in [m, n), seeded from gl_FragCoord.
64126
+ *
64127
+ * JS-side `Random` has matching semantics (see `library/core.ts`'s
64128
+ * polymorphic dispatch). JS↔GLSL parity is approximate — same seed yields
64129
+ * a similar value, not bit-identical, due to fp64 vs fp32 and platform
64130
+ * `sin` differences.
64131
+ */
64132
+ Random: (args, compile3, target) => {
64133
+ if (args.length === 0) {
64134
+ if (target.language === "wgsl") {
64135
+ throw new Error(
64136
+ "Random(): WGSL compile requires an explicit seed argument. WGSL has no gl_FragCoord built-in outside fragment entry points, so the no-arg fallback used in GLSL is unavailable. Use Random(seed) where seed is a deterministic per-invocation value."
64137
+ );
64138
+ }
64139
+ return "_gpu_random(gl_FragCoord.x + gl_FragCoord.y * 1024.0)";
64140
+ }
64141
+ if (args.length === 1) {
64142
+ const arg = args[0];
64143
+ if (BaseCompiler.isIntegerValued(arg)) {
64144
+ const compiled = compile3(arg);
64145
+ return `floor(_gpu_random(float(${compiled})) * float(${compiled}))`;
64146
+ }
64147
+ return `_gpu_random(${compile3(arg)})`;
64148
+ }
64149
+ if (args.length === 2) {
64150
+ if (target.language === "wgsl") {
64151
+ throw new Error(
64152
+ "Random(m, n): WGSL compile requires explicit seeding. Use a seeded variant or compute the integer range manually."
64153
+ );
64154
+ }
64155
+ const m = compile3(args[0]);
64156
+ const n = compile3(args[1]);
64157
+ const seed = "_gpu_random(gl_FragCoord.x + gl_FragCoord.y * 1024.0)";
64158
+ return `(float(${m}) + floor(${seed} * float((${n}) - (${m}))))`;
64159
+ }
64160
+ throw new Error("Random: GPU compile expects 0, 1, or 2 arguments");
64161
+ },
62854
64162
  // Function (lambda) — not supported in GPU
62855
64163
  Function: () => {
62856
64164
  throw new Error(
@@ -63450,6 +64758,212 @@ fn _fractal_julia(z_in: vec2f, c: vec2f, maxIter: i32) -> f32 {
63450
64758
  return 1.0;
63451
64759
  }
63452
64760
  `;
64761
+ var GPU_GCD_PREAMBLE_GLSL = `
64762
+ float _gpu_gcd(float a, float b) {
64763
+ a = abs(a); b = abs(b);
64764
+ for (int i = 0; i < 32; i++) {
64765
+ if (b < 0.5) break;
64766
+ float t = mod(a, b);
64767
+ a = b;
64768
+ b = t;
64769
+ }
64770
+ return a;
64771
+ }
64772
+ `;
64773
+ var GPU_GCD_PREAMBLE_WGSL = `
64774
+ fn _gpu_gcd(a_in: f32, b_in: f32) -> f32 {
64775
+ var a = abs(a_in); var b = abs(b_in);
64776
+ for (var i: i32 = 0; i < 32; i++) {
64777
+ if (b < 0.5) { break; }
64778
+ let t = a % b;
64779
+ a = b;
64780
+ b = t;
64781
+ }
64782
+ return a;
64783
+ }
64784
+ `;
64785
+ var GPU_RANDOM_PREAMBLE_GLSL = `
64786
+ // Deterministic pseudorandom in [0, 1) from a float seed.
64787
+ // Standard fract-sin hash; reproducible across runs for the same seed.
64788
+ // Note: this hash exhibits visible banding near seed \u2248 k\u03C0 for integer k.
64789
+ // For high-quality shader random, callers should use a more robust hash
64790
+ // (e.g. PCG or xxHash) and pre-seed it appropriately.
64791
+ float _gpu_random(float seed) {
64792
+ return fract(sin(seed * 12.9898) * 43758.5453);
64793
+ }
64794
+ `;
64795
+ var GPU_RANDOM_PREAMBLE_WGSL = `
64796
+ // Deterministic pseudorandom in [0, 1) from a float seed.
64797
+ // Standard fract-sin hash; reproducible across runs for the same seed.
64798
+ // Note: this hash exhibits visible banding near seed \u2248 k\u03C0 for integer k.
64799
+ // For high-quality shader random, callers should use a more robust hash
64800
+ // (e.g. PCG or xxHash) and pre-seed it appropriately.
64801
+ fn _gpu_random(seed: f32) -> f32 {
64802
+ return fract(sin(seed * 12.9898) * 43758.5453);
64803
+ }
64804
+ `;
64805
+ var GPU_MEDIAN_PREAMBLE_GLSL = `
64806
+ float _gpu_median_2(float a, float b) {
64807
+ return (a + b) * 0.5;
64808
+ }
64809
+ float _gpu_median_3(float a, float b, float c) {
64810
+ return max(min(a, b), min(max(a, b), c));
64811
+ }
64812
+ float _gpu_median_4(float a, float b, float c, float d) {
64813
+ float lo = max(min(a, b), min(c, d));
64814
+ float hi = min(max(a, b), max(c, d));
64815
+ return (lo + hi) * 0.5;
64816
+ }
64817
+ float _gpu_median_5(float a, float b, float c, float d, float e) {
64818
+ // 9-comparator Bose-Nelson sort; v2 holds the median.
64819
+ float t; float v0=a,v1=b,v2=c,v3=d,v4=e;
64820
+ t=min(v0,v1); v1=max(v0,v1); v0=t;
64821
+ t=min(v3,v4); v4=max(v3,v4); v3=t;
64822
+ t=min(v2,v4); v4=max(v2,v4); v2=t;
64823
+ t=min(v2,v3); v3=max(v2,v3); v2=t;
64824
+ t=min(v0,v3); v3=max(v0,v3); v0=t;
64825
+ t=min(v0,v2); v2=max(v0,v2); v0=t;
64826
+ t=min(v1,v4); v4=max(v1,v4); v1=t;
64827
+ t=min(v1,v3); v3=max(v1,v3); v1=t;
64828
+ t=min(v1,v2); v2=max(v1,v2); v1=t;
64829
+ return v2;
64830
+ }
64831
+ float _gpu_median_6(float a, float b, float c, float d, float e, float f) {
64832
+ float t; float v0=a,v1=b,v2=c,v3=d,v4=e,v5=f;
64833
+ t=min(v0,v1); v1=max(v0,v1); v0=t;
64834
+ t=min(v2,v3); v3=max(v2,v3); v2=t;
64835
+ t=min(v4,v5); v5=max(v4,v5); v4=t;
64836
+ t=min(v0,v2); v2=max(v0,v2); v0=t;
64837
+ t=min(v1,v3); v3=max(v1,v3); v1=t;
64838
+ t=min(v0,v4); v4=max(v0,v4); v0=t;
64839
+ t=min(v1,v5); v5=max(v1,v5); v1=t;
64840
+ t=min(v2,v4); v4=max(v2,v4); v2=t;
64841
+ t=min(v1,v2); v2=max(v1,v2); v1=t;
64842
+ t=min(v3,v5); v5=max(v3,v5); v3=t;
64843
+ t=min(v3,v4); v4=max(v3,v4); v3=t;
64844
+ return (v2 + v3) * 0.5;
64845
+ }
64846
+ float _gpu_median_7(float a, float b, float c, float d, float e, float f, float g) {
64847
+ float t; float v0=a,v1=b,v2=c,v3=d,v4=e,v5=f,v6=g;
64848
+ t=min(v0,v1); v1=max(v0,v1); v0=t;
64849
+ t=min(v2,v3); v3=max(v2,v3); v2=t;
64850
+ t=min(v4,v5); v5=max(v4,v5); v4=t;
64851
+ t=min(v0,v2); v2=max(v0,v2); v0=t;
64852
+ t=min(v1,v3); v3=max(v1,v3); v1=t;
64853
+ t=min(v4,v6); v6=max(v4,v6); v4=t;
64854
+ t=min(v0,v4); v4=max(v0,v4); v0=t;
64855
+ t=min(v1,v5); v5=max(v1,v5); v1=t;
64856
+ t=min(v2,v6); v6=max(v2,v6); v2=t;
64857
+ t=min(v1,v2); v2=max(v1,v2); v1=t;
64858
+ t=min(v3,v5); v5=max(v3,v5); v3=t;
64859
+ t=min(v2,v4); v4=max(v2,v4); v2=t;
64860
+ t=min(v3,v4); v4=max(v3,v4); v3=t;
64861
+ return v3;
64862
+ }
64863
+ float _gpu_median_8(float a, float b, float c, float d, float e, float f, float g, float h) {
64864
+ float t; float v0=a,v1=b,v2=c,v3=d,v4=e,v5=f,v6=g,v7=h;
64865
+ t=min(v0,v1); v1=max(v0,v1); v0=t;
64866
+ t=min(v2,v3); v3=max(v2,v3); v2=t;
64867
+ t=min(v4,v5); v5=max(v4,v5); v4=t;
64868
+ t=min(v6,v7); v7=max(v6,v7); v6=t;
64869
+ t=min(v0,v2); v2=max(v0,v2); v0=t;
64870
+ t=min(v1,v3); v3=max(v1,v3); v1=t;
64871
+ t=min(v4,v6); v6=max(v4,v6); v4=t;
64872
+ t=min(v5,v7); v7=max(v5,v7); v5=t;
64873
+ t=min(v0,v4); v4=max(v0,v4); v0=t;
64874
+ t=min(v1,v5); v5=max(v1,v5); v1=t;
64875
+ t=min(v2,v6); v6=max(v2,v6); v2=t;
64876
+ t=min(v3,v7); v7=max(v3,v7); v3=t;
64877
+ t=min(v1,v2); v2=max(v1,v2); v1=t;
64878
+ t=min(v3,v4); v4=max(v3,v4); v3=t;
64879
+ t=min(v5,v6); v6=max(v5,v6); v5=t;
64880
+ t=min(v3,v5); v5=max(v3,v5); v3=t;
64881
+ t=min(v2,v4); v4=max(v2,v4); v2=t;
64882
+ t=min(v3,v4); v4=max(v3,v4); v3=t;
64883
+ return (v3 + v4) * 0.5;
64884
+ }
64885
+ `;
64886
+ var GPU_MEDIAN_PREAMBLE_WGSL = `
64887
+ fn _gpu_median_2(a: f32, b: f32) -> f32 {
64888
+ return (a + b) * 0.5;
64889
+ }
64890
+ fn _gpu_median_3(a: f32, b: f32, c: f32) -> f32 {
64891
+ return max(min(a, b), min(max(a, b), c));
64892
+ }
64893
+ fn _gpu_median_4(a: f32, b: f32, c: f32, d: f32) -> f32 {
64894
+ let lo = max(min(a, b), min(c, d));
64895
+ let hi = min(max(a, b), max(c, d));
64896
+ return (lo + hi) * 0.5;
64897
+ }
64898
+ fn _gpu_median_5(a: f32, b: f32, c: f32, d: f32, e: f32) -> f32 {
64899
+ // 9-comparator Bose-Nelson sort; v2 holds the median.
64900
+ var v0=a; var v1=b; var v2=c; var v3=d; var v4=e; var t: f32;
64901
+ t=min(v0,v1); v1=max(v0,v1); v0=t;
64902
+ t=min(v3,v4); v4=max(v3,v4); v3=t;
64903
+ t=min(v2,v4); v4=max(v2,v4); v2=t;
64904
+ t=min(v2,v3); v3=max(v2,v3); v2=t;
64905
+ t=min(v0,v3); v3=max(v0,v3); v0=t;
64906
+ t=min(v0,v2); v2=max(v0,v2); v0=t;
64907
+ t=min(v1,v4); v4=max(v1,v4); v1=t;
64908
+ t=min(v1,v3); v3=max(v1,v3); v1=t;
64909
+ t=min(v1,v2); v2=max(v1,v2); v1=t;
64910
+ return v2;
64911
+ }
64912
+ fn _gpu_median_6(a: f32, b: f32, c: f32, d: f32, e: f32, f: f32) -> f32 {
64913
+ var v0=a; var v1=b; var v2=c; var v3=d; var v4=e; var v5=f; var t: f32;
64914
+ t=min(v0,v1); v1=max(v0,v1); v0=t;
64915
+ t=min(v2,v3); v3=max(v2,v3); v2=t;
64916
+ t=min(v4,v5); v5=max(v4,v5); v4=t;
64917
+ t=min(v0,v2); v2=max(v0,v2); v0=t;
64918
+ t=min(v1,v3); v3=max(v1,v3); v1=t;
64919
+ t=min(v0,v4); v4=max(v0,v4); v0=t;
64920
+ t=min(v1,v5); v5=max(v1,v5); v1=t;
64921
+ t=min(v2,v4); v4=max(v2,v4); v2=t;
64922
+ t=min(v1,v2); v2=max(v1,v2); v1=t;
64923
+ t=min(v3,v5); v5=max(v3,v5); v3=t;
64924
+ t=min(v3,v4); v4=max(v3,v4); v3=t;
64925
+ return (v2 + v3) * 0.5;
64926
+ }
64927
+ fn _gpu_median_7(a: f32, b: f32, c: f32, d: f32, e: f32, f: f32, g: f32) -> f32 {
64928
+ var v0=a; var v1=b; var v2=c; var v3=d; var v4=e; var v5=f; var v6=g; var t: f32;
64929
+ t=min(v0,v1); v1=max(v0,v1); v0=t;
64930
+ t=min(v2,v3); v3=max(v2,v3); v2=t;
64931
+ t=min(v4,v5); v5=max(v4,v5); v4=t;
64932
+ t=min(v0,v2); v2=max(v0,v2); v0=t;
64933
+ t=min(v1,v3); v3=max(v1,v3); v1=t;
64934
+ t=min(v4,v6); v6=max(v4,v6); v4=t;
64935
+ t=min(v0,v4); v4=max(v0,v4); v0=t;
64936
+ t=min(v1,v5); v5=max(v1,v5); v1=t;
64937
+ t=min(v2,v6); v6=max(v2,v6); v2=t;
64938
+ t=min(v1,v2); v2=max(v1,v2); v1=t;
64939
+ t=min(v3,v5); v5=max(v3,v5); v3=t;
64940
+ t=min(v2,v4); v4=max(v2,v4); v2=t;
64941
+ t=min(v3,v4); v4=max(v3,v4); v3=t;
64942
+ return v3;
64943
+ }
64944
+ fn _gpu_median_8(a: f32, b: f32, c: f32, d: f32, e: f32, f: f32, g: f32, h: f32) -> f32 {
64945
+ var v0=a; var v1=b; var v2=c; var v3=d; var v4=e; var v5=f; var v6=g; var v7=h; var t: f32;
64946
+ t=min(v0,v1); v1=max(v0,v1); v0=t;
64947
+ t=min(v2,v3); v3=max(v2,v3); v2=t;
64948
+ t=min(v4,v5); v5=max(v4,v5); v4=t;
64949
+ t=min(v6,v7); v7=max(v6,v7); v6=t;
64950
+ t=min(v0,v2); v2=max(v0,v2); v0=t;
64951
+ t=min(v1,v3); v3=max(v1,v3); v1=t;
64952
+ t=min(v4,v6); v6=max(v4,v6); v4=t;
64953
+ t=min(v5,v7); v7=max(v5,v7); v5=t;
64954
+ t=min(v0,v4); v4=max(v0,v4); v0=t;
64955
+ t=min(v1,v5); v5=max(v1,v5); v1=t;
64956
+ t=min(v2,v6); v6=max(v2,v6); v2=t;
64957
+ t=min(v3,v7); v7=max(v3,v7); v3=t;
64958
+ t=min(v1,v2); v2=max(v1,v2); v1=t;
64959
+ t=min(v3,v4); v4=max(v3,v4); v3=t;
64960
+ t=min(v5,v6); v6=max(v5,v6); v5=t;
64961
+ t=min(v3,v5); v5=max(v3,v5); v3=t;
64962
+ t=min(v2,v4); v4=max(v2,v4); v2=t;
64963
+ t=min(v3,v4); v4=max(v3,v4); v3=t;
64964
+ return (v3 + v4) * 0.5;
64965
+ }
64966
+ `;
63453
64967
  var GPU_COLOR_PREAMBLE_GLSL = `
63454
64968
  float _gpu_srgb_to_linear(float c) {
63455
64969
  if (c <= 0.04045) return c / 12.92;
@@ -64127,6 +65641,12 @@ var GPUShaderTarget = class {
64127
65641
  if (code.includes("_fractal_")) {
64128
65642
  preamble += this.languageId === "wgsl" ? GPU_FRACTAL_PREAMBLE_WGSL : GPU_FRACTAL_PREAMBLE_GLSL;
64129
65643
  }
65644
+ if (code.includes("_gpu_random"))
65645
+ preamble += this.languageId === "wgsl" ? GPU_RANDOM_PREAMBLE_WGSL : GPU_RANDOM_PREAMBLE_GLSL;
65646
+ if (code.includes("_gpu_gcd"))
65647
+ preamble += this.languageId === "wgsl" ? GPU_GCD_PREAMBLE_WGSL : GPU_GCD_PREAMBLE_GLSL;
65648
+ if (code.includes("_gpu_median_"))
65649
+ preamble += this.languageId === "wgsl" ? GPU_MEDIAN_PREAMBLE_WGSL : GPU_MEDIAN_PREAMBLE_GLSL;
64130
65650
  if (code.includes("_gpu_srgb_to") || code.includes("_gpu_oklab") || code.includes("_gpu_oklch") || code.includes("_gpu_color_mix") || code.includes("_gpu_apca")) {
64131
65651
  preamble += this.languageId === "wgsl" ? GPU_COLOR_PREAMBLE_WGSL : GPU_COLOR_PREAMBLE_GLSL;
64132
65652
  }
@@ -66352,6 +67872,7 @@ var EngineRuntimeState = class {
66352
67872
  _timeLimit = 2e3;
66353
67873
  _iterationLimit = 1024;
66354
67874
  _recursionLimit = 1024;
67875
+ _maxCollectionSize = 1e4;
66355
67876
  _deadline = void 0;
66356
67877
  _isVerifying = false;
66357
67878
  get timeLimit() {
@@ -66372,6 +67893,12 @@ var EngineRuntimeState = class {
66372
67893
  set recursionLimit(value) {
66373
67894
  this._recursionLimit = value <= 0 ? Number.POSITIVE_INFINITY : value;
66374
67895
  }
67896
+ get maxCollectionSize() {
67897
+ return this._maxCollectionSize;
67898
+ }
67899
+ set maxCollectionSize(value) {
67900
+ this._maxCollectionSize = value <= 0 ? Number.POSITIVE_INFINITY : value;
67901
+ }
66375
67902
  get deadline() {
66376
67903
  return this._deadline;
66377
67904
  }
@@ -68410,6 +69937,23 @@ var ComputeEngine = class _ComputeEngine {
68410
69937
  set recursionLimit(t) {
68411
69938
  this._runtimeState.recursionLimit = t;
68412
69939
  }
69940
+ /** Maximum number of elements a collection may have when materialized
69941
+ * (converted from a lazy form to a `List`). Default: 10,000.
69942
+ *
69943
+ * When a materialization would exceed this size, the operation leaves
69944
+ * the expression in its lazy form. Consumers can detect oversize
69945
+ * collections via the symbolic form's `count`.
69946
+ *
69947
+ * Set to `Infinity` (or `0` / a negative number) to disable the cap.
69948
+ *
69949
+ * @experimental
69950
+ */
69951
+ get maxCollectionSize() {
69952
+ return this._runtimeState.maxCollectionSize;
69953
+ }
69954
+ set maxCollectionSize(t) {
69955
+ this._runtimeState.maxCollectionSize = t;
69956
+ }
68413
69957
  /**
68414
69958
  * Flag to prevent infinite recursion in the verify/ask/equality checking cycle.
68415
69959
  *
@@ -68686,6 +70230,36 @@ var ComputeEngine = class _ComputeEngine {
68686
70230
  lookupDefinition(id) {
68687
70231
  return lookupDefinition(this, id);
68688
70232
  }
70233
+ normalizeIdentifier(latex) {
70234
+ if (!latex) return "";
70235
+ if (isValidSymbol(latex)) return latex;
70236
+ this.pushScope();
70237
+ try {
70238
+ const expr2 = this.parse(latex);
70239
+ if (isSymbol2(expr2)) return expr2.symbol;
70240
+ } finally {
70241
+ this.popScope();
70242
+ }
70243
+ return "";
70244
+ }
70245
+ operatorInfo(head) {
70246
+ const def = this.lookupDefinition(head);
70247
+ if (!def || !isOperatorDef(def)) return void 0;
70248
+ const op = def.operator;
70249
+ return {
70250
+ kind: op.evaluate || op.collection ? "function" : "opaque",
70251
+ signature: op.signature
70252
+ };
70253
+ }
70254
+ symbolInfo(name) {
70255
+ const def = this.lookupDefinition(name);
70256
+ if (!def || !isValueDef(def)) return void 0;
70257
+ const v = def.value;
70258
+ return {
70259
+ kind: v.isConstant ? "constant" : "variable",
70260
+ type: v.type
70261
+ };
70262
+ }
68689
70263
  /**
68690
70264
  * Associate a new definition to a symbol in the current context.
68691
70265
  *
@@ -68973,6 +70547,7 @@ var ComputeEngine = class _ComputeEngine {
68973
70547
  const def = this.lookupDefinition(id);
68974
70548
  return !!(isValueDef(def) && def.value.subscriptEvaluate);
68975
70549
  },
70550
+ tolerance: this.tolerance,
68976
70551
  ...this._latexOptions,
68977
70552
  ...parseOpts
68978
70553
  });
@@ -69137,7 +70712,7 @@ var ComputeEngine = class _ComputeEngine {
69137
70712
  _setDefaultEngineFactory(() => new ComputeEngine());
69138
70713
 
69139
70714
  // src/core.ts
69140
- var version = "0.56.0";
70715
+ var version = "0.58.0";
69141
70716
  export {
69142
70717
  ComputeEngine,
69143
70718
  N,