@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
@@ -1,4 +1,4 @@
1
- /** Compute Engine 0.56.0 */
1
+ /** Compute Engine 0.58.0 */
2
2
  (function(global,factory){typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'],factory):(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ComputeEngine = {}));})(this, (function (exports) { 'use strict';
3
3
  var ComputeEngine = (() => {
4
4
  var __defProp = Object.defineProperty;
@@ -4344,7 +4344,42 @@ var ComputeEngine = (() => {
4344
4344
  if (b === "nothing") return a;
4345
4345
  if (isSubtype(a, b)) return b;
4346
4346
  if (isSubtype(b, a)) return a;
4347
- return superType(a, b);
4347
+ const sup = superType(a, b);
4348
+ if (LOSSY_SUPERTYPE.has(sup)) return unionTypes(a, b);
4349
+ return sup;
4350
+ }
4351
+ var LOSSY_SUPERTYPE = /* @__PURE__ */ new Set([
4352
+ "scalar",
4353
+ "value",
4354
+ "function",
4355
+ "expression",
4356
+ "collection",
4357
+ "indexed_collection",
4358
+ "list",
4359
+ "set",
4360
+ "tuple",
4361
+ "record",
4362
+ "dictionary",
4363
+ "map",
4364
+ "any"
4365
+ ]);
4366
+ function unionTypes(a, b) {
4367
+ const members = [];
4368
+ const push = (t) => {
4369
+ if (typeof t === "object" && t.kind === "union") {
4370
+ for (const m of t.types) push(m);
4371
+ return;
4372
+ }
4373
+ const key = typeof t === "string" ? t : JSON.stringify(t);
4374
+ if (!members.some(
4375
+ (m) => (typeof m === "string" ? m : JSON.stringify(m)) === key
4376
+ ))
4377
+ members.push(t);
4378
+ };
4379
+ push(a);
4380
+ push(b);
4381
+ if (members.length === 1) return members[0];
4382
+ return { kind: "union", types: members };
4348
4383
  }
4349
4384
  function narrow(...types) {
4350
4385
  if (types.length === 0) return "nothing";
@@ -4422,7 +4457,7 @@ var ComputeEngine = (() => {
4422
4457
  if (type2.kind === "set") return type2.elements;
4423
4458
  if (type2.kind === "tuple") return widen(...type2.elements.map((x) => x.type));
4424
4459
  if (type2.kind === "dictionary")
4425
- return parseType(`tuple<string, ${type2.values}>`);
4460
+ return parseType(`tuple<string, ${typeToString(type2.values)}>`);
4426
4461
  if (type2.kind === "record") {
4427
4462
  return parseType(
4428
4463
  `tuple<string, ${typeToString(widen(...Object.values(type2.elements)))}>`
@@ -5968,6 +6003,84 @@ var ComputeEngine = (() => {
5968
6003
  }
5969
6004
 
5970
6005
  // src/compute-engine/boxed-expression/inequality-bounds.ts
6006
+ function extractIntervalBounds(expr2, symbol2) {
6007
+ if (isFunction2(expr2, "When")) {
6008
+ const cond = expr2.op2;
6009
+ if (!cond) return void 0;
6010
+ return extractIntervalBounds(cond, symbol2);
6011
+ }
6012
+ if (isFunction2(expr2, "Multiply")) {
6013
+ const ops = expr2.ops;
6014
+ if (!ops) return void 0;
6015
+ const merged = {};
6016
+ for (const sub2 of ops) {
6017
+ if (isFunction2(sub2, "When")) {
6018
+ const sub_ = extractIntervalBounds(sub2, symbol2);
6019
+ if (sub_ !== void 0) _mergeBounds(merged, sub_);
6020
+ }
6021
+ }
6022
+ return _hasAnyBound(merged) ? merged : void 0;
6023
+ }
6024
+ const result = {};
6025
+ if (isFunction2(expr2, "And")) {
6026
+ const ops = expr2.ops;
6027
+ if (!ops) return void 0;
6028
+ for (const sub2 of ops) {
6029
+ const subBounds = extractIntervalBounds(sub2, symbol2);
6030
+ if (subBounds === void 0) continue;
6031
+ _mergeBounds(result, subBounds);
6032
+ }
6033
+ return _hasAnyBound(result) ? result : void 0;
6034
+ }
6035
+ const op = expr2.operator;
6036
+ if ((op === "Less" || op === "LessEqual" || op === "Greater" || op === "GreaterEqual") && isFunction2(expr2)) {
6037
+ const isStrict = op === "Less" || op === "Greater";
6038
+ const ops = expr2.ops;
6039
+ if (!ops || ops.length < 2) return void 0;
6040
+ const flipped = op === "Greater" || op === "GreaterEqual" ? [...ops].reverse() : ops;
6041
+ for (let i = 0; i < flipped.length; i++) {
6042
+ if (isSymbol2(flipped[i], symbol2)) {
6043
+ if (i > 0) {
6044
+ const candidate = flipped[i - 1];
6045
+ if (result.lower === void 0 || candidate.isGreater(result.lower) === true) {
6046
+ result.lower = candidate;
6047
+ result.lowerStrict = isStrict;
6048
+ }
6049
+ }
6050
+ if (i < flipped.length - 1) {
6051
+ const candidate = flipped[i + 1];
6052
+ if (result.upper === void 0 || candidate.isLess(result.upper) === true) {
6053
+ result.upper = candidate;
6054
+ result.upperStrict = isStrict;
6055
+ }
6056
+ }
6057
+ }
6058
+ }
6059
+ return _hasAnyBound(result) ? result : void 0;
6060
+ }
6061
+ return void 0;
6062
+ }
6063
+ function _mergeBounds(into, from) {
6064
+ if (from.lower !== void 0) {
6065
+ if (into.lower === void 0 || from.lower.isGreater(into.lower) === true) {
6066
+ into.lower = from.lower;
6067
+ into.lowerStrict = from.lowerStrict;
6068
+ } else if (from.lower.isSame(into.lower)) {
6069
+ into.lowerStrict = into.lowerStrict || from.lowerStrict;
6070
+ }
6071
+ }
6072
+ if (from.upper !== void 0) {
6073
+ if (into.upper === void 0 || from.upper.isLess(into.upper) === true) {
6074
+ into.upper = from.upper;
6075
+ into.upperStrict = from.upperStrict;
6076
+ } else if (from.upper.isSame(into.upper)) {
6077
+ into.upperStrict = into.upperStrict || from.upperStrict;
6078
+ }
6079
+ }
6080
+ }
6081
+ function _hasAnyBound(b) {
6082
+ return b.lower !== void 0 || b.upper !== void 0;
6083
+ }
5971
6084
  function getInequalityBoundsFromAssumptions(ce, symbol2) {
5972
6085
  const result = {};
5973
6086
  const assumptions = ce.context?.assumptions;
@@ -5984,8 +6097,8 @@ var ComputeEngine = (() => {
5984
6097
  const isStrict = op === "Less";
5985
6098
  if (isFunction2(lhs, "Negate") && isSymbol2(lhs.op1, symbol2)) {
5986
6099
  const bound = ce.Zero;
5987
- if (result.lowerBound === void 0 || bound.isGreater(result.lowerBound) === true) {
5988
- result.lowerBound = bound;
6100
+ if (result.lower === void 0 || bound.isGreater(result.lower) === true) {
6101
+ result.lower = bound;
5989
6102
  result.lowerStrict = isStrict;
5990
6103
  }
5991
6104
  }
@@ -6004,16 +6117,16 @@ var ComputeEngine = (() => {
6004
6117
  }
6005
6118
  if (hasNegatedSymbol && constantSum !== 0) {
6006
6119
  const bound = ce.expr(constantSum);
6007
- if (result.lowerBound === void 0 || bound.isGreater(result.lowerBound) === true) {
6008
- result.lowerBound = bound;
6120
+ if (result.lower === void 0 || bound.isGreater(result.lower) === true) {
6121
+ result.lower = bound;
6009
6122
  result.lowerStrict = isStrict;
6010
6123
  }
6011
6124
  }
6012
6125
  }
6013
6126
  if (isSymbol2(lhs, symbol2)) {
6014
6127
  const bound = ce.Zero;
6015
- if (result.upperBound === void 0 || bound.isLess(result.upperBound) === true) {
6016
- result.upperBound = bound;
6128
+ if (result.upper === void 0 || bound.isLess(result.upper) === true) {
6129
+ result.upper = bound;
6017
6130
  result.upperStrict = isStrict;
6018
6131
  }
6019
6132
  }
@@ -6032,8 +6145,8 @@ var ComputeEngine = (() => {
6032
6145
  }
6033
6146
  if (hasSymbol && constantSum !== 0) {
6034
6147
  const bound = ce.expr(-constantSum);
6035
- if (result.upperBound === void 0 || bound.isLess(result.upperBound) === true) {
6036
- result.upperBound = bound;
6148
+ if (result.upper === void 0 || bound.isLess(result.upper) === true) {
6149
+ result.upper = bound;
6037
6150
  result.upperStrict = isStrict;
6038
6151
  }
6039
6152
  }
@@ -6253,8 +6366,8 @@ var ComputeEngine = (() => {
6253
6366
  const bounds = getInequalityBoundsFromAssumptions(a.engine, b.symbol);
6254
6367
  const aNum = typeof a.numericValue === "number" ? a.numericValue : a.numericValue.re;
6255
6368
  if (aNum !== void 0 && Number.isFinite(aNum)) {
6256
- if (bounds.lowerBound !== void 0) {
6257
- const lb = bounds.lowerBound;
6369
+ if (bounds.lower !== void 0) {
6370
+ const lb = bounds.lower;
6258
6371
  const lowerNum = isNumber(lb) ? typeof lb.numericValue === "number" ? lb.numericValue : lb.numericValue.re : void 0;
6259
6372
  if (lowerNum !== void 0 && Number.isFinite(lowerNum)) {
6260
6373
  if (lowerNum > aNum) return "<";
@@ -6262,8 +6375,8 @@ var ComputeEngine = (() => {
6262
6375
  if (lowerNum === aNum && !bounds.lowerStrict) return "<=";
6263
6376
  }
6264
6377
  }
6265
- if (bounds.upperBound !== void 0) {
6266
- const ub = bounds.upperBound;
6378
+ if (bounds.upper !== void 0) {
6379
+ const ub = bounds.upper;
6267
6380
  const upperNum = isNumber(ub) ? typeof ub.numericValue === "number" ? ub.numericValue : ub.numericValue.re : void 0;
6268
6381
  if (upperNum !== void 0 && Number.isFinite(upperNum)) {
6269
6382
  if (upperNum < aNum) return ">";
@@ -6293,8 +6406,8 @@ var ComputeEngine = (() => {
6293
6406
  if (typeof b === "number") {
6294
6407
  if (isSymbol2(a)) {
6295
6408
  const bounds = getInequalityBoundsFromAssumptions(a.engine, a.symbol);
6296
- if (bounds.lowerBound !== void 0) {
6297
- const lb = bounds.lowerBound;
6409
+ if (bounds.lower !== void 0) {
6410
+ const lb = bounds.lower;
6298
6411
  const lowerNum = isNumber(lb) ? typeof lb.numericValue === "number" ? lb.numericValue : lb.numericValue.re : void 0;
6299
6412
  if (lowerNum !== void 0 && Number.isFinite(lowerNum)) {
6300
6413
  if (lowerNum > b) return ">";
@@ -6302,8 +6415,8 @@ var ComputeEngine = (() => {
6302
6415
  if (lowerNum === b && !bounds.lowerStrict) return ">=";
6303
6416
  }
6304
6417
  }
6305
- if (bounds.upperBound !== void 0) {
6306
- const ub = bounds.upperBound;
6418
+ if (bounds.upper !== void 0) {
6419
+ const ub = bounds.upper;
6307
6420
  const upperNum = isNumber(ub) ? typeof ub.numericValue === "number" ? ub.numericValue : ub.numericValue.re : void 0;
6308
6421
  if (upperNum !== void 0 && Number.isFinite(upperNum)) {
6309
6422
  if (upperNum < b) return "<";
@@ -6359,8 +6472,8 @@ var ComputeEngine = (() => {
6359
6472
  const bounds = getInequalityBoundsFromAssumptions(a.engine, a.symbol);
6360
6473
  const bNum = typeof b.numericValue === "number" ? b.numericValue : b.numericValue.re;
6361
6474
  if (bNum !== void 0 && Number.isFinite(bNum)) {
6362
- if (bounds.lowerBound !== void 0) {
6363
- const lb = bounds.lowerBound;
6475
+ if (bounds.lower !== void 0) {
6476
+ const lb = bounds.lower;
6364
6477
  const lowerNum = isNumber(lb) ? typeof lb.numericValue === "number" ? lb.numericValue : lb.numericValue.re : void 0;
6365
6478
  if (lowerNum !== void 0 && Number.isFinite(lowerNum)) {
6366
6479
  if (lowerNum > bNum) return ">";
@@ -6368,8 +6481,8 @@ var ComputeEngine = (() => {
6368
6481
  if (lowerNum === bNum && !bounds.lowerStrict) return ">=";
6369
6482
  }
6370
6483
  }
6371
- if (bounds.upperBound !== void 0) {
6372
- const ub = bounds.upperBound;
6484
+ if (bounds.upper !== void 0) {
6485
+ const ub = bounds.upper;
6373
6486
  const upperNum = isNumber(ub) ? typeof ub.numericValue === "number" ? ub.numericValue : ub.numericValue.re : void 0;
6374
6487
  if (upperNum !== void 0 && Number.isFinite(upperNum)) {
6375
6488
  if (upperNum < bNum) return "<";
@@ -6907,6 +7020,12 @@ var ComputeEngine = (() => {
6907
7020
  scoped = false;
6908
7021
  signature;
6909
7022
  inferredSignature = true;
7023
+ /** True if this operator definition was created from a user-defined
7024
+ * function literal (e.g. via `ce.assign('f', ce.parse('x \\mapsto x^2'))`).
7025
+ * Used to enable auto-broadcasting when applied to indexed collections.
7026
+ * @internal
7027
+ */
7028
+ _isLambda = false;
6910
7029
  type;
6911
7030
  sgn;
6912
7031
  eq;
@@ -7064,6 +7183,8 @@ var ComputeEngine = (() => {
7064
7183
  this.engine._typeResolver
7065
7184
  );
7066
7185
  }
7186
+ if (isFunction2(boxedFn) && boxedFn.operator === "Function")
7187
+ this._isLambda = true;
7067
7188
  const fn = applicable(boxedFn);
7068
7189
  evaluate2 = (xs, _options) => fn(xs);
7069
7190
  Object.defineProperty(evaluate2, "toString", {
@@ -7914,8 +8035,15 @@ var ComputeEngine = (() => {
7914
8035
  *
7915
8036
  * Numeric values are rounded to `ce.precision` significant digits
7916
8037
  * (via `fractionalDigits: 'auto'`).
8038
+ *
8039
+ * If `options.verbatim` is `true` and `verbatimLatex` is set on this
8040
+ * expression (i.e. it was parsed with `preserveLatex: true`), return
8041
+ * the verbatim source instead of re-serializing. Falls through to
8042
+ * re-serialization if no verbatim is available.
7917
8043
  */
7918
8044
  toLatex(options) {
8045
+ if (options?.verbatim === true && this.verbatimLatex !== void 0)
8046
+ return this.verbatimLatex;
7919
8047
  if (this.isLazyCollection) {
7920
8048
  const materialized = this.evaluate({
7921
8049
  materialization: options?.materialization ?? true
@@ -8373,6 +8501,29 @@ var ComputeEngine = (() => {
8373
8501
  get isReal() {
8374
8502
  return void 0;
8375
8503
  }
8504
+ toSignedFunction() {
8505
+ const op = this.operator;
8506
+ if (op === void 0 || this.ops === void 0 || this.ops.length < 2) {
8507
+ return void 0;
8508
+ }
8509
+ const [lhs, rhs] = this.ops;
8510
+ const engine = this.engine;
8511
+ switch (op) {
8512
+ case "Equal":
8513
+ case "NotEqual":
8514
+ case "Less":
8515
+ case "LessEqual":
8516
+ return engine.function("Subtract", [lhs, rhs]);
8517
+ case "Greater":
8518
+ case "GreaterEqual":
8519
+ return engine.function("Subtract", [rhs, lhs]);
8520
+ default:
8521
+ return void 0;
8522
+ }
8523
+ }
8524
+ getInterval(symbol2) {
8525
+ return extractIntervalBounds(this, symbol2);
8526
+ }
8376
8527
  simplify(_options) {
8377
8528
  return this;
8378
8529
  }
@@ -10058,6 +10209,64 @@ var ComputeEngine = (() => {
10058
10209
  }
10059
10210
 
10060
10211
  // src/compute-engine/latex-syntax/dictionary/definitions-core.ts
10212
+ var COMPONENT_ACCESS_HEADS = {
10213
+ x: "First",
10214
+ y: "Second",
10215
+ z: "Third",
10216
+ real: "Real",
10217
+ re: "Real",
10218
+ imag: "Imaginary",
10219
+ im: "Imaginary",
10220
+ count: "Length",
10221
+ total: "Sum",
10222
+ max: "Max",
10223
+ min: "Min"
10224
+ };
10225
+ function memberHead(name) {
10226
+ return COMPONENT_ACCESS_HEADS[name] ?? null;
10227
+ }
10228
+ function parseComponentAccess(parser, lhs) {
10229
+ parser.skipVisualSpace();
10230
+ if (parser.match("\\operatorname")) {
10231
+ const name = parser.parseStringGroup();
10232
+ if (name === null) return null;
10233
+ const head = memberHead(name.trim());
10234
+ if (head === null) return null;
10235
+ return [head, lhs];
10236
+ }
10237
+ const tok = parser.peek;
10238
+ if (typeof tok === "string" && tok.startsWith("\\")) {
10239
+ const bare = tok.slice(1);
10240
+ const head = memberHead(bare);
10241
+ if (head !== null) {
10242
+ parser.nextToken();
10243
+ return [head, lhs];
10244
+ }
10245
+ return null;
10246
+ }
10247
+ if (typeof tok === "string" && /^[a-zA-Z]$/.test(tok)) {
10248
+ const head = memberHead(tok);
10249
+ if (head === null) return null;
10250
+ parser.nextToken();
10251
+ return [head, lhs];
10252
+ }
10253
+ return null;
10254
+ }
10255
+ function parseWhenRestriction(parser, lhs, close) {
10256
+ parser.addBoundary(close);
10257
+ parser.skipVisualSpace();
10258
+ const cond = parser.parseExpression({ minPrec: 0 });
10259
+ if (cond === null) {
10260
+ parser.removeBoundary();
10261
+ return null;
10262
+ }
10263
+ parser.skipVisualSpace();
10264
+ if (!parser.matchBoundary()) {
10265
+ parser.removeBoundary();
10266
+ return null;
10267
+ }
10268
+ return ["When", lhs, cond];
10269
+ }
10061
10270
  function parseSequence(parser, terminator, lhs, prec, sep) {
10062
10271
  if (terminator && terminator.minPrec >= prec) return null;
10063
10272
  const result = lhs ? [lhs] : ["Nothing"];
@@ -10333,15 +10542,16 @@ var ComputeEngine = (() => {
10333
10542
  precedence: ASSIGNMENT_PRECEDENCE,
10334
10543
  parse: parseAssign
10335
10544
  },
10336
- // General colon operator (type annotation, mapping notation)
10337
- // Precedence below assignment (260) so `:=` takes priority,
10338
- // and below arrows (270) so `f: A \to B` parses as `Colon(f, To(A, B))`
10545
+ // General colon operator (type annotation, mapping notation, Desmos piecewise)
10546
+ // Precedence below comparisons (245) so `cond : val` (Desmos compact piecewise)
10547
+ // parses as `Colon(cond, val)`, and below arrows (270) so
10548
+ // `f: A \to B` parses as `Colon(f, To(A, B))`.
10339
10549
  {
10340
10550
  name: "Colon",
10341
10551
  latexTrigger: ":",
10342
10552
  kind: "infix",
10343
10553
  associativity: "right",
10344
- precedence: 250,
10554
+ precedence: 240,
10345
10555
  serialize: (serializer, expr2) => joinLatex([
10346
10556
  serializer.serialize(operand(expr2, 1)),
10347
10557
  "\\colon",
@@ -10352,7 +10562,7 @@ var ComputeEngine = (() => {
10352
10562
  latexTrigger: "\\colon",
10353
10563
  kind: "infix",
10354
10564
  associativity: "right",
10355
- precedence: 250,
10565
+ precedence: 240,
10356
10566
  parse: "Colon"
10357
10567
  },
10358
10568
  {
@@ -10529,6 +10739,15 @@ var ComputeEngine = (() => {
10529
10739
  }
10530
10740
  },
10531
10741
  { name: "LatexTokens", serialize: serializeLatexTokens },
10742
+ // Component-access postfix: expr.member (C3)
10743
+ // The '.' trigger is consumed before the parse function is called.
10744
+ // Precedence 850 > 810 (At/indexing) so .x chains tightly.
10745
+ {
10746
+ kind: "postfix",
10747
+ precedence: 850,
10748
+ latexTrigger: ["."],
10749
+ parse: parseComponentAccess
10750
+ },
10532
10751
  {
10533
10752
  name: "At",
10534
10753
  kind: "postfix",
@@ -10549,6 +10768,29 @@ var ComputeEngine = (() => {
10549
10768
  latexTrigger: ["\\left", "\\lbrack"],
10550
10769
  parse: parseAt("\\right", "\\rbrack")
10551
10770
  },
10771
+ // When-restriction: `expr\left\{cond\right\}` → `When(expr, cond)` (D3)
10772
+ {
10773
+ name: "When",
10774
+ kind: "postfix",
10775
+ precedence: 800,
10776
+ latexTrigger: ["\\left", "\\{"],
10777
+ parse: (parser, lhs) => parseWhenRestriction(parser, lhs, ["\\right", "\\}"]),
10778
+ serialize: (serializer, expr2) => {
10779
+ const e = operand(expr2, 1);
10780
+ const cond = operand(expr2, 2);
10781
+ if (!e || !cond) return "";
10782
+ const clauses = operator(cond) === "And" ? operands(cond) ?? [] : [cond];
10783
+ const inner = clauses.map((c) => `\\left\\{${serializer.serialize(c)}\\right\\}`).join("");
10784
+ return `${serializer.serialize(e)}${inner}`;
10785
+ }
10786
+ },
10787
+ // When-restriction: bare `expr\{cond\}` → `When(expr, cond)`
10788
+ {
10789
+ kind: "postfix",
10790
+ precedence: 800,
10791
+ latexTrigger: ["\\{"],
10792
+ parse: (parser, lhs) => parseWhenRestriction(parser, lhs, ["\\}"])
10793
+ },
10552
10794
  {
10553
10795
  kind: "postfix",
10554
10796
  latexTrigger: ["_"],
@@ -10631,6 +10873,29 @@ var ComputeEngine = (() => {
10631
10873
  return "";
10632
10874
  }
10633
10875
  },
10876
+ // Additional triggers for Range: `...`, `\ldots`, and `\dots` are
10877
+ // equivalent to `..` when used as infix operators (e.g. `[1...9]`).
10878
+ // No `name` field here — names must be unique per the dictionary rules;
10879
+ // the first Range entry owns the name. When there is no LHS the symbol
10880
+ // entries near the top of the file still fire (ContinuationPlaceholder).
10881
+ {
10882
+ latexTrigger: [".", ".", "."],
10883
+ kind: "infix",
10884
+ precedence: 800,
10885
+ parse: parseRange
10886
+ },
10887
+ {
10888
+ latexTrigger: ["\\ldots"],
10889
+ kind: "infix",
10890
+ precedence: 800,
10891
+ parse: parseRange
10892
+ },
10893
+ {
10894
+ latexTrigger: ["\\dots"],
10895
+ kind: "infix",
10896
+ precedence: 800,
10897
+ parse: parseRange
10898
+ },
10634
10899
  {
10635
10900
  latexTrigger: [";"],
10636
10901
  kind: "infix",
@@ -10815,13 +11080,24 @@ var ComputeEngine = (() => {
10815
11080
  const args = operands(expr2);
10816
11081
  if (!args || args.length < 2) return "";
10817
11082
  const body = args[0];
10818
- const indexing = args[1];
10819
- if (operator(indexing) === "Element") {
10820
- const index = operand(indexing, 1);
10821
- const range2 = operand(indexing, 2);
10822
- if (operator(range2) === "Range") {
10823
- const lo = operand(range2, 1);
10824
- const hi = operand(range2, 2);
11083
+ const elements = args.slice(1);
11084
+ const allElements = elements.every((e) => operator(e) === "Element");
11085
+ if (!allElements) {
11086
+ return joinLatex([
11087
+ "\\operatorname{Loop}(",
11088
+ serializer.serialize(body),
11089
+ ", ",
11090
+ serializer.serialize(elements[0]),
11091
+ ")"
11092
+ ]);
11093
+ }
11094
+ if (elements.length === 1) {
11095
+ const elem = elements[0];
11096
+ const index = operand(elem, 1);
11097
+ const coll = operand(elem, 2);
11098
+ if (operator(coll) === "Range") {
11099
+ const lo = operand(coll, 1);
11100
+ const hi = operand(coll, 2);
10825
11101
  return joinLatex([
10826
11102
  "\\text{for }",
10827
11103
  serializer.serialize(index),
@@ -10833,13 +11109,27 @@ var ComputeEngine = (() => {
10833
11109
  serializer.serialize(body)
10834
11110
  ]);
10835
11111
  }
11112
+ return joinLatex([
11113
+ serializer.serialize(body),
11114
+ " \\operatorname{for} ",
11115
+ serializer.serialize(index),
11116
+ " = ",
11117
+ serializer.serialize(coll)
11118
+ ]);
10836
11119
  }
11120
+ const bindings = elements.map((elem) => {
11121
+ const name = operand(elem, 1);
11122
+ const coll = operand(elem, 2);
11123
+ return joinLatex([
11124
+ serializer.serialize(name),
11125
+ " = ",
11126
+ serializer.serialize(coll)
11127
+ ]);
11128
+ }).join(", ");
10837
11129
  return joinLatex([
10838
- "\\operatorname{Loop}(",
10839
11130
  serializer.serialize(body),
10840
- ", ",
10841
- serializer.serialize(indexing),
10842
- ")"
11131
+ " \\operatorname{for} ",
11132
+ bindings
10843
11133
  ]);
10844
11134
  }
10845
11135
  },
@@ -10872,6 +11162,18 @@ var ComputeEngine = (() => {
10872
11162
  precedence: 245,
10873
11163
  parse: (parser, until) => parseForExpression(parser, until)
10874
11164
  },
11165
+ // \operatorname{for} as postfix infix (list comprehension):
11166
+ // `body \operatorname{for} x = L_1, y = L_2`
11167
+ // Precedence 19 — just below comma (20) so the body is allowed to use
11168
+ // any operator (including comma sequencing) up to the keyword, and the
11169
+ // bindings can be comma-separated below us.
11170
+ {
11171
+ symbolTrigger: "for",
11172
+ kind: "infix",
11173
+ associativity: "none",
11174
+ precedence: 19,
11175
+ parse: (parser, lhs, until) => parseForComprehension(parser, lhs, until)
11176
+ },
10875
11177
  // \operatorname{break}
10876
11178
  {
10877
11179
  symbolTrigger: "break",
@@ -11076,7 +11378,10 @@ var ComputeEngine = (() => {
11076
11378
  if (!sym2 || !parser.getSymbolType(sym2).matches("function")) return null;
11077
11379
  parser.addBoundary([")"]);
11078
11380
  const expr2 = parser.parseExpression(until);
11079
- if (!parser.matchBoundary()) return null;
11381
+ if (!parser.matchBoundary()) {
11382
+ parser.removeBoundary();
11383
+ return null;
11384
+ }
11080
11385
  if (!parser.match("<}>")) return null;
11081
11386
  return ["Derivative", lhs, expr2];
11082
11387
  }
@@ -11517,7 +11822,12 @@ var ComputeEngine = (() => {
11517
11822
  if (isEmptySequence(body)) return ["List"];
11518
11823
  const h = operator(body);
11519
11824
  if (h === "Range" || h === "Linspace") return body;
11520
- if (h === "Sequence") return ["List", ...operands(body)];
11825
+ if (h === "Sequence") {
11826
+ const elems = operands(body);
11827
+ const inferred = tryInferRangeFromElements(elems, parser);
11828
+ if (inferred) return inferred;
11829
+ return ["List", ...elems];
11830
+ }
11521
11831
  if (h === "Delimiter") {
11522
11832
  const delim = stringValue(operand(body, 2)) ?? "...";
11523
11833
  if (delim === ";" || delim === ".;.") {
@@ -11530,12 +11840,37 @@ var ComputeEngine = (() => {
11530
11840
  }
11531
11841
  if (delim === "," || delim === ".,.") {
11532
11842
  body = operand(body, 1);
11533
- if (operator(body) === "Sequence") return ["List", ...operands(body)];
11843
+ if (operator(body) === "Sequence") {
11844
+ const elems = operands(body);
11845
+ const inferred = tryInferRangeFromElements(elems, parser);
11846
+ if (inferred) return inferred;
11847
+ return ["List", ...elems];
11848
+ }
11534
11849
  return ["List", body ?? "Nothing"];
11535
11850
  }
11536
11851
  }
11537
11852
  return ["List", body];
11538
11853
  }
11854
+ function tryInferRangeFromElements(elems, parser) {
11855
+ if (elems.length < 4) return null;
11856
+ const penultimate = elems[elems.length - 2];
11857
+ if (symbol(penultimate) !== "ContinuationPlaceholder") return null;
11858
+ const samples = elems.slice(0, -2);
11859
+ const endExpr = elems[elems.length - 1];
11860
+ if (samples.length < 2) return null;
11861
+ const sampleNums = samples.map(machineValue);
11862
+ if (sampleNums.some((n) => n === null)) return null;
11863
+ const nums = sampleNums;
11864
+ const step = nums[nums.length - 1] - nums[nums.length - 2];
11865
+ const tol = parser.options.tolerance;
11866
+ if (Math.abs(step) < tol)
11867
+ return parser.error("degenerate-range-step", parser.index);
11868
+ for (let i = 1; i < nums.length; i++) {
11869
+ if (Math.abs(nums[i] - nums[i - 1] - step) > tol)
11870
+ return parser.error("inconsistent-range-samples", parser.index);
11871
+ }
11872
+ return ["Range", nums[0], endExpr, step];
11873
+ }
11539
11874
  function serializeList(serializer, expr2) {
11540
11875
  if (nops(expr2) > 1 && operands(expr2).every((x) => {
11541
11876
  const op = operator(x);
@@ -11787,6 +12122,38 @@ var ComputeEngine = (() => {
11787
12122
  ["Element", index, ["Range", lower, upper]]
11788
12123
  ];
11789
12124
  }
12125
+ function parseForComprehension(parser, lhs, until) {
12126
+ const bindingTerminator = {
12127
+ minPrec: 21,
12128
+ // Above comma (20) and ; (19), so `x = L_1` is captured whole
12129
+ condition: (p) => {
12130
+ if (until?.condition?.(p)) return true;
12131
+ const saved = p.index;
12132
+ p.skipVisualSpace();
12133
+ const isComma = p.peek === ",";
12134
+ p.index = saved;
12135
+ if (isComma) return true;
12136
+ if (peekKeyword(p, "where")) return true;
12137
+ if (peekKeyword(p, "with")) return true;
12138
+ return false;
12139
+ }
12140
+ };
12141
+ const elements = [];
12142
+ do {
12143
+ parser.skipVisualSpace();
12144
+ const binding = parser.parseExpression(bindingTerminator);
12145
+ if (binding === null) break;
12146
+ const op = operator(binding);
12147
+ if (op !== "Equal" && op !== "Assign") return null;
12148
+ const name = operand(binding, 1);
12149
+ const list = operand(binding, 2);
12150
+ if (!name || !list) return null;
12151
+ elements.push(["Element", name, list]);
12152
+ parser.skipVisualSpace();
12153
+ } while (parser.match(","));
12154
+ if (elements.length === 0) return null;
12155
+ return ["Loop", lhs, ...elements];
12156
+ }
11790
12157
  function parseWhereExpression(parser, lhs, until) {
11791
12158
  const bindingTerminator = {
11792
12159
  minPrec: 21,
@@ -11809,6 +12176,25 @@ var ComputeEngine = (() => {
11809
12176
  parser.skipVisualSpace();
11810
12177
  } while (parser.match(","));
11811
12178
  if (bindings.length === 0) return null;
12179
+ const forStart = parser.index;
12180
+ if (matchKeyword(parser, "for")) {
12181
+ const loop = parseForComprehension(parser, lhs, until);
12182
+ if (loop) {
12183
+ const block2 = [];
12184
+ for (const b of bindings) {
12185
+ const normalized = normalizeLocalAssign(b);
12186
+ if (operator(normalized) === "Assign") {
12187
+ block2.push(["Declare", operand(normalized, 1)]);
12188
+ block2.push(normalized);
12189
+ } else {
12190
+ block2.push(normalized);
12191
+ }
12192
+ }
12193
+ block2.push(loop);
12194
+ return ["Block", ...block2];
12195
+ }
12196
+ parser.index = forStart;
12197
+ }
11812
12198
  const block = [];
11813
12199
  for (const b of bindings) {
11814
12200
  const normalized = normalizeLocalAssign(b);
@@ -12022,6 +12408,17 @@ var ComputeEngine = (() => {
12022
12408
  const upperExpr = openRight ? ["Open", upper] : upper;
12023
12409
  return ["Interval", lowerExpr, upperExpr];
12024
12410
  }
12411
+ var COMPARISON_HEADS = /* @__PURE__ */ new Set([
12412
+ "Less",
12413
+ "LessEqual",
12414
+ "Greater",
12415
+ "GreaterEqual",
12416
+ "Equal",
12417
+ "NotEqual",
12418
+ "And",
12419
+ "Or",
12420
+ "Not"
12421
+ ]);
12025
12422
  var DEFINITIONS_SETS = [
12026
12423
  //
12027
12424
  // Constants
@@ -12280,18 +12677,58 @@ var ComputeEngine = (() => {
12280
12677
  closeTrigger: "}",
12281
12678
  parse: (_parser, body) => {
12282
12679
  if (isEmptySequence(body)) return "EmptySet";
12680
+ if (operator(body) == "Delimiter" && stringValue(operand(body, 2)) === ",") {
12681
+ body = operand(body, 1);
12682
+ }
12283
12683
  const h = operator(body);
12284
- if (h === "Divides" || h === "Colon") {
12684
+ if (h === "Divides") {
12285
12685
  const expr2 = operand(body, 1);
12286
12686
  const condition = operand(body, 2);
12287
12687
  if (expr2 !== null && condition !== null)
12288
12688
  return ["Set", expr2, ["Condition", condition]];
12289
12689
  }
12290
- if (operator(body) == "Delimiter" && stringValue(operand(body, 2)) === ",") {
12291
- body = operand(body, 1);
12690
+ if (h === "Colon") {
12691
+ const lhs = operand(body, 1);
12692
+ const rhs = operand(body, 2);
12693
+ if (lhs !== null && rhs !== null) {
12694
+ const lhsOp = operator(lhs);
12695
+ if (lhsOp !== null && COMPARISON_HEADS.has(lhsOp)) {
12696
+ return ["Which", lhs, rhs];
12697
+ }
12698
+ return ["Set", lhs, ["Condition", rhs]];
12699
+ }
12292
12700
  }
12293
- if (operator(body) !== "Sequence") return ["Set", body];
12294
- return ["Set", ...operands(body)];
12701
+ if (h === "Sequence") {
12702
+ const elements = operands(body);
12703
+ const colonElements = elements.filter((el) => operator(el) === "Colon");
12704
+ const allPiecewise = colonElements.length > 0 && colonElements.every((el) => {
12705
+ const lhs = operand(el, 1);
12706
+ const lhsOp = lhs !== null ? operator(lhs) : null;
12707
+ return lhsOp !== null && COMPARISON_HEADS.has(lhsOp);
12708
+ });
12709
+ if (allPiecewise) {
12710
+ const whichOps = [];
12711
+ for (let i = 0; i < elements.length; i++) {
12712
+ const el = elements[i];
12713
+ if (operator(el) === "Colon") {
12714
+ const cond = operand(el, 1);
12715
+ const val = operand(el, 2);
12716
+ if (cond === null || val === null) {
12717
+ return ["Set", ...elements];
12718
+ }
12719
+ whichOps.push(cond, val);
12720
+ } else {
12721
+ if (i !== elements.length - 1) {
12722
+ return ["Set", ...elements];
12723
+ }
12724
+ whichOps.push("True", el);
12725
+ }
12726
+ }
12727
+ return ["Which", ...whichOps];
12728
+ }
12729
+ return ["Set", ...elements];
12730
+ }
12731
+ return ["Set", body];
12295
12732
  },
12296
12733
  serialize: (serializer, expr2) => {
12297
12734
  if (nops(expr2) === 2 && operator(operand(expr2, 2)) === "Condition") {
@@ -14295,7 +14732,8 @@ var ComputeEngine = (() => {
14295
14732
  minPrec: MULTIPLICATION_PRECEDENCE,
14296
14733
  condition: (parser2) => trigCommands[parser2.peek] || (until?.condition?.(parser2) ?? false)
14297
14734
  });
14298
- const appliedFn = args === null ? fn : typeof fn === "string" ? [fn, ...args] : ["Apply", fn, ...args];
14735
+ const head = fn === "Arctan" && args?.length === 2 ? "Arctan2" : fn;
14736
+ const appliedFn = args === null ? fn : typeof head === "string" ? [head, ...args] : ["Apply", head, ...args];
14299
14737
  return sup === null ? appliedFn : ["Power", appliedFn, sup];
14300
14738
  };
14301
14739
  }
@@ -16511,10 +16949,17 @@ var ComputeEngine = (() => {
16511
16949
  // The capitalized library entries already exist; these are pure parse
16512
16950
  // aliases so the lowercase names don't land in `unsupported-operator`.
16513
16951
  // ---------------------------------------------------------------------------
16952
+ { latexTrigger: "\\operatorname{count}", parse: "Length" },
16514
16953
  { latexTrigger: "\\operatorname{random}", parse: "Random" },
16515
16954
  { latexTrigger: "\\operatorname{shuffle}", parse: "Shuffle" },
16516
16955
  { latexTrigger: "\\operatorname{repeat}", parse: "Repeat" },
16517
16956
  { latexTrigger: "\\operatorname{join}", parse: "Join" },
16957
+ { latexTrigger: "\\operatorname{range}", parse: "Range" },
16958
+ // Note: `\operatorname{with}` (Desmos's local-binding clause) is intentionally
16959
+ // NOT registered here. Use the math-notation equivalent `\operatorname{where}`
16960
+ // (with `\coloneq` for bindings), or register `with` as a custom dictionary
16961
+ // entry at the integration layer — see the "Desmos-Specific Syntax — Prefer
16962
+ // Custom LaTeX Dictionary" section in COMPUTE_ENGINE.md for a worked example.
16518
16963
  // ---------------------------------------------------------------------------
16519
16964
  // Geometric primitive heads. Registered as known typed heads so consumers
16520
16965
  // can branch on the operator name; CE itself doesn't render them. The
@@ -19558,6 +20003,19 @@ var ComputeEngine = (() => {
19558
20003
  } while (postfix !== null);
19559
20004
  }
19560
20005
  if (result !== null) result = this.parseSupsub(result);
20006
+ if (result !== null) {
20007
+ let postfix = null;
20008
+ let index = this.index;
20009
+ do {
20010
+ postfix = this.parsePostfixOperator(result, until);
20011
+ result = postfix ?? result;
20012
+ if (this.index === index && postfix !== null) {
20013
+ console.assert(this.index !== index, "No token consumed");
20014
+ break;
20015
+ }
20016
+ index = this.index;
20017
+ } while (postfix !== null);
20018
+ }
19561
20019
  if (result === null) {
19562
20020
  result = this.options.parseUnexpectedToken?.(null, this) ?? null;
19563
20021
  if (result === null && this.peek.startsWith("\\")) {
@@ -20066,6 +20524,28 @@ var ComputeEngine = (() => {
20066
20524
  }
20067
20525
 
20068
20526
  // src/compute-engine/latex-syntax/serializer.ts
20527
+ var DOT_NOTATION_MAP = {
20528
+ First: ".x",
20529
+ Second: ".y",
20530
+ Third: ".z",
20531
+ Real: ".\\operatorname{real}",
20532
+ Imaginary: ".\\operatorname{imag}",
20533
+ Length: ".\\operatorname{count}",
20534
+ Sum: ".\\operatorname{total}",
20535
+ Max: ".\\max",
20536
+ Min: ".\\min"
20537
+ };
20538
+ function trySerializeDotNotation(serializer, expr2) {
20539
+ if (!serializer.options.dotNotation) return null;
20540
+ const ops = operands(expr2);
20541
+ if (!ops || ops.length !== 1) return null;
20542
+ const head = operator(expr2);
20543
+ if (!head) return null;
20544
+ const suffix = DOT_NOTATION_MAP[head];
20545
+ if (suffix === void 0) return null;
20546
+ const lhs = serializer.wrap(ops[0], 810);
20547
+ return `${lhs}${suffix}`;
20548
+ }
20069
20549
  var ACCENT_MODIFIERS = {
20070
20550
  deg: (s) => `${s}\\degree`,
20071
20551
  prime: (s) => `${s}^{\\prime}`,
@@ -20109,6 +20589,7 @@ var ComputeEngine = (() => {
20109
20589
  constructor(dictionary, options) {
20110
20590
  this.dictionary = dictionary;
20111
20591
  this.options = {
20592
+ dotNotation: false,
20112
20593
  dmsFormat: false,
20113
20594
  angleNormalization: "none",
20114
20595
  ...options
@@ -20207,6 +20688,8 @@ var ComputeEngine = (() => {
20207
20688
  return def?.serialize?.(this, expr2) ?? serializeSymbol2(symbol(expr2)) ?? "";
20208
20689
  }
20209
20690
  serializeFunction(expr2, def) {
20691
+ const dotResult = trySerializeDotNotation(this, expr2);
20692
+ if (dotResult !== null) return dotResult;
20210
20693
  if (def?.serialize) return def.serialize(this, expr2);
20211
20694
  const h = operator(expr2);
20212
20695
  return serializeSymbol2(h, "auto") + this.wrapArguments(expr2);
@@ -20449,6 +20932,8 @@ var ComputeEngine = (() => {
20449
20932
  preserveLatex: opts.preserveLatex ?? false,
20450
20933
  quantifierScope: opts.quantifierScope ?? "tight",
20451
20934
  timeDerivativeVariable: opts.timeDerivativeVariable ?? "t",
20935
+ // Standalone mode has no engine; use the same default as ComputeEngine
20936
+ tolerance: 1e-7,
20452
20937
  // Callbacks -- standalone mode has no engine, so these are stubs
20453
20938
  getSymbolType: (_id) => BoxedType.unknown,
20454
20939
  hasSubscriptEvaluate: (_id) => false,
@@ -20481,6 +20966,7 @@ var ComputeEngine = (() => {
20481
20966
  invisiblePlus: "",
20482
20967
  multiply: "\\times",
20483
20968
  missingSymbol: "\\blacksquare",
20969
+ dotNotation: false,
20484
20970
  dmsFormat: false,
20485
20971
  angleNormalization: "none",
20486
20972
  // Style callbacks -- use same defaults as the engine
@@ -22315,6 +22801,15 @@ var ComputeEngine = (() => {
22315
22801
  }
22316
22802
  } else {
22317
22803
  for (const item of t) {
22804
+ const op = item.operator;
22805
+ if (op === "Tuple" || op === "Pair" || op === "Single" || op === "Triple" || op === "Quadruple" || op === "KeyValuePair" || op === "Dictionary" || op === "Set" || op === "Record") {
22806
+ valid = false;
22807
+ return;
22808
+ }
22809
+ if (item.type.type === "string") {
22810
+ valid = false;
22811
+ return;
22812
+ }
22318
22813
  dtype = getSupertype(dtype, getExpressionDatatype(item));
22319
22814
  }
22320
22815
  }
@@ -27350,6 +27845,15 @@ ${lines.join("\n")}`;
27350
27845
  return void 0;
27351
27846
  }
27352
27847
 
27848
+ // src/compute-engine/numerics/random.ts
27849
+ function deterministicRandom(seed) {
27850
+ const v = Math.sin(seed * 12.9898) * 43758.5453;
27851
+ return v - Math.floor(v);
27852
+ }
27853
+ function nextSeed(seed) {
27854
+ return seed + 0.6180339887498949;
27855
+ }
27856
+
27353
27857
  // src/compute-engine/boxed-expression/canonical-utils.ts
27354
27858
  function canonical(ce, xs, scope) {
27355
27859
  if (xs.every((x) => x.isCanonical)) return xs;
@@ -27399,6 +27903,19 @@ ${lines.join("\n")}`;
27399
27903
  indexWhere: void 0
27400
27904
  }
27401
27905
  },
27906
+ Length: {
27907
+ description: "Number of elements in a collection. Returns undefined for non-collections and for infinite collections.",
27908
+ complexity: 4e3,
27909
+ signature: "(any) -> integer",
27910
+ type: () => "integer",
27911
+ evaluate: ([xs], { engine }) => {
27912
+ if (!xs.isCollection) return void 0;
27913
+ if (xs.isEmptyCollection) return engine.Zero;
27914
+ const n = xs.count;
27915
+ if (n === void 0 || !isFinite(n)) return void 0;
27916
+ return engine.number(n);
27917
+ }
27918
+ },
27402
27919
  Tuple: {
27403
27920
  description: "A fixed number of heterogeneous elements",
27404
27921
  complexity: 8200,
@@ -27451,7 +27968,11 @@ ${lines.join("\n")}`;
27451
27968
  //
27452
27969
  Range: {
27453
27970
  complexity: 8200,
27454
- signature: "(number, number?, step: number?) -> indexed_collection<integer>",
27971
+ signature: "(number, number?, step: number?) -> indexed_collection<number>",
27972
+ type: (ops) => {
27973
+ const allInt = ops.every((op) => op.isInteger);
27974
+ return allInt ? parseType("indexed_collection<integer>") : parseType("indexed_collection<number>");
27975
+ },
27455
27976
  canonical: (ops, { engine: ce }) => {
27456
27977
  if (ops.length === 0) return null;
27457
27978
  if (ops.length === 1) return ce._fn("Range", [ce.One, ops[0].canonical]);
@@ -27475,19 +27996,26 @@ ${lines.join("\n")}`;
27475
27996
  const [lower, upper, step] = range(expr2);
27476
27997
  if (step === 0) return 0;
27477
27998
  if (!isFinite(lower) || !isFinite(upper)) return Infinity;
27478
- return 1 + Math.max(0, Math.floor((upper - lower) / step));
27999
+ return Math.max(0, Math.floor((upper - lower) / step) + 1);
27479
28000
  },
27480
28001
  contains: (expr2, target) => {
27481
- if (!target.type.matches("integer")) return false;
27482
28002
  const t = target.re;
28003
+ if (!isFinite(t)) return false;
27483
28004
  const [lower, upper, step] = range(expr2);
27484
28005
  if (step === 0) return false;
27485
- if (step > 0) return t >= lower && t <= upper;
27486
- return t <= lower && t >= upper;
28006
+ if (step > 0) {
28007
+ if (t < lower || t > upper) return false;
28008
+ } else {
28009
+ if (t > lower || t < upper) return false;
28010
+ }
28011
+ const k = (t - lower) / step;
28012
+ const tol = expr2.engine.tolerance;
28013
+ const kRounded = Math.round(k);
28014
+ return kRounded >= 0 && Math.abs(k - kRounded) < tol;
27487
28015
  },
27488
28016
  iterator: (expr2) => {
27489
28017
  const [lower, upper, step] = range(expr2);
27490
- const maxCount = step === 0 ? 0 : Math.floor((upper - lower) / step) + 1;
28018
+ const maxCount = step === 0 ? 0 : Math.max(0, Math.floor((upper - lower) / step) + 1);
27491
28019
  let index = 1;
27492
28020
  return {
27493
28021
  next: () => {
@@ -27505,7 +28033,9 @@ ${lines.join("\n")}`;
27505
28033
  at: (expr2, index) => {
27506
28034
  if (typeof index !== "number") return void 0;
27507
28035
  const [lower, upper, step] = range(expr2);
27508
- if (index < 1 || index > 1 + (upper - lower) / step) return void 0;
28036
+ if (step === 0) return void 0;
28037
+ const maxCount = Math.max(0, Math.floor((upper - lower) / step) + 1);
28038
+ if (index < 1 || index > maxCount) return void 0;
27509
28039
  return expr2.engine.number(lower + step * (index - 1));
27510
28040
  },
27511
28041
  indexWhere: void 0,
@@ -27530,7 +28060,13 @@ ${lines.join("\n")}`;
27530
28060
  if (step > 0) return lower <= upper ? "positive" : "negative";
27531
28061
  return lower >= upper ? "positive" : "negative";
27532
28062
  },
27533
- elttype: (_expr) => "finite_integer"
28063
+ elttype: (expr2) => {
28064
+ if (!isFunction2(expr2)) return "finite_integer";
28065
+ for (let i = 1; i <= expr2.nops; i++) {
28066
+ if (!expr2[`op${i}`].isInteger) return "finite_real";
28067
+ }
28068
+ return "finite_integer";
28069
+ }
27534
28070
  }
27535
28071
  },
27536
28072
  Interval: {
@@ -27633,10 +28169,12 @@ ${lines.join("\n")}`;
27633
28169
  const upper = expr2.op2.re;
27634
28170
  let count = expr2.op3.re;
27635
28171
  if (!isFinite(count)) count = DEFAULT_LINSPACE_COUNT;
28172
+ count = Math.floor(count);
27636
28173
  if (!isFinite(lower) || !isFinite(upper)) return void 0;
27637
28174
  if (index < 1 || index > count) return void 0;
28175
+ if (count === 1) return expr2.engine.number(lower);
27638
28176
  return expr2.engine.number(
27639
- lower + (upper - lower) * (index - 1) / count
28177
+ lower + (upper - lower) * (index - 1) / (count - 1)
27640
28178
  );
27641
28179
  },
27642
28180
  iterator: (expr2) => {
@@ -27655,6 +28193,8 @@ ${lines.join("\n")}`;
27655
28193
  !isFinite(expr2.op3.re) ? DEFAULT_LINSPACE_COUNT : expr2.op3.re
27656
28194
  );
27657
28195
  }
28196
+ totalCount = Math.floor(totalCount);
28197
+ const denom = totalCount > 1 ? totalCount - 1 : 1;
27658
28198
  let index = 1;
27659
28199
  return {
27660
28200
  next: () => {
@@ -27663,7 +28203,7 @@ ${lines.join("\n")}`;
27663
28203
  index += 1;
27664
28204
  return {
27665
28205
  value: expr2.engine.number(
27666
- lower + (upper - lower) * (index - 1 - 1) / totalCount
28206
+ lower + (upper - lower) * (index - 1 - 1) / denom
27667
28207
  ),
27668
28208
  done: false
27669
28209
  };
@@ -27679,9 +28219,14 @@ ${lines.join("\n")}`;
27679
28219
  if (t < lower || t > upper) return false;
27680
28220
  let count = expr2.op3.re;
27681
28221
  if (!isFinite(count)) count = DEFAULT_LINSPACE_COUNT;
28222
+ count = Math.floor(count);
27682
28223
  if (count === 0) return false;
27683
- const step = (upper - lower) / count;
27684
- return (t - lower) % step === 0;
28224
+ if (count === 1) return t === lower;
28225
+ const step = (upper - lower) / (count - 1);
28226
+ const k = (t - lower) / step;
28227
+ const tol = expr2.engine.tolerance;
28228
+ const kRounded = Math.round(k);
28229
+ return kRounded >= 0 && kRounded <= count - 1 && Math.abs(k - kRounded) < tol;
27685
28230
  }
27686
28231
  }
27687
28232
  },
@@ -28006,10 +28551,12 @@ ${lines.join("\n")}`;
28006
28551
  description: [
28007
28552
  "Access an element of an indexed collection.",
28008
28553
  "If the index is negative, it is counted from the end.",
28009
- "Multiple indices can be provided to access nested collections (e.g., matrices)."
28554
+ "Multiple indices can be provided to access nested collections (e.g., matrices).",
28555
+ "If the index is a finite collection of booleans, returns the elements where the mask is True.",
28556
+ "If the index is a finite collection of integers, returns the elements at those indices."
28010
28557
  ],
28011
28558
  complexity: 8200,
28012
- signature: "(value: indexed_collection, index: (number|string)+) -> unknown",
28559
+ signature: "(value: indexed_collection, index: (number|string|indexed_collection)+) -> unknown",
28013
28560
  type: ([xs]) => xs.operatorDefinition?.collection?.elttype?.(xs) ?? collectionElementType(xs.type.type) ?? "any",
28014
28561
  evaluate: (ops, { engine: ce }) => {
28015
28562
  let expr2 = ops[0];
@@ -28020,12 +28567,39 @@ ${lines.join("\n")}`;
28020
28567
  if (!at) return void 0;
28021
28568
  const opAtIndex = ops[index];
28022
28569
  const s = isString(opAtIndex) ? opAtIndex.string : void 0;
28023
- if (s !== void 0) expr2 = at(expr2, s) ?? ce.Nothing;
28024
- else {
28025
- const i = ops[index].re;
28026
- if (!Number.isInteger(i)) return void 0;
28027
- expr2 = at(expr2, i) ?? ce.Nothing;
28570
+ if (s !== void 0) {
28571
+ expr2 = at(expr2, s) ?? ce.Nothing;
28572
+ index += 1;
28573
+ continue;
28028
28574
  }
28575
+ if (opAtIndex.isCollection && opAtIndex.isFiniteCollection) {
28576
+ const indices = Array.from(opAtIndex.each());
28577
+ const isMask = indices.every((m) => {
28578
+ const name = sym(m);
28579
+ return name === "True" || name === "False";
28580
+ });
28581
+ const picked = [];
28582
+ if (isMask) {
28583
+ indices.forEach((m, i2) => {
28584
+ if (sym(m) !== "True") return;
28585
+ const v = at(expr2, i2 + 1);
28586
+ if (v !== void 0) picked.push(v);
28587
+ });
28588
+ } else {
28589
+ for (const m of indices) {
28590
+ const k = m.re;
28591
+ if (!Number.isInteger(k)) return void 0;
28592
+ const v = at(expr2, k);
28593
+ if (v !== void 0) picked.push(v);
28594
+ }
28595
+ }
28596
+ expr2 = ce._fn("List", picked);
28597
+ index += 1;
28598
+ continue;
28599
+ }
28600
+ const i = opAtIndex.re;
28601
+ if (!Number.isInteger(i)) return void 0;
28602
+ expr2 = at(expr2, i) ?? ce.Nothing;
28029
28603
  index += 1;
28030
28604
  }
28031
28605
  return expr2;
@@ -28036,7 +28610,7 @@ ${lines.join("\n")}`;
28036
28610
  description: ["Return `n` elements from a collection."],
28037
28611
  complexity: 8200,
28038
28612
  signature: "(xs: indexed_collection, count: number) -> indexed_collection",
28039
- type: ([xs]) => `list<${collectionElementType(xs.type.type)}>`,
28613
+ type: ([xs]) => `list<${typeToString(collectionElementType(xs.type.type) ?? "any")}>`,
28040
28614
  evaluate: (ops, { engine, materialization: eager }) => {
28041
28615
  if (!eager) return void 0;
28042
28616
  const takeExpr = engine._fn("Take", ops);
@@ -28083,7 +28657,7 @@ ${lines.join("\n")}`;
28083
28657
  description: ["Return the collection without the first n elements."],
28084
28658
  complexity: 8200,
28085
28659
  signature: "(xs: indexed_collection, count: number) -> indexed_collection",
28086
- type: ([xs]) => `list<${collectionElementType(xs.type.type)}>`,
28660
+ type: ([xs]) => `list<${typeToString(collectionElementType(xs.type.type) ?? "any")}>`,
28087
28661
  collection: {
28088
28662
  isLazy: (_expr) => true,
28089
28663
  count: (expr2) => {
@@ -28128,15 +28702,45 @@ ${lines.join("\n")}`;
28128
28702
  },
28129
28703
  First: {
28130
28704
  complexity: 8200,
28131
- signature: "(collection) -> any",
28705
+ signature: "(any) -> any",
28132
28706
  type: ([xs]) => xs.operatorDefinition?.collection?.elttype?.(xs) ?? "any",
28133
- evaluate: ([xs], { engine: ce }) => xs.at(1) ?? ce.Nothing
28707
+ evaluate: ([xs], { engine: ce }) => {
28708
+ if (!xs.isCollection)
28709
+ return ce.error([
28710
+ "incompatible-type",
28711
+ `'collection'`,
28712
+ xs.type.toString()
28713
+ ]);
28714
+ return xs.at(1) ?? ce.Nothing;
28715
+ }
28134
28716
  },
28135
28717
  Second: {
28136
28718
  complexity: 8200,
28137
- signature: "(collection) -> any",
28719
+ signature: "(any) -> any",
28138
28720
  type: ([xs]) => xs.operatorDefinition?.collection?.elttype?.(xs) ?? "any",
28139
- evaluate: ([xs], { engine: ce }) => xs.at(2) ?? ce.Nothing
28721
+ evaluate: ([xs], { engine: ce }) => {
28722
+ if (!xs.isCollection)
28723
+ return ce.error([
28724
+ "incompatible-type",
28725
+ `'collection'`,
28726
+ xs.type.toString()
28727
+ ]);
28728
+ return xs.at(2) ?? ce.Nothing;
28729
+ }
28730
+ },
28731
+ Third: {
28732
+ complexity: 8200,
28733
+ signature: "(any) -> any",
28734
+ type: ([xs]) => xs.operatorDefinition?.collection?.elttype?.(xs) ?? "any",
28735
+ evaluate: ([xs], { engine: ce }) => {
28736
+ if (!xs.isCollection)
28737
+ return ce.error([
28738
+ "incompatible-type",
28739
+ `'collection'`,
28740
+ xs.type.toString()
28741
+ ]);
28742
+ return xs.at(3) ?? ce.Nothing;
28743
+ }
28140
28744
  },
28141
28745
  Last: {
28142
28746
  complexity: 8200,
@@ -28249,7 +28853,9 @@ ${lines.join("\n")}`;
28249
28853
  ],
28250
28854
  complexity: 8200,
28251
28855
  signature: "(value: indexed_collection, start: number, end: number) -> list",
28252
- type: ([xs]) => parseType(`list<${collectionElementType(xs.type.type)}>`),
28856
+ type: ([xs]) => parseType(
28857
+ `list<${typeToString(collectionElementType(xs.type.type) ?? "any")}>`
28858
+ ),
28253
28859
  collection: {
28254
28860
  isLazy: (_expr) => true,
28255
28861
  count: (expr2) => {
@@ -28581,16 +29187,26 @@ ${lines.join("\n")}`;
28581
29187
  },
28582
29188
  // Randomize the order of the elements in the collection.
28583
29189
  Shuffle: {
28584
- description: "Randomize the order of the elements in the collection.",
29190
+ description: "Randomize the order of the elements in the collection. With an optional `seed` argument, the shuffle is deterministic.",
28585
29191
  complexity: 8200,
28586
- signature: "(indexed_collection) -> indexed_collection",
29192
+ signature: "(indexed_collection, real?) -> indexed_collection",
28587
29193
  type: (ops) => ops[0].type,
28588
- evaluate: ([xs], { engine: ce }) => {
29194
+ evaluate: ([xs, seedOp], { engine: ce }) => {
28589
29195
  if (!xs.isFiniteCollection) return void 0;
28590
29196
  const data = Array.from(xs.each());
28591
- for (let i = data.length - 1; i > 0; i--) {
28592
- const j = Math.floor(Math.random() * (i + 1));
28593
- [data[i], data[j]] = [data[j], data[i]];
29197
+ const seed = seedOp?.re;
29198
+ if (seed !== void 0 && !Number.isNaN(seed)) {
29199
+ let s = seed;
29200
+ for (let i = data.length - 1; i > 0; i--) {
29201
+ const j = Math.floor(deterministicRandom(s) * (i + 1));
29202
+ [data[i], data[j]] = [data[j], data[i]];
29203
+ s = nextSeed(s);
29204
+ }
29205
+ } else {
29206
+ for (let i = data.length - 1; i > 0; i--) {
29207
+ const j = Math.floor(Math.random() * (i + 1));
29208
+ [data[i], data[j]] = [data[j], data[i]];
29209
+ }
28594
29210
  }
28595
29211
  return ce.function(xs.operator, data);
28596
29212
  }
@@ -28657,7 +29273,9 @@ ${lines.join("\n")}`;
28657
29273
  if (t === "string")
28658
29274
  return parseType(`tuple<list<string>, list<integer>>`);
28659
29275
  return parseType(
28660
- `tuple<list<${collectionElementType(t)}>, list<integer>>`
29276
+ `tuple<list<${typeToString(
29277
+ collectionElementType(t) ?? "any"
29278
+ )}>, list<integer>>`
28661
29279
  );
28662
29280
  },
28663
29281
  evaluate: (ops, { engine: ce }) => {
@@ -28673,7 +29291,7 @@ ${lines.join("\n")}`;
28673
29291
  description: "Return a list of the unique elements of the collection.",
28674
29292
  complexity: 8200,
28675
29293
  signature: "(collection) -> list",
28676
- type: ([xs]) => `list<${collectionElementType(xs.type.type)}>`,
29294
+ type: ([xs]) => `list<${typeToString(collectionElementType(xs.type.type) ?? "any")}>`,
28677
29295
  evaluate: (ops, { engine: ce }) => {
28678
29296
  if (!ops[0].isFiniteCollection) return void 0;
28679
29297
  const [values, _counts] = tally(ops[0]);
@@ -28685,7 +29303,7 @@ ${lines.join("\n")}`;
28685
29303
  wikidata: "Q381060",
28686
29304
  complexity: 8200,
28687
29305
  signature: "(collection, integer | function) -> list",
28688
- type: ([xs]) => `list<${collectionElementType(xs.type.type)}>`,
29306
+ type: ([xs]) => `list<${typeToString(collectionElementType(xs.type.type) ?? "any")}>`,
28689
29307
  evaluate: ([xs, arg], { engine: ce }) => {
28690
29308
  if (!xs.isFiniteCollection) return void 0;
28691
29309
  const k = toInteger(arg);
@@ -28859,32 +29477,74 @@ ${lines.join("\n")}`;
28859
29477
  }
28860
29478
  }
28861
29479
  },
28862
- // Repeat(x) -> [x, x, ...]
28863
- // This is an infinite series. Can use Take(Repeat(x), n) to get a finite series
28864
- // x is evaluated once. Although could use Hold()?
28865
- // So that First(Repeat(Hold(Random(5))), 10) would return 10 random numbers...
29480
+ // Repeat(x) -> [x, x, ...] — infinite sequence
29481
+ // Repeat(x, n) -> [x, x, ..., x] — finite list of n copies
28866
29482
  Repeat: {
28867
- description: "Produce an infinite sequence by repeating a single value.",
29483
+ 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.",
28868
29484
  complexity: 8200,
28869
- signature: "(value: any) -> list",
29485
+ signature: "(value: any, count: integer?) -> list",
29486
+ evaluate: (ops, { engine }) => {
29487
+ if (ops.length !== 2) return void 0;
29488
+ const raw = toInteger(ops[1]);
29489
+ if (raw === null) return void 0;
29490
+ const n = Math.max(0, raw);
29491
+ if (n > engine.maxCollectionSize) return void 0;
29492
+ return engine._fn("List", Array(n).fill(ops[0]));
29493
+ },
28870
29494
  collection: {
28871
- isLazy: (_expr) => true,
28872
- count: () => Infinity,
28873
- isEmpty: (_expr) => false,
28874
- // Never empty
28875
- isFinite: () => false,
28876
- // Infinite collection
29495
+ isLazy: (expr2) => isFunction2(expr2) && expr2.ops?.length === 1,
29496
+ count: (expr2) => {
29497
+ if (!isFunction2(expr2)) return void 0;
29498
+ if (expr2.ops?.length === 2) {
29499
+ const n = toInteger(expr2.op2);
29500
+ return n !== null ? Math.max(0, n) : void 0;
29501
+ }
29502
+ return Infinity;
29503
+ },
29504
+ isEmpty: (expr2) => {
29505
+ if (!isFunction2(expr2)) return void 0;
29506
+ if (expr2.ops?.length === 2) {
29507
+ const n = toInteger(expr2.op2);
29508
+ return n !== null ? n <= 0 : void 0;
29509
+ }
29510
+ return false;
29511
+ },
29512
+ isFinite: (expr2) => isFunction2(expr2) && expr2.ops?.length === 2,
28877
29513
  contains: (expr2, target) => {
28878
29514
  if (!isFunction2(expr2)) return false;
29515
+ if (expr2.ops?.length === 2) {
29516
+ const n = toInteger(expr2.op2);
29517
+ if (n !== null && n <= 0) return false;
29518
+ }
28879
29519
  return expr2.op1.isSame(target);
28880
29520
  },
28881
29521
  iterator: (expr2) => {
28882
29522
  if (!isFunction2(expr2))
28883
29523
  return { next: () => ({ value: void 0, done: true }) };
29524
+ if (expr2.ops?.length === 2) {
29525
+ const n = toInteger(expr2.op2);
29526
+ if (n === null) {
29527
+ return { next: () => ({ value: void 0, done: true }) };
29528
+ }
29529
+ const count = Math.max(0, n);
29530
+ let i = 0;
29531
+ return {
29532
+ next: () => i++ < count ? { value: expr2.op1, done: false } : { value: void 0, done: true }
29533
+ };
29534
+ }
28884
29535
  return { next: () => ({ value: expr2.op1, done: false }) };
28885
29536
  },
28886
- at: (expr2, _index) => {
29537
+ // at is 1-based (consistent with Range, Take, and other collection handlers)
29538
+ at: (expr2, index) => {
28887
29539
  if (!isFunction2(expr2)) return void 0;
29540
+ if (typeof index !== "number") return void 0;
29541
+ if (expr2.ops?.length === 2) {
29542
+ const n = toInteger(expr2.op2);
29543
+ const count = n !== null ? Math.max(0, n) : 0;
29544
+ if (index < 1 || index > count) return void 0;
29545
+ } else {
29546
+ if (index < 1) return void 0;
29547
+ }
28888
29548
  return expr2.op1;
28889
29549
  }
28890
29550
  }
@@ -29115,17 +29775,14 @@ ${lines.join("\n")}`;
29115
29775
  if (!isFunction2(expr2)) return [1, 0, 0];
29116
29776
  if (expr2.nops === 0) return [1, 0, 0];
29117
29777
  let op1 = expr2.op1.re;
29118
- if (!isFinite(op1)) op1 = 1;
29119
- else op1 = Math.round(op1);
29778
+ if (!isFinite(op1) && !op1) op1 = 1;
29120
29779
  if (expr2.nops === 1) return [1, op1, 1];
29121
29780
  let op2 = expr2.op2.re;
29122
29781
  if (!isFinite(op2) && !op2) op2 = 1;
29123
- else if (isFinite(op2)) op2 = Math.round(op2);
29124
- if (expr2.nops === 2) return [op1, op2, op2 > op1 ? 1 : -1];
29782
+ if (expr2.nops === 2) return [op1, op2, op2 >= op1 ? 1 : -1];
29125
29783
  let op3 = expr2.op3.re;
29126
- if (!isFinite(op3)) op3 = 1;
29127
- else op3 = Math.abs(Math.round(op3));
29128
- return [op1, op2, op1 < op2 ? op3 : -op3];
29784
+ if (!isFinite(op3) && !op3) op3 = 1;
29785
+ return [op1, op2, op3];
29129
29786
  }
29130
29787
  function rangeLast(r) {
29131
29788
  const [lower, upper, step] = r;
@@ -31173,11 +31830,12 @@ ${lines.join("\n")}`;
31173
31830
  );
31174
31831
  }
31175
31832
  },
31176
- // Complex: {
31177
- // // This function is converted during boxing, so unlikely to encounter
31178
- // wikidata: 'Q11567',
31179
- // complexity: 500,
31180
- // },
31833
+ Complex: {
31834
+ 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.',
31835
+ wikidata: "Q11567",
31836
+ complexity: 500,
31837
+ signature: "(real: number, imaginary: number) -> complex"
31838
+ },
31181
31839
  Divide: {
31182
31840
  description: "Quotient of a numerator and one or more denominators.",
31183
31841
  wikidata: "Q1226939",
@@ -32527,48 +33185,83 @@ ${lines.join("\n")}`;
32527
33185
  }
32528
33186
  },
32529
33187
  Sum: {
32530
- description: "`Sum(f, [a, b])` computes the sum of `f` from `a` to `b`",
33188
+ description: "`Sum(f, [a, b])` computes the sum of `f` from `a` to `b`; `Sum(L)` sums the elements of a collection `L`",
32531
33189
  wikidata: "Q218005",
32532
33190
  complexity: 1e3,
32533
33191
  broadcastable: false,
32534
33192
  scoped: true,
32535
33193
  lazy: true,
32536
- signature: "((number) -> number, bounds:tuple+) -> number",
32537
- canonical: ([body, ...bounds], { scope }) => canonicalBigop("Sum", body, bounds, scope),
32538
- evaluate: ([body, ...indexes], { engine, numericApproximation: numericApproximation2 }) => {
33194
+ signature: "(any, tuple*) -> number",
33195
+ canonical: ([body, ...bounds], { scope, engine: ce }) => {
33196
+ if (bounds.length === 0) {
33197
+ const canon = body?.canonical;
33198
+ if (canon?.isCollection) return ce._fn("Sum", [canon]);
33199
+ }
33200
+ return canonicalBigop("Sum", body, bounds, scope);
33201
+ },
33202
+ evaluate: ([first, ...rest], { engine, numericApproximation: numericApproximation2 }) => {
33203
+ if (rest.length === 0 && first?.isCollection) {
33204
+ if (first.isFiniteCollection !== true) return void 0;
33205
+ const result2 = run(
33206
+ reduceCollection2(
33207
+ first,
33208
+ engine.Zero,
33209
+ (acc, x) => acc.add(x.evaluate({ numericApproximation: numericApproximation2 }))
33210
+ ),
33211
+ engine._timeRemaining
33212
+ );
33213
+ return result2?.evaluate({ numericApproximation: numericApproximation2 }) ?? engine.NaN;
33214
+ }
32539
33215
  const result = run(
32540
33216
  reduceBigOp(
32541
- body,
32542
- indexes,
33217
+ first,
33218
+ rest,
32543
33219
  (acc, x) => acc.add(x.evaluate({ numericApproximation: numericApproximation2 })),
32544
33220
  engine.Zero
32545
33221
  ),
32546
33222
  engine._timeRemaining
32547
33223
  );
32548
- if (result === NON_ENUMERABLE_DOMAIN) {
32549
- return void 0;
32550
- }
33224
+ if (result === NON_ENUMERABLE_DOMAIN) return void 0;
32551
33225
  return result?.evaluate({ numericApproximation: numericApproximation2 }) ?? engine.NaN;
32552
33226
  },
32553
- evaluateAsync: async (xs, { engine, signal, numericApproximation: numericApproximation2 }) => {
33227
+ evaluateAsync: async ([first, ...rest], { engine, signal, numericApproximation: numericApproximation2 }) => {
33228
+ if (rest.length === 0 && first?.isCollection) {
33229
+ if (first.isFiniteCollection !== true) return void 0;
33230
+ const result2 = await runAsync(
33231
+ reduceCollection2(
33232
+ first,
33233
+ engine.Zero,
33234
+ (acc, x) => acc.add(x.evaluate({ numericApproximation: numericApproximation2 }))
33235
+ ),
33236
+ engine._timeRemaining,
33237
+ signal
33238
+ );
33239
+ return result2?.evaluate({ numericApproximation: numericApproximation2 }) ?? engine.NaN;
33240
+ }
32554
33241
  const result = await runAsync(
32555
33242
  reduceBigOp(
32556
- xs[0],
32557
- xs.slice(1),
33243
+ first,
33244
+ rest,
32558
33245
  (acc, x) => acc.add(x.evaluate({ numericApproximation: numericApproximation2 })),
32559
33246
  engine.Zero
32560
33247
  ),
32561
33248
  engine._timeRemaining,
32562
33249
  signal
32563
33250
  );
32564
- if (result === NON_ENUMERABLE_DOMAIN) {
32565
- return void 0;
32566
- }
33251
+ if (result === NON_ENUMERABLE_DOMAIN) return void 0;
32567
33252
  return result?.evaluate({ numericApproximation: numericApproximation2 }) ?? engine.NaN;
32568
33253
  }
32569
33254
  }
32570
33255
  }
32571
33256
  ];
33257
+ function* reduceCollection2(collection, init, combine) {
33258
+ let acc = init;
33259
+ for (const x of collection.each()) {
33260
+ acc = combine(acc, x);
33261
+ yield acc;
33262
+ }
33263
+ return acc;
33264
+ }
32572
33265
  function evaluateAbs(arg) {
32573
33266
  const ce = arg.engine;
32574
33267
  if (isNumber(arg)) {
@@ -42805,7 +43498,7 @@ ${e.message}
42805
43498
  var CONTROL_STRUCTURES_LIBRARY = [
42806
43499
  {
42807
43500
  Block: {
42808
- description: "Evaluate a sequence of expressions in a local scope.",
43501
+ 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.",
42809
43502
  lazy: true,
42810
43503
  scoped: true,
42811
43504
  signature: "(unknown*) -> unknown",
@@ -42855,12 +43548,42 @@ ${e.message}
42855
43548
  }
42856
43549
  },
42857
43550
  Loop: {
42858
- description: "Evaluate a body expression over elements of a collection.",
43551
+ description: "Evaluate a body expression in nested iteration over Element clauses. Later clauses see earlier bindings; independent clauses produce a Cartesian product.",
42859
43552
  lazy: true,
42860
- signature: "(body:expression, collection:expression) -> any",
42861
- type: ([body]) => body.type,
42862
- evaluate: ([body, collection], { engine: ce }) => run(runLoop(body, collection, ce), ce._timeRemaining),
42863
- evaluateAsync: async ([body, collection], { engine: ce, signal }) => runAsync(runLoop(body, collection, ce), ce._timeRemaining, signal)
43553
+ signature: "(body:expression, iterators:expression*) -> any",
43554
+ type: ([body]) => {
43555
+ if (!body) return "nothing";
43556
+ return parseType(`indexed_collection<${String(body.type)}>`);
43557
+ },
43558
+ canonical: canonicalLoop,
43559
+ evaluate: (ops, { engine: ce }) => run(runLoop(ops[0], ops.slice(1), ce), ce._timeRemaining),
43560
+ evaluateAsync: async (ops, { engine: ce, signal }) => runAsync(runLoop(ops[0], ops.slice(1), ce), ce._timeRemaining, signal)
43561
+ },
43562
+ When: {
43563
+ 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.',
43564
+ lazy: true,
43565
+ signature: "(expression, boolean) -> any",
43566
+ type: ([expr2]) => expr2.type,
43567
+ canonical: (args, { engine: ce }) => {
43568
+ if (args.length !== 2) return null;
43569
+ const [expr2, cond] = args;
43570
+ if (isFunction2(expr2, "When")) {
43571
+ const inner = expr2.op1.canonical;
43572
+ const innerCond = expr2.op2.canonical;
43573
+ return ce._fn("When", [
43574
+ inner,
43575
+ ce._fn("And", [innerCond, cond.canonical])
43576
+ ]);
43577
+ }
43578
+ return ce._fn("When", [expr2.canonical, cond.canonical]);
43579
+ },
43580
+ evaluate: ([expr2, cond], { engine: ce }) => {
43581
+ const c = cond.evaluate();
43582
+ const cs = sym(c);
43583
+ if (cs === "True") return expr2.evaluate();
43584
+ if (cs === "False") return ce.symbol("Undefined");
43585
+ return ce._fn("When", [expr2, c]);
43586
+ }
42864
43587
  },
42865
43588
  Which: {
42866
43589
  description: "Return the value for the first condition that is true.",
@@ -42920,9 +43643,141 @@ ${e.message}
42920
43643
  );
42921
43644
  return result;
42922
43645
  }
42923
- function* runLoop(body, collection, ce) {
43646
+ function canonicalLoop(ops, options) {
43647
+ const { engine: ce, scope } = options;
43648
+ if (ops.length === 0) return null;
43649
+ if (ops.length === 1) {
43650
+ return ce._fn("Loop", [ops[0].canonical]);
43651
+ }
43652
+ const body = ops[0];
43653
+ const iterators = ops.slice(1);
43654
+ const allElement = iterators.every((it) => it.operator === "Element");
43655
+ if (!allElement) {
43656
+ return ce._fn(
43657
+ "Loop",
43658
+ ops.map((op) => op.canonical)
43659
+ );
43660
+ }
43661
+ const loopScope = scope ?? {
43662
+ parent: ce.context.lexicalScope,
43663
+ bindings: /* @__PURE__ */ new Map()
43664
+ };
43665
+ loopScope.noAutoDeclare = true;
43666
+ ce.pushScope(loopScope);
43667
+ let canonicalIterators;
43668
+ let canonicalBody;
43669
+ try {
43670
+ canonicalIterators = iterators.map((it) => {
43671
+ if (!isFunction2(it, "Element")) {
43672
+ return ce._fn("Element", [
43673
+ ce.error("missing").canonical,
43674
+ ce.error("missing").canonical
43675
+ ]);
43676
+ }
43677
+ const indexExpr = it.ops[0];
43678
+ const collExpr = it.ops[1];
43679
+ if (!indexExpr || !collExpr) {
43680
+ return ce._fn("Element", [
43681
+ (indexExpr ?? ce.error("missing")).canonical,
43682
+ (collExpr ?? ce.error("missing")).canonical
43683
+ ]);
43684
+ }
43685
+ if (isSymbol2(indexExpr) && indexExpr.symbol !== "Nothing") {
43686
+ if (!ce.context.lexicalScope.bindings.has(indexExpr.symbol))
43687
+ ce.declare(indexExpr.symbol, "unknown");
43688
+ }
43689
+ return ce._fn("Element", [indexExpr.canonical, collExpr.canonical]);
43690
+ });
43691
+ canonicalBody = body.canonical;
43692
+ } finally {
43693
+ ce.popScope();
43694
+ loopScope.noAutoDeclare = false;
43695
+ }
43696
+ return ce._fn("Loop", [canonicalBody, ...canonicalIterators], {
43697
+ scope: loopScope
43698
+ });
43699
+ }
43700
+ function* runLoop(body, elements, ce) {
42924
43701
  body ??= ce.Nothing;
42925
43702
  if (sym(body) === "Nothing") return body;
43703
+ if (elements.length === 0) {
43704
+ const result = body.evaluate();
43705
+ yield result;
43706
+ return result;
43707
+ }
43708
+ if (elements.length === 1 && elements[0].operator !== "Element") {
43709
+ return yield* runLoopLegacy(body, elements[0], ce);
43710
+ }
43711
+ const results = [];
43712
+ const state = { stopped: false, broke: false, count: 0 };
43713
+ const freshScope = {
43714
+ parent: ce.context.lexicalScope,
43715
+ bindings: /* @__PURE__ */ new Map()
43716
+ };
43717
+ ce._pushEvalContext(freshScope);
43718
+ try {
43719
+ for (const elem of elements) {
43720
+ if (!isFunction2(elem, "Element")) continue;
43721
+ const idx = elem.ops[0];
43722
+ if (idx && isSymbol2(idx) && idx.symbol !== "Nothing") {
43723
+ if (!freshScope.bindings.has(idx.symbol))
43724
+ ce.declare(idx.symbol, "unknown");
43725
+ }
43726
+ }
43727
+ yield* runLoopNested(body, elements, 0, ce, results, state);
43728
+ } finally {
43729
+ ce._popEvalContext();
43730
+ }
43731
+ if (state.stopped && state.value !== void 0) {
43732
+ if (!state.broke) return state.value;
43733
+ return state.value;
43734
+ }
43735
+ return ce.function("List", results);
43736
+ }
43737
+ function* runLoopNested(body, elements, index, ce, results, state) {
43738
+ if (state.stopped) return;
43739
+ if (index === elements.length) {
43740
+ const result = body.evaluate();
43741
+ state.count += 1;
43742
+ if (state.count > ce.iterationLimit)
43743
+ throw new CancellationError({ cause: "iteration-limit-exceeded" });
43744
+ if (isFunction2(result, "Break")) {
43745
+ state.stopped = true;
43746
+ state.broke = true;
43747
+ state.value = result.op1;
43748
+ return;
43749
+ }
43750
+ if (result.operator === "Return") {
43751
+ state.stopped = true;
43752
+ state.value = result;
43753
+ return;
43754
+ }
43755
+ results.push(result);
43756
+ yield result;
43757
+ return;
43758
+ }
43759
+ const elem = elements[index];
43760
+ if (!isFunction2(elem, "Element")) {
43761
+ return;
43762
+ }
43763
+ const indexExpr = elem.ops[0];
43764
+ const collExpr = elem.ops[1];
43765
+ if (!indexExpr || !isSymbol2(indexExpr) || !collExpr) {
43766
+ return;
43767
+ }
43768
+ const name = indexExpr.symbol;
43769
+ const collection = collExpr.evaluate();
43770
+ if (!collection?.isCollection) {
43771
+ return;
43772
+ }
43773
+ const skipAssign = name === "Nothing";
43774
+ for (const value of collection.each()) {
43775
+ if (!skipAssign) ce.assign(name, value);
43776
+ yield* runLoopNested(body, elements, index + 1, ce, results, state);
43777
+ if (state.stopped) return;
43778
+ }
43779
+ }
43780
+ function* runLoopLegacy(body, collection, ce) {
42926
43781
  if (collection?.isCollection) {
42927
43782
  let result = void 0;
42928
43783
  const fn = applicable(body);
@@ -44564,7 +45419,7 @@ ${e.message}
44564
45419
  evaluate: (ops) => apply(ops[0], ops.slice(1))
44565
45420
  },
44566
45421
  Assign: {
44567
- description: "Assign a value to a symbol or define a sequence",
45422
+ 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).",
44568
45423
  lazy: true,
44569
45424
  pure: false,
44570
45425
  signature: "(symbol | expression, any) -> any",
@@ -44695,7 +45550,12 @@ ${e.message}
44695
45550
  evaluate: (ops, { engine: ce }) => {
44696
45551
  const symbolName2 = sym(ops[0].evaluate());
44697
45552
  if (!symbolName2) return void 0;
45553
+ const currentScope = ce.context.lexicalScope;
45554
+ const existing = currentScope.bindings.get(symbolName2);
45555
+ const existingValueDef = existing && isValueDef(existing) ? existing : void 0;
45556
+ const isAutoDeclareHere = !!existingValueDef && existingValueDef.value.inferredType && existingValueDef.value.value === void 0;
44698
45557
  if (!ops[1]) {
45558
+ if (isAutoDeclareHere) return ce.Nothing;
44699
45559
  ce.declare(symbolName2, { inferred: true, type: "unknown" });
44700
45560
  return ce.Nothing;
44701
45561
  }
@@ -44704,6 +45564,11 @@ ${e.message}
44704
45564
  (isString(t) ? t.string : void 0) ?? sym(t) ?? void 0
44705
45565
  );
44706
45566
  if (!isValidType(type2)) return void 0;
45567
+ if (isAutoDeclareHere && existingValueDef) {
45568
+ existingValueDef.value.type = ce.type(type2);
45569
+ existingValueDef.value.inferredType = false;
45570
+ return ce.Nothing;
45571
+ }
44707
45572
  ce.declare(symbolName2, type2);
44708
45573
  return ce.Nothing;
44709
45574
  }
@@ -44821,33 +45686,41 @@ ${e.message}
44821
45686
  },
44822
45687
  Random: {
44823
45688
  description: [
44824
- "Random(): Return a random number between 0 and 1",
44825
- "Random(n): Return a random integer between 0 and n-1",
44826
- "Random(m, n): Return a random integer between m and n-1"
45689
+ "Random(): non-deterministic float in [0, 1)",
45690
+ "Random(seed: real): deterministic float in [0, 1) from a real seed",
45691
+ "Random(n: integer): non-deterministic integer in [0, n)",
45692
+ "Random(m: integer, n: integer): non-deterministic integer in [m, n)"
44827
45693
  ],
44828
45694
  pure: false,
44829
- signature: "(lower:integer?, upper:integer?) -> finite_number",
44830
- type: ([lower, upper]) => {
44831
- if (lower === void 0 && upper === void 0) return "finite_number";
44832
- return "finite_integer";
45695
+ // Signature accepts: nothing, one number, or two integers.
45696
+ // Use `number` (not `integer`) for the single-arg case so float seeds
45697
+ // type-check; runtime dispatch differentiates integer vs real.
45698
+ signature: "(number?, integer?) -> finite_number",
45699
+ type: ([first, second]) => {
45700
+ if (first === void 0) return "finite_number";
45701
+ if (second !== void 0) return "finite_integer";
45702
+ if (first.type.matches("integer")) return "finite_integer";
45703
+ return "finite_number";
44833
45704
  },
44834
45705
  sgn: () => "non-negative",
44835
45706
  evaluate: (ops, { engine: ce }) => {
44836
45707
  if (ops.length === 0) return ce.number(Math.random());
44837
- const [lowerOp, upperOp] = ops;
44838
- let lower;
44839
- let upper;
44840
- if (upperOp === void 0) {
44841
- lower = 0;
44842
- upper = Math.floor(lowerOp.re - 1);
44843
- if (isNaN(upper)) upper = 0;
44844
- } else {
44845
- lower = Math.floor(lowerOp.re);
44846
- upper = Math.floor(upperOp.re);
45708
+ const [firstOp, secondOp] = ops;
45709
+ if (secondOp !== void 0) {
45710
+ let lower = Math.floor(firstOp.re);
45711
+ let upper = Math.floor(secondOp.re);
44847
45712
  if (isNaN(lower)) lower = 0;
44848
45713
  if (isNaN(upper)) upper = 0;
45714
+ return ce.number(lower + Math.floor(Math.random() * (upper - lower)));
45715
+ }
45716
+ if (firstOp.type.matches("integer")) {
45717
+ let n = Math.floor(firstOp.re);
45718
+ if (isNaN(n)) n = 0;
45719
+ return ce.number(Math.floor(Math.random() * n));
44849
45720
  }
44850
- return ce.number(lower + Math.floor(Math.random() * (upper - lower)));
45721
+ const seed = firstOp.re;
45722
+ if (isNaN(seed)) return ce.number(0);
45723
+ return ce.number(deterministicRandom(seed));
44851
45724
  }
44852
45725
  },
44853
45726
  // @todo: need review
@@ -45303,6 +46176,14 @@ ${e.message}
45303
46176
  To: {
45304
46177
  description: "Action arrow / mapping (`a \\to b`) \u2014 opaque typed head.",
45305
46178
  signature: "(any, any) -> nothing"
46179
+ },
46180
+ Colon: {
46181
+ description: "Type annotation (`a : b`) \u2014 opaque typed head.",
46182
+ signature: "(any, any) -> expression"
46183
+ },
46184
+ Prime: {
46185
+ description: "Derivative or prime notation (`f'`, `f^{(n)}`) \u2014 opaque typed head until a derivative library handler runs.",
46186
+ signature: "(any, integer?) -> expression"
45306
46187
  }
45307
46188
  }
45308
46189
  ];
@@ -50343,18 +51224,28 @@ ${e.message}
50343
51224
  },
50344
51225
  {
50345
51226
  Sample: {
50346
- description: "Return a random sample of k elements from the collection, without replacement.",
51227
+ description: "Return a random sample of k elements from the collection, without replacement. With an optional `seed` argument, the sample is deterministic.",
50347
51228
  complexity: 8200,
50348
- signature: "(collection, integer) -> list",
50349
- evaluate: ([xs, nArg], { engine: ce }) => {
51229
+ signature: "(collection, integer, real?) -> list",
51230
+ evaluate: ([xs, nArg, seedArg], { engine: ce }) => {
50350
51231
  if (!xs.isFiniteCollection) return void 0;
50351
51232
  const k = toInteger(nArg);
50352
51233
  if (k === null || k < 0) return void 0;
50353
51234
  const data = Array.from(xs.each());
50354
51235
  if (k > data.length) return void 0;
50355
- for (let i = data.length - 1; i > 0; i--) {
50356
- const j = Math.floor(Math.random() * (i + 1));
50357
- [data[i], data[j]] = [data[j], data[i]];
51236
+ const seed = seedArg?.re;
51237
+ if (seed !== void 0 && !Number.isNaN(seed)) {
51238
+ let s = seed;
51239
+ for (let i = data.length - 1; i > 0; i--) {
51240
+ const j = Math.floor(deterministicRandom(s) * (i + 1));
51241
+ [data[i], data[j]] = [data[j], data[i]];
51242
+ s = nextSeed(s);
51243
+ }
51244
+ } else {
51245
+ for (let i = data.length - 1; i > 0; i--) {
51246
+ const j = Math.floor(Math.random() * (i + 1));
51247
+ [data[i], data[j]] = [data[j], data[i]];
51248
+ }
50358
51249
  }
50359
51250
  const sample = data.slice(0, k);
50360
51251
  return ce.function("List", sample);
@@ -53175,6 +54066,20 @@ Error in definition of "${name}"`,
53175
54066
  if (results.length === 1) return results[0];
53176
54067
  return this.engine._fn("List", results);
53177
54068
  }
54069
+ if (def instanceof _BoxedOperatorDefinition && def._isLambda && this.ops.some((x) => isFiniteIndexedCollection(x)) && paramsAreScalar(def)) {
54070
+ const items = zip(this._ops);
54071
+ if (items) {
54072
+ const results = [];
54073
+ while (true) {
54074
+ const { done, value } = items.next();
54075
+ if (done) break;
54076
+ results.push(
54077
+ this.engine._fn(this.operator, value).evaluate(options)
54078
+ );
54079
+ }
54080
+ return this.engine._fn("List", results);
54081
+ }
54082
+ }
53178
54083
  if (materialization !== false && !def.evaluate && this.isLazyCollection)
53179
54084
  return materialize(this, def, options);
53180
54085
  const tail = holdMap(this, (x) => x.evaluate(options));
@@ -53221,6 +54126,22 @@ Error in definition of "${name}"`,
53221
54126
  (resolved) => this.engine._fn("List", resolved)
53222
54127
  );
53223
54128
  }
54129
+ if (def instanceof _BoxedOperatorDefinition && def._isLambda && this.ops.some((x) => isFiniteIndexedCollection(x)) && paramsAreScalar(def)) {
54130
+ const items = zip(this._ops);
54131
+ if (items) {
54132
+ const results = [];
54133
+ while (true) {
54134
+ const { done, value } = items.next();
54135
+ if (done) break;
54136
+ results.push(
54137
+ this.engine._fn(this.operator, value).evaluateAsync(options)
54138
+ );
54139
+ }
54140
+ return Promise.all(results).then(
54141
+ (resolved) => this.engine._fn("List", resolved)
54142
+ );
54143
+ }
54144
+ }
53224
54145
  const tail = await holdMapAsync(
53225
54146
  this,
53226
54147
  async (x) => await x.evaluateAsync(options)
@@ -53435,8 +54356,47 @@ Error in definition of "${name}"`,
53435
54356
  const ops = expr2.ops.map((x) => x.evaluate(options));
53436
54357
  if (!value || value.type.isUnknown)
53437
54358
  return expr2.engine.function(expr2.operator, ops);
54359
+ if (ops.some((x) => isFiniteIndexedCollection(x)) && paramsAreScalar(value.type.type)) {
54360
+ const items = zip(ops);
54361
+ if (items) {
54362
+ const results = [];
54363
+ while (true) {
54364
+ const { done, value: zipped } = items.next();
54365
+ if (done) break;
54366
+ results.push(apply(value, zipped).evaluate(options));
54367
+ }
54368
+ return expr2.engine._fn("List", results);
54369
+ }
54370
+ }
53438
54371
  return apply(value, ops);
53439
54372
  }
54373
+ function paramsAreScalar(source) {
54374
+ const sigType = isOperatorDefinition(source) ? source.signature?.type : source;
54375
+ if (!sigType || typeof sigType === "string") return true;
54376
+ if (sigType.kind !== "signature") return true;
54377
+ const args = [
54378
+ ...sigType.args ?? [],
54379
+ ...sigType.optArgs ?? [],
54380
+ ...sigType.variadicArg ? [sigType.variadicArg] : []
54381
+ ];
54382
+ return args.every((arg) => isScalarType(arg.type));
54383
+ }
54384
+ function isOperatorDefinition(source) {
54385
+ return typeof source === "object" && source !== null && "signature" in source;
54386
+ }
54387
+ function isScalarType(t) {
54388
+ if (typeof t === "string") {
54389
+ if (t === "collection" || t === "indexed_collection" || t === "list" || t === "tuple" || t === "set" || t === "dictionary" || t === "record" || t === "function")
54390
+ return false;
54391
+ return true;
54392
+ }
54393
+ 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")
54394
+ return false;
54395
+ if (t.kind === "union" || t.kind === "intersection")
54396
+ return t.types.every((x) => isScalarType(x));
54397
+ if (t.kind === "negation") return isScalarType(t.type);
54398
+ return true;
54399
+ }
53440
54400
  function materialize(expr2, def, options) {
53441
54401
  if (!expr2.isValid || options?.materialization === false) return expr2;
53442
54402
  let materialization = options?.materialization ?? false;
@@ -53444,6 +54404,11 @@ Error in definition of "${name}"`,
53444
54404
  materialization = DEFAULT_MATERIALIZATION;
53445
54405
  const isIndexed = expr2.isIndexedCollection;
53446
54406
  const isFinite2 = expr2.isFiniteCollection;
54407
+ if (isIndexed && isFinite2) {
54408
+ const count = expr2.count;
54409
+ if (count !== void 0 && count > expr2.engine.maxCollectionSize)
54410
+ return expr2;
54411
+ }
53447
54412
  const xs = [];
53448
54413
  if (!expr2.isEmptyCollection) {
53449
54414
  if (!isIndexed || !isFinite2) {
@@ -53671,7 +54636,7 @@ Error in definition of "${name}"`,
53671
54636
  const eltType = widen(
53672
54637
  ...Object.values(this._keyValues).map((op) => op.type.type)
53673
54638
  );
53674
- this._type = this.engine.type(`dictionary<${eltType}>`);
54639
+ this._type = new BoxedType({ kind: "dictionary", values: eltType });
53675
54640
  return this._type;
53676
54641
  }
53677
54642
  get isPure() {
@@ -53949,6 +54914,7 @@ Error in definition of "${name}"`,
53949
54914
  }
53950
54915
  if (typeof expr2 === "number" || expr2 instanceof BigDecimal || expr2 instanceof Complex)
53951
54916
  return ce.number(expr2);
54917
+ if (typeof expr2 === "boolean") return ce.symbol(expr2 ? "True" : "False");
53952
54918
  if (typeof expr2 === "string") {
53953
54919
  if (matchesSymbol(expr2)) {
53954
54920
  const sym2 = symbol(expr2);
@@ -55005,6 +55971,23 @@ Error in definition of "${name}"`,
55005
55971
  };
55006
55972
  return compilePair(0);
55007
55973
  }
55974
+ if (h === "When") {
55975
+ if (args.length !== 2)
55976
+ throw new Error("When: expected exactly 2 arguments (expr, cond)");
55977
+ const fn2 = target.functions?.(h);
55978
+ if (fn2) {
55979
+ if (typeof fn2 === "function") {
55980
+ return fn2(args, (expr2) => _BaseCompiler.compile(expr2, target), target);
55981
+ }
55982
+ return `${fn2}(${args.map((x) => _BaseCompiler.compile(x, target)).join(", ")})`;
55983
+ }
55984
+ if (isSymbol2(args[1], "True"))
55985
+ return `(${_BaseCompiler.compile(args[0], target)})`;
55986
+ if (isSymbol2(args[1], "False")) return "NaN";
55987
+ const val = _BaseCompiler.compile(args[0], target);
55988
+ const cond = _BaseCompiler.compile(args[1], target);
55989
+ return `((${cond}) ? (${val}) : NaN)`;
55990
+ }
55008
55991
  if (h === "Block") {
55009
55992
  return _BaseCompiler.compileBlock(args, target);
55010
55993
  }
@@ -55079,17 +56062,98 @@ Error in definition of "${name}"`,
55079
56062
  )}${target.ws("\n")}})()`;
55080
56063
  }
55081
56064
  /**
55082
- * Compile a Loop expression with Element(index, Range(lo, hi)) indexing.
55083
- * Generates: (() => { for (let i = lo; i <= hi; i++) { body } })()
56065
+ * Compile a Loop expression.
56066
+ *
56067
+ * Two forms are supported:
56068
+ *
56069
+ * 1. **Imperative / single-Element form** (existing behaviour):
56070
+ * `Loop(body, Element(i, Range(lo, hi)))`
56071
+ * Generates a raw `for (let i = lo; i <= hi; i++) { body }` loop wrapped
56072
+ * in an IIFE. The loop counter is always a plain number. For targets
56073
+ * that wrap numeric values (e.g. interval-js uses `_IA.point()`),
56074
+ * references to the loop index inside the body are re-wrapped via
56075
+ * `target.number`. `break` / `continue` / `return` are preserved.
55084
56076
  *
55085
- * The loop counter is always a raw number. For targets that wrap numeric
55086
- * values (e.g. interval-js wraps with `_IA.point()`), references to the
55087
- * loop index inside the body are wrapped via `target.number`.
56077
+ * 2. **Comprehension / variadic-Element form** (new):
56078
+ * `Loop(body, Element(x, coll1), Element(y, coll2), )`
56079
+ * When two or more `Element` clauses are present or when the single
56080
+ * Element's collection is not a `Range` — the loop is compiled as a
56081
+ * comprehension that collects results into an array. Each clause
56082
+ * produces a `for (const name of collection)` loop, nested
56083
+ * outermost-to-innermost, and the innermost body pushes into `result`.
56084
+ *
56085
+ * Example output (JS):
56086
+ * ```js
56087
+ * (() => { const result = [];
56088
+ * for (const x of [1,2]) { for (const y of [3,4]) { result.push(body); } }
56089
+ * return result; })()
56090
+ * ```
56091
+ *
56092
+ * GLSL: multi-Element comprehension is not trivially representable in
56093
+ * GLSL (no dynamic arrays, no push). A compile-time error is thrown.
56094
+ * TODO(E3-GLSL): support GLSL multi-Element via a pre-declared fixed-size
56095
+ * array or by unrolling when bounds are known at compile time.
56096
+ *
56097
+ * Known issue (imperative form): the IIFE generated by form (1) has no
56098
+ * `return` statement, so `Loop(body, Element(i, Range(lo, hi)))` compiled
56099
+ * to JS evaluates to `undefined` at runtime, while CE evaluation returns a
56100
+ * `List` of body values. See `test/compute-engine/a1-c1-compile-parity.test.ts`
56101
+ * ("Loop compiles in JS") for the verify-only test that locks in the
56102
+ * current behavior.
55088
56103
  */
55089
56104
  static compileForLoop(args, target) {
55090
56105
  if (!args[0]) throw new Error("Loop: no body");
55091
56106
  if (!args[1]) throw new Error("Loop: no indexing set");
55092
- const indexing = args[1];
56107
+ const body = args[0];
56108
+ const elements = args.slice(1);
56109
+ const useComprehension = elements.length > 1 || elements.length === 1 && isFunction2(elements[0], "Element") && !_BaseCompiler.isLegacyCompatibleRange(elements[0].ops[1]);
56110
+ if (useComprehension) {
56111
+ const lang = target.language ?? "";
56112
+ if (lang === "glsl" || lang === "wgsl") {
56113
+ throw new Error(
56114
+ `${lang.toUpperCase()}: multi-Element Loop comprehension is not yet supported. TODO(E3-GLSL): unroll or use a fixed-size array.`
56115
+ );
56116
+ }
56117
+ const narrowedElements = [];
56118
+ for (let i = 0; i < elements.length; i++) {
56119
+ const elem = elements[i];
56120
+ if (!isFunction2(elem, "Element"))
56121
+ throw new Error(
56122
+ `Loop: argument ${i + 1} must be an Element clause, got ${elem.operator ?? "?"}`
56123
+ );
56124
+ if (!isSymbol2(elem.ops[0]))
56125
+ throw new Error(
56126
+ `Loop: Element index (argument ${i + 1}) must be a symbol`
56127
+ );
56128
+ narrowedElements.push(elem);
56129
+ }
56130
+ const loopVarSet = new Set(
56131
+ narrowedElements.map(
56132
+ (e) => e.ops[0].symbol
56133
+ )
56134
+ );
56135
+ const needsWrap2 = target.number(0) !== "0";
56136
+ const bodyTarget2 = needsWrap2 ? {
56137
+ ...target,
56138
+ var: (id) => loopVarSet.has(id) ? target.number(0).replace("0", id) : target.var(id)
56139
+ } : target;
56140
+ const bodyCode = _BaseCompiler.compile(body, bodyTarget2);
56141
+ let inner = `result.push(${bodyCode});`;
56142
+ for (let i = narrowedElements.length - 1; i >= 0; i--) {
56143
+ const elem = narrowedElements[i];
56144
+ const name = elem.ops[0].symbol;
56145
+ const collExpr = elem.ops[1];
56146
+ let collection;
56147
+ if (isFunction2(collExpr, "Range")) {
56148
+ collection = _BaseCompiler.compileRangeIterable(collExpr, bodyTarget2);
56149
+ } else {
56150
+ collection = _BaseCompiler.compile(collExpr, bodyTarget2);
56151
+ }
56152
+ inner = `for (const ${name} of ${collection}) { ${inner} }`;
56153
+ }
56154
+ return `(() => { const result = []; ${inner} return result; })()`;
56155
+ }
56156
+ const indexing = elements[0];
55093
56157
  if (!isFunction2(indexing, "Element"))
55094
56158
  throw new Error("Loop: expected Element(index, Range(lo, hi))");
55095
56159
  const indexExpr = indexing.ops[0];
@@ -55107,13 +56171,72 @@ Error in definition of "${name}"`,
55107
56171
  ...target,
55108
56172
  var: (id) => id === index ? needsWrap ? target.number(0).replace("0", index) : index : target.var(id)
55109
56173
  };
55110
- const bodyStmts = _BaseCompiler.compileLoopBody(args[0], bodyTarget);
56174
+ const bodyStmts = _BaseCompiler.compileLoopBody(body, bodyTarget);
55111
56175
  return `(() => {${target.ws(
55112
56176
  "\n"
55113
56177
  )}for (let ${index} = ${lower}; ${index} <= ${upper}; ${index}++) {${target.ws(
55114
56178
  "\n"
55115
56179
  )}${bodyStmts}${target.ws("\n")}}${target.ws("\n")}})()`;
55116
56180
  }
56181
+ /**
56182
+ * Returns `true` when the given collection expression is a `Range` whose
56183
+ * runtime semantics match the legacy imperative for-loop shape
56184
+ * `for (let i = lo; i <= hi; i++)`.
56185
+ *
56186
+ * Concretely: integer-ascending bounds and step omitted-or-1. When bounds
56187
+ * are not statically numeric we accept the Range (the historical
56188
+ * behaviour) — runtime mismatch in the descending-unknown-bounds case is
56189
+ * left as a known limitation; callers can force the iterable path by
56190
+ * supplying an explicit step.
56191
+ */
56192
+ static isLegacyCompatibleRange(coll) {
56193
+ if (!isFunction2(coll, "Range")) return false;
56194
+ if (coll.ops.length >= 3) {
56195
+ const stepExpr = coll.ops[2];
56196
+ if (!isNumber(stepExpr) || stepExpr.re !== 1) return false;
56197
+ }
56198
+ const lo = coll.ops[0];
56199
+ const hi = coll.ops[1];
56200
+ if (isNumber(lo) && !Number.isInteger(lo.re)) return false;
56201
+ if (isNumber(hi) && !Number.isInteger(hi.re)) return false;
56202
+ if (isNumber(lo) && isNumber(hi) && lo.re > hi.re) return false;
56203
+ return true;
56204
+ }
56205
+ /**
56206
+ * Compile a `Range(lo, hi)` or `Range(lo, hi, step)` expression into a JS
56207
+ * iterable expression. Mirrors the runtime semantics in
56208
+ * `library/collections.ts` Range:
56209
+ * count = step === 0 ? 0 : max(0, floor((hi - lo) / step) + 1)
56210
+ * element = lo + step * k (0-indexed)
56211
+ * Default step is 1 when omitted. Bounds and step may be fractional.
56212
+ *
56213
+ * Only used from the comprehension path in `compileForLoop`.
56214
+ * Caller must have already verified `isFunction(rangeExpr, 'Range')`.
56215
+ */
56216
+ static compileRangeIterable(rangeExpr, target) {
56217
+ const loExpr = rangeExpr.ops[0];
56218
+ const hiExpr = rangeExpr.ops[1];
56219
+ const stepExpr = rangeExpr.ops[2];
56220
+ if (isNumber(loExpr) && isNumber(hiExpr) && (stepExpr === void 0 || isNumber(stepExpr))) {
56221
+ const lo2 = loExpr.re;
56222
+ const hi2 = hiExpr.re;
56223
+ const step2 = stepExpr === void 0 ? hi2 >= lo2 ? 1 : -1 : stepExpr.re;
56224
+ if (step2 === 0) return "[]";
56225
+ const len = Math.max(0, Math.floor((hi2 - lo2) / step2) + 1);
56226
+ if (step2 === 1) {
56227
+ if (lo2 === 0) return `Array.from({length:${len}},(_,k)=>k)`;
56228
+ return `Array.from({length:${len}},(_,k)=>${lo2}+k)`;
56229
+ }
56230
+ return `Array.from({length:${len}},(_,k)=>${lo2}+(${step2})*k)`;
56231
+ }
56232
+ const lo = _BaseCompiler.compile(loExpr, target);
56233
+ const hi = _BaseCompiler.compile(hiExpr, target);
56234
+ if (stepExpr === void 0) {
56235
+ 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})`;
56236
+ }
56237
+ const step = _BaseCompiler.compile(stepExpr, target);
56238
+ 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})`;
56239
+ }
55117
56240
  /**
55118
56241
  * Compile a loop body expression as statements (not wrapped in IIFE).
55119
56242
  * Handles Break, Continue, Return as statements, and If as if-else when
@@ -59613,8 +60736,8 @@ Error in definition of "${name}"`,
59613
60736
  }
59614
60737
  if (effectiveOp === "greater" || effectiveOp === "greaterEqual") {
59615
60738
  const isStrict = effectiveOp === "greater";
59616
- if (bounds.lowerBound !== void 0) {
59617
- const lowerVal = isNumber(bounds.lowerBound) ? bounds.lowerBound.numericValue : void 0;
60739
+ if (bounds.lower !== void 0) {
60740
+ const lowerVal = isNumber(bounds.lower) ? bounds.lower.numericValue : void 0;
59618
60741
  if (typeof lowerVal === "number" && isFinite(lowerVal)) {
59619
60742
  if (isStrict) {
59620
60743
  if (lowerVal > k) return "tautology";
@@ -59626,8 +60749,8 @@ Error in definition of "${name}"`,
59626
60749
  }
59627
60750
  }
59628
60751
  }
59629
- if (bounds.upperBound !== void 0) {
59630
- const upperVal = isNumber(bounds.upperBound) ? bounds.upperBound.numericValue : void 0;
60752
+ if (bounds.upper !== void 0) {
60753
+ const upperVal = isNumber(bounds.upper) ? bounds.upper.numericValue : void 0;
59631
60754
  if (typeof upperVal === "number" && isFinite(upperVal)) {
59632
60755
  if (isStrict) {
59633
60756
  if (upperVal < k) return "contradiction";
@@ -59642,8 +60765,8 @@ Error in definition of "${name}"`,
59642
60765
  }
59643
60766
  } else {
59644
60767
  const isStrict = effectiveOp === "less";
59645
- if (bounds.upperBound !== void 0) {
59646
- const upperVal = isNumber(bounds.upperBound) ? bounds.upperBound.numericValue : void 0;
60768
+ if (bounds.upper !== void 0) {
60769
+ const upperVal = isNumber(bounds.upper) ? bounds.upper.numericValue : void 0;
59647
60770
  if (typeof upperVal === "number" && isFinite(upperVal)) {
59648
60771
  if (isStrict) {
59649
60772
  if (upperVal < k) return "tautology";
@@ -59654,8 +60777,8 @@ Error in definition of "${name}"`,
59654
60777
  }
59655
60778
  }
59656
60779
  }
59657
- if (bounds.lowerBound !== void 0) {
59658
- const lowerVal = isNumber(bounds.lowerBound) ? bounds.lowerBound.numericValue : void 0;
60780
+ if (bounds.lower !== void 0) {
60781
+ const lowerVal = isNumber(bounds.lower) ? bounds.lower.numericValue : void 0;
59659
60782
  if (typeof lowerVal === "number" && isFinite(lowerVal)) {
59660
60783
  if (isStrict) {
59661
60784
  if (lowerVal > k) return "contradiction";
@@ -59883,7 +61006,7 @@ Error in definition of "${name}"`,
59883
61006
  const patOp1B2 = pat.op1;
59884
61007
  if (isSymbol2(patOp1B2)) {
59885
61008
  const bounds = getInequalityBoundsFromAssumptions(ce, patOp1B2.symbol);
59886
- const bound = isLower ? bounds.lowerBound : bounds.upperBound;
61009
+ const bound = isLower ? bounds.lower : bounds.upper;
59887
61010
  const strictOk = isLower ? bounds.lowerStrict : bounds.upperStrict;
59888
61011
  if (bound !== void 0 && (!isStrict || strictOk === true))
59889
61012
  pushResult({ [boundWildcard]: bound });
@@ -59893,7 +61016,7 @@ Error in definition of "${name}"`,
59893
61016
  if (symbolWildcard && !symbolWildcard.startsWith("__")) {
59894
61017
  for (const s of candidatesFromAssumptions()) {
59895
61018
  const bounds = getInequalityBoundsFromAssumptions(ce, s);
59896
- const bound = isLower ? bounds.lowerBound : bounds.upperBound;
61019
+ const bound = isLower ? bounds.lower : bounds.upper;
59897
61020
  const strictOk = isLower ? bounds.lowerStrict : bounds.upperStrict;
59898
61021
  if (bound === void 0 || isStrict && strictOk !== true)
59899
61022
  continue;
@@ -60925,6 +62048,7 @@ Error in definition of "${name}"`,
60925
62048
  return `_SYS.cexp(${compile3(args[0])})`;
60926
62049
  return `Math.exp(${compile3(args[0])})`;
60927
62050
  },
62051
+ First: (args, compile3) => `${compile3(args[0])}[0]`,
60928
62052
  Floor: (args, compile3) => {
60929
62053
  if (BaseCompiler.isIntegerValued(args[0])) return compile3(args[0]);
60930
62054
  return `Math.floor(${compile3(args[0])})`;
@@ -61083,7 +62207,20 @@ Error in definition of "${name}"`,
61083
62207
  if (nConst !== void 0) return `Math.pow(${compile3(arg)}, ${1 / nConst})`;
61084
62208
  return `Math.pow(${compile3(arg)}, 1 / (${compile3(exp3)}))`;
61085
62209
  },
61086
- Random: "Math.random",
62210
+ Random: (args, compile3) => {
62211
+ if (args.length === 0) return "Math.random()";
62212
+ if (args.length === 2) {
62213
+ const m = compile3(args[0]);
62214
+ const n = compile3(args[1]);
62215
+ return `((${m}) + Math.floor(Math.random() * ((${n}) - (${m}))))`;
62216
+ }
62217
+ const arg = args[0];
62218
+ if (BaseCompiler.isIntegerValued(arg)) {
62219
+ return `Math.floor(Math.random() * (${compile3(arg)}))`;
62220
+ }
62221
+ const a = compile3(arg);
62222
+ return `(() => { const _s = (${a}) * 12.9898; const _v = Math.sin(_s) * 43758.5453; return _v - Math.floor(_v); })()`;
62223
+ },
61087
62224
  Round: (args, compile3) => {
61088
62225
  if (BaseCompiler.isIntegerValued(args[0])) return compile3(args[0]);
61089
62226
  return `Math.round(${compile3(args[0])})`;
@@ -61111,6 +62248,7 @@ Error in definition of "${name}"`,
61111
62248
  if (BaseCompiler.isComplexValued(arg)) return `_SYS.csech(${compile3(arg)})`;
61112
62249
  return `1 / Math.cosh(${compile3(arg)})`;
61113
62250
  },
62251
+ Second: (args, compile3) => `${compile3(args[0])}[1]`,
61114
62252
  Heaviside: "_SYS.heaviside",
61115
62253
  Sign: "Math.sign",
61116
62254
  Sinc: "_SYS.sinc",
@@ -61143,6 +62281,7 @@ Error in definition of "${name}"`,
61143
62281
  return `_SYS.ctanh(${compile3(args[0])})`;
61144
62282
  return `Math.tanh(${compile3(args[0])})`;
61145
62283
  },
62284
+ Third: (args, compile3) => `${compile3(args[0])}[2]`,
61146
62285
  Mod: ([a, b], compile3) => {
61147
62286
  if (a === null || b === null) throw new Error("Mod: missing argument");
61148
62287
  const ca = compile3(a);
@@ -62422,6 +63561,14 @@ Error in definition of "${name}"`,
62422
63561
  return `exp(${compile3(args[0])})`;
62423
63562
  },
62424
63563
  Exp2: "exp2",
63564
+ // Component access — assumes the argument compiles to a vec2/vec3/vec4
63565
+ // (the common case for 2D/3D points). For 5+-element tuples that compile
63566
+ // to `float[N]` arrays, swizzle access is invalid GLSL and the shader
63567
+ // will fail to compile; that's an edge case `First`/`Second`/`Third`
63568
+ // aren't designed for. Vec swizzles are identical between GLSL and WGSL.
63569
+ First: (args, compile3) => `${compile3(args[0])}.x`,
63570
+ Second: (args, compile3) => `${compile3(args[0])}.y`,
63571
+ Third: (args, compile3) => `${compile3(args[0])}.z`,
62425
63572
  Floor: (args, compile3) => {
62426
63573
  if (BaseCompiler.isIntegerValued(args[0])) return compile3(args[0]);
62427
63574
  return `floor(${compile3(args[0])})`;
@@ -62899,6 +64046,39 @@ Error in definition of "${name}"`,
62899
64046
  // Sum/Product — unrolled or for-loop
62900
64047
  Sum: (args, compile3, target) => compileGPUSumProduct("Sum", args, compile3, target),
62901
64048
  Product: (args, compile3, target) => compileGPUSumProduct("Product", args, compile3, target),
64049
+ // Range — inline constant array literal (bounds must be compile-time constants)
64050
+ Range: (args, _compile2, target) => {
64051
+ if (args.length < 2 || args.length > 3) {
64052
+ throw new Error(
64053
+ "Range: GPU compile expects 2 or 3 arguments (lo, hi, step?)"
64054
+ );
64055
+ }
64056
+ const lo = args[0].re;
64057
+ const hi = args[1].re;
64058
+ const step = args.length === 3 ? args[2].re : 1;
64059
+ if (!Number.isFinite(lo) || !Number.isFinite(hi) || !Number.isFinite(step)) {
64060
+ throw new Error(
64061
+ "Range: GPU compile requires constant numeric bounds (non-constant ranges must be materialized at JS host then uploaded as a uniform)"
64062
+ );
64063
+ }
64064
+ if (step === 0) throw new Error("Range: step cannot be zero");
64065
+ const count = Math.max(0, Math.floor((hi - lo) / step) + 1);
64066
+ if (count === 0) {
64067
+ throw new Error(
64068
+ "Range: empty range (lo > hi for positive step, or lo < hi for negative step)"
64069
+ );
64070
+ }
64071
+ if (count > 256) {
64072
+ throw new Error(
64073
+ `Range: GPU compile inlines ranges up to 256 elements (got ${count})`
64074
+ );
64075
+ }
64076
+ const values = [];
64077
+ for (let i = 0; i < count; i++) values.push(lo + i * step);
64078
+ const isWGSL = target.language === "wgsl";
64079
+ const arrayType = isWGSL ? `array<f32, ${count}>` : `float[${count}]`;
64080
+ return `${arrayType}(${values.map(formatGPUNumber).join(", ")})`;
64081
+ },
62902
64082
  // Loop — GPU for-loop (no IIFE, no let)
62903
64083
  Loop: (args, _compile2, target) => {
62904
64084
  if (!args[0]) throw new Error("Loop: no body");
@@ -62927,6 +64107,134 @@ Error in definition of "${name}"`,
62927
64107
  ${bodyCode};
62928
64108
  }`;
62929
64109
  },
64110
+ // Statistical functions
64111
+ /**
64112
+ * GCD of two scalar arguments.
64113
+ *
64114
+ * Uses a preamble helper `_gpu_gcd` (Euclidean algorithm via `mod`).
64115
+ * Only two-argument form is supported in GPU targets.
64116
+ */
64117
+ GCD: (args, compile3) => {
64118
+ if (args.length < 2) throw new Error("GCD: need at least two arguments");
64119
+ if (args.length > 2)
64120
+ throw new Error("GCD: GPU target supports only two-argument GCD");
64121
+ const a = args[0];
64122
+ const b = args[1];
64123
+ if (a === null || b === null) throw new Error("GCD: missing argument");
64124
+ return `_gpu_gcd(${compile3(a)}, ${compile3(b)})`;
64125
+ },
64126
+ /**
64127
+ * Variance of a compile-time-known list.
64128
+ *
64129
+ * Accepts either a single `List(...)` argument or N scalar arguments.
64130
+ * Generates fully inline code: computes mean then sum of squared deviations,
64131
+ * divided by (N-1) for sample variance (matches JS `_SYS.variance`).
64132
+ */
64133
+ Variance: (args, compile3) => {
64134
+ let elems;
64135
+ if (args.length === 1 && isFunction2(args[0], "List")) {
64136
+ elems = args[0].ops;
64137
+ } else if (args.length >= 2) {
64138
+ elems = args;
64139
+ } else {
64140
+ throw new Error(
64141
+ "Variance: GPU target requires a List argument or at least 2 scalar arguments"
64142
+ );
64143
+ }
64144
+ const n = elems.length;
64145
+ if (n < 2) throw new Error("Variance: need at least 2 elements");
64146
+ const compiled = elems.map((e) => compile3(e));
64147
+ const sum = compiled.join(" + ");
64148
+ const mean2 = `((${sum}) / ${formatGPUNumber(n)})`;
64149
+ const sqDiffs = compiled.map((c) => `(${c} - ${mean2}) * (${c} - ${mean2})`).join(" + ");
64150
+ return `((${sqDiffs}) / ${formatGPUNumber(n - 1)})`;
64151
+ },
64152
+ /**
64153
+ * Median of a compile-time-known list.
64154
+ *
64155
+ * Accepts either a single `List(...)` argument or N scalar arguments.
64156
+ * For N ≤ 8: generates a fully unrolled inline sorting network followed by
64157
+ * a middle-element pick. For larger N, throws (too large to inline cleanly).
64158
+ *
64159
+ * The sorting network uses the "odd-even merge sort" comparator pattern
64160
+ * inlined as `min`/`max` calls — no GPU statements required.
64161
+ */
64162
+ Median: (args, compile3) => {
64163
+ let elems;
64164
+ if (args.length === 1 && isFunction2(args[0], "List")) {
64165
+ elems = args[0].ops;
64166
+ } else if (args.length >= 1) {
64167
+ elems = args;
64168
+ } else {
64169
+ throw new Error(
64170
+ "Median: GPU target requires a List argument or at least 1 scalar argument"
64171
+ );
64172
+ }
64173
+ const n = elems.length;
64174
+ if (n === 0) throw new Error("Median: empty list");
64175
+ if (n > 8) {
64176
+ throw new Error(
64177
+ `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.`
64178
+ );
64179
+ }
64180
+ const compiled = elems.map((e) => compile3(e));
64181
+ if (n === 1) return compiled[0];
64182
+ return `_gpu_median_${n}(${compiled.join(", ")})`;
64183
+ },
64184
+ /**
64185
+ * Deterministic pseudorandom for GPU.
64186
+ *
64187
+ * All emitted forms return a GLSL `float` (or WGSL `f32`) so the result
64188
+ * composes with surrounding float arithmetic without explicit casts. The
64189
+ * "integer-bound" forms return an integer-valued float (the result of
64190
+ * `floor`), matching the convention used by `Floor` and other ostensibly
64191
+ * integer-returning operators in this target.
64192
+ *
64193
+ * - 0 args (GLSL only): fall back to a fragment-coord-derived seed.
64194
+ * Only meaningful in fragment shaders (gl_FragCoord is FS-only).
64195
+ * - 0 args (WGSL): throws — WGSL has no built-in fragment coordinate;
64196
+ * caller must provide an explicit seed.
64197
+ * - 1 arg, real-typed: `_gpu_random(seed)` — deterministic float in [0, 1)
64198
+ * - 1 arg, integer-typed: `floor(_gpu_random(float(n)) * float(n))` —
64199
+ * integer-valued float in {0, 1, ..., n-1}. The seed is derived from
64200
+ * `n` itself, so the result is per-pixel-and-n deterministic in GLSL.
64201
+ * - 2 args (integer m, n): float in [m, n), seeded from gl_FragCoord.
64202
+ *
64203
+ * JS-side `Random` has matching semantics (see `library/core.ts`'s
64204
+ * polymorphic dispatch). JS↔GLSL parity is approximate — same seed yields
64205
+ * a similar value, not bit-identical, due to fp64 vs fp32 and platform
64206
+ * `sin` differences.
64207
+ */
64208
+ Random: (args, compile3, target) => {
64209
+ if (args.length === 0) {
64210
+ if (target.language === "wgsl") {
64211
+ throw new Error(
64212
+ "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."
64213
+ );
64214
+ }
64215
+ return "_gpu_random(gl_FragCoord.x + gl_FragCoord.y * 1024.0)";
64216
+ }
64217
+ if (args.length === 1) {
64218
+ const arg = args[0];
64219
+ if (BaseCompiler.isIntegerValued(arg)) {
64220
+ const compiled = compile3(arg);
64221
+ return `floor(_gpu_random(float(${compiled})) * float(${compiled}))`;
64222
+ }
64223
+ return `_gpu_random(${compile3(arg)})`;
64224
+ }
64225
+ if (args.length === 2) {
64226
+ if (target.language === "wgsl") {
64227
+ throw new Error(
64228
+ "Random(m, n): WGSL compile requires explicit seeding. Use a seeded variant or compute the integer range manually."
64229
+ );
64230
+ }
64231
+ const m = compile3(args[0]);
64232
+ const n = compile3(args[1]);
64233
+ const seed = "_gpu_random(gl_FragCoord.x + gl_FragCoord.y * 1024.0)";
64234
+ return `(float(${m}) + floor(${seed} * float((${n}) - (${m}))))`;
64235
+ }
64236
+ throw new Error("Random: GPU compile expects 0, 1, or 2 arguments");
64237
+ },
62930
64238
  // Function (lambda) — not supported in GPU
62931
64239
  Function: () => {
62932
64240
  throw new Error(
@@ -63525,6 +64833,212 @@ fn _fractal_julia(z_in: vec2f, c: vec2f, maxIter: i32) -> f32 {
63525
64833
  }
63526
64834
  return 1.0;
63527
64835
  }
64836
+ `;
64837
+ var GPU_GCD_PREAMBLE_GLSL = `
64838
+ float _gpu_gcd(float a, float b) {
64839
+ a = abs(a); b = abs(b);
64840
+ for (int i = 0; i < 32; i++) {
64841
+ if (b < 0.5) break;
64842
+ float t = mod(a, b);
64843
+ a = b;
64844
+ b = t;
64845
+ }
64846
+ return a;
64847
+ }
64848
+ `;
64849
+ var GPU_GCD_PREAMBLE_WGSL = `
64850
+ fn _gpu_gcd(a_in: f32, b_in: f32) -> f32 {
64851
+ var a = abs(a_in); var b = abs(b_in);
64852
+ for (var i: i32 = 0; i < 32; i++) {
64853
+ if (b < 0.5) { break; }
64854
+ let t = a % b;
64855
+ a = b;
64856
+ b = t;
64857
+ }
64858
+ return a;
64859
+ }
64860
+ `;
64861
+ var GPU_RANDOM_PREAMBLE_GLSL = `
64862
+ // Deterministic pseudorandom in [0, 1) from a float seed.
64863
+ // Standard fract-sin hash; reproducible across runs for the same seed.
64864
+ // Note: this hash exhibits visible banding near seed \u2248 k\u03C0 for integer k.
64865
+ // For high-quality shader random, callers should use a more robust hash
64866
+ // (e.g. PCG or xxHash) and pre-seed it appropriately.
64867
+ float _gpu_random(float seed) {
64868
+ return fract(sin(seed * 12.9898) * 43758.5453);
64869
+ }
64870
+ `;
64871
+ var GPU_RANDOM_PREAMBLE_WGSL = `
64872
+ // Deterministic pseudorandom in [0, 1) from a float seed.
64873
+ // Standard fract-sin hash; reproducible across runs for the same seed.
64874
+ // Note: this hash exhibits visible banding near seed \u2248 k\u03C0 for integer k.
64875
+ // For high-quality shader random, callers should use a more robust hash
64876
+ // (e.g. PCG or xxHash) and pre-seed it appropriately.
64877
+ fn _gpu_random(seed: f32) -> f32 {
64878
+ return fract(sin(seed * 12.9898) * 43758.5453);
64879
+ }
64880
+ `;
64881
+ var GPU_MEDIAN_PREAMBLE_GLSL = `
64882
+ float _gpu_median_2(float a, float b) {
64883
+ return (a + b) * 0.5;
64884
+ }
64885
+ float _gpu_median_3(float a, float b, float c) {
64886
+ return max(min(a, b), min(max(a, b), c));
64887
+ }
64888
+ float _gpu_median_4(float a, float b, float c, float d) {
64889
+ float lo = max(min(a, b), min(c, d));
64890
+ float hi = min(max(a, b), max(c, d));
64891
+ return (lo + hi) * 0.5;
64892
+ }
64893
+ float _gpu_median_5(float a, float b, float c, float d, float e) {
64894
+ // 9-comparator Bose-Nelson sort; v2 holds the median.
64895
+ float t; float v0=a,v1=b,v2=c,v3=d,v4=e;
64896
+ t=min(v0,v1); v1=max(v0,v1); v0=t;
64897
+ t=min(v3,v4); v4=max(v3,v4); v3=t;
64898
+ t=min(v2,v4); v4=max(v2,v4); v2=t;
64899
+ t=min(v2,v3); v3=max(v2,v3); v2=t;
64900
+ t=min(v0,v3); v3=max(v0,v3); v0=t;
64901
+ t=min(v0,v2); v2=max(v0,v2); v0=t;
64902
+ t=min(v1,v4); v4=max(v1,v4); v1=t;
64903
+ t=min(v1,v3); v3=max(v1,v3); v1=t;
64904
+ t=min(v1,v2); v2=max(v1,v2); v1=t;
64905
+ return v2;
64906
+ }
64907
+ float _gpu_median_6(float a, float b, float c, float d, float e, float f) {
64908
+ float t; float v0=a,v1=b,v2=c,v3=d,v4=e,v5=f;
64909
+ t=min(v0,v1); v1=max(v0,v1); v0=t;
64910
+ t=min(v2,v3); v3=max(v2,v3); v2=t;
64911
+ t=min(v4,v5); v5=max(v4,v5); v4=t;
64912
+ t=min(v0,v2); v2=max(v0,v2); v0=t;
64913
+ t=min(v1,v3); v3=max(v1,v3); v1=t;
64914
+ t=min(v0,v4); v4=max(v0,v4); v0=t;
64915
+ t=min(v1,v5); v5=max(v1,v5); v1=t;
64916
+ t=min(v2,v4); v4=max(v2,v4); v2=t;
64917
+ t=min(v1,v2); v2=max(v1,v2); v1=t;
64918
+ t=min(v3,v5); v5=max(v3,v5); v3=t;
64919
+ t=min(v3,v4); v4=max(v3,v4); v3=t;
64920
+ return (v2 + v3) * 0.5;
64921
+ }
64922
+ float _gpu_median_7(float a, float b, float c, float d, float e, float f, float g) {
64923
+ float t; float v0=a,v1=b,v2=c,v3=d,v4=e,v5=f,v6=g;
64924
+ t=min(v0,v1); v1=max(v0,v1); v0=t;
64925
+ t=min(v2,v3); v3=max(v2,v3); v2=t;
64926
+ t=min(v4,v5); v5=max(v4,v5); v4=t;
64927
+ t=min(v0,v2); v2=max(v0,v2); v0=t;
64928
+ t=min(v1,v3); v3=max(v1,v3); v1=t;
64929
+ t=min(v4,v6); v6=max(v4,v6); v4=t;
64930
+ t=min(v0,v4); v4=max(v0,v4); v0=t;
64931
+ t=min(v1,v5); v5=max(v1,v5); v1=t;
64932
+ t=min(v2,v6); v6=max(v2,v6); v2=t;
64933
+ t=min(v1,v2); v2=max(v1,v2); v1=t;
64934
+ t=min(v3,v5); v5=max(v3,v5); v3=t;
64935
+ t=min(v2,v4); v4=max(v2,v4); v2=t;
64936
+ t=min(v3,v4); v4=max(v3,v4); v3=t;
64937
+ return v3;
64938
+ }
64939
+ float _gpu_median_8(float a, float b, float c, float d, float e, float f, float g, float h) {
64940
+ float t; float v0=a,v1=b,v2=c,v3=d,v4=e,v5=f,v6=g,v7=h;
64941
+ t=min(v0,v1); v1=max(v0,v1); v0=t;
64942
+ t=min(v2,v3); v3=max(v2,v3); v2=t;
64943
+ t=min(v4,v5); v5=max(v4,v5); v4=t;
64944
+ t=min(v6,v7); v7=max(v6,v7); v6=t;
64945
+ t=min(v0,v2); v2=max(v0,v2); v0=t;
64946
+ t=min(v1,v3); v3=max(v1,v3); v1=t;
64947
+ t=min(v4,v6); v6=max(v4,v6); v4=t;
64948
+ t=min(v5,v7); v7=max(v5,v7); v5=t;
64949
+ t=min(v0,v4); v4=max(v0,v4); v0=t;
64950
+ t=min(v1,v5); v5=max(v1,v5); v1=t;
64951
+ t=min(v2,v6); v6=max(v2,v6); v2=t;
64952
+ t=min(v3,v7); v7=max(v3,v7); v3=t;
64953
+ t=min(v1,v2); v2=max(v1,v2); v1=t;
64954
+ t=min(v3,v4); v4=max(v3,v4); v3=t;
64955
+ t=min(v5,v6); v6=max(v5,v6); v5=t;
64956
+ t=min(v3,v5); v5=max(v3,v5); v3=t;
64957
+ t=min(v2,v4); v4=max(v2,v4); v2=t;
64958
+ t=min(v3,v4); v4=max(v3,v4); v3=t;
64959
+ return (v3 + v4) * 0.5;
64960
+ }
64961
+ `;
64962
+ var GPU_MEDIAN_PREAMBLE_WGSL = `
64963
+ fn _gpu_median_2(a: f32, b: f32) -> f32 {
64964
+ return (a + b) * 0.5;
64965
+ }
64966
+ fn _gpu_median_3(a: f32, b: f32, c: f32) -> f32 {
64967
+ return max(min(a, b), min(max(a, b), c));
64968
+ }
64969
+ fn _gpu_median_4(a: f32, b: f32, c: f32, d: f32) -> f32 {
64970
+ let lo = max(min(a, b), min(c, d));
64971
+ let hi = min(max(a, b), max(c, d));
64972
+ return (lo + hi) * 0.5;
64973
+ }
64974
+ fn _gpu_median_5(a: f32, b: f32, c: f32, d: f32, e: f32) -> f32 {
64975
+ // 9-comparator Bose-Nelson sort; v2 holds the median.
64976
+ var v0=a; var v1=b; var v2=c; var v3=d; var v4=e; var t: f32;
64977
+ t=min(v0,v1); v1=max(v0,v1); v0=t;
64978
+ t=min(v3,v4); v4=max(v3,v4); v3=t;
64979
+ t=min(v2,v4); v4=max(v2,v4); v2=t;
64980
+ t=min(v2,v3); v3=max(v2,v3); v2=t;
64981
+ t=min(v0,v3); v3=max(v0,v3); v0=t;
64982
+ t=min(v0,v2); v2=max(v0,v2); v0=t;
64983
+ t=min(v1,v4); v4=max(v1,v4); v1=t;
64984
+ t=min(v1,v3); v3=max(v1,v3); v1=t;
64985
+ t=min(v1,v2); v2=max(v1,v2); v1=t;
64986
+ return v2;
64987
+ }
64988
+ fn _gpu_median_6(a: f32, b: f32, c: f32, d: f32, e: f32, f: f32) -> f32 {
64989
+ var v0=a; var v1=b; var v2=c; var v3=d; var v4=e; var v5=f; var t: f32;
64990
+ t=min(v0,v1); v1=max(v0,v1); v0=t;
64991
+ t=min(v2,v3); v3=max(v2,v3); v2=t;
64992
+ t=min(v4,v5); v5=max(v4,v5); v4=t;
64993
+ t=min(v0,v2); v2=max(v0,v2); v0=t;
64994
+ t=min(v1,v3); v3=max(v1,v3); v1=t;
64995
+ t=min(v0,v4); v4=max(v0,v4); v0=t;
64996
+ t=min(v1,v5); v5=max(v1,v5); v1=t;
64997
+ t=min(v2,v4); v4=max(v2,v4); v2=t;
64998
+ t=min(v1,v2); v2=max(v1,v2); v1=t;
64999
+ t=min(v3,v5); v5=max(v3,v5); v3=t;
65000
+ t=min(v3,v4); v4=max(v3,v4); v3=t;
65001
+ return (v2 + v3) * 0.5;
65002
+ }
65003
+ fn _gpu_median_7(a: f32, b: f32, c: f32, d: f32, e: f32, f: f32, g: f32) -> f32 {
65004
+ var v0=a; var v1=b; var v2=c; var v3=d; var v4=e; var v5=f; var v6=g; var t: f32;
65005
+ t=min(v0,v1); v1=max(v0,v1); v0=t;
65006
+ t=min(v2,v3); v3=max(v2,v3); v2=t;
65007
+ t=min(v4,v5); v5=max(v4,v5); v4=t;
65008
+ t=min(v0,v2); v2=max(v0,v2); v0=t;
65009
+ t=min(v1,v3); v3=max(v1,v3); v1=t;
65010
+ t=min(v4,v6); v6=max(v4,v6); v4=t;
65011
+ t=min(v0,v4); v4=max(v0,v4); v0=t;
65012
+ t=min(v1,v5); v5=max(v1,v5); v1=t;
65013
+ t=min(v2,v6); v6=max(v2,v6); v2=t;
65014
+ t=min(v1,v2); v2=max(v1,v2); v1=t;
65015
+ t=min(v3,v5); v5=max(v3,v5); v3=t;
65016
+ t=min(v2,v4); v4=max(v2,v4); v2=t;
65017
+ t=min(v3,v4); v4=max(v3,v4); v3=t;
65018
+ return v3;
65019
+ }
65020
+ fn _gpu_median_8(a: f32, b: f32, c: f32, d: f32, e: f32, f: f32, g: f32, h: f32) -> f32 {
65021
+ 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;
65022
+ t=min(v0,v1); v1=max(v0,v1); v0=t;
65023
+ t=min(v2,v3); v3=max(v2,v3); v2=t;
65024
+ t=min(v4,v5); v5=max(v4,v5); v4=t;
65025
+ t=min(v6,v7); v7=max(v6,v7); v6=t;
65026
+ t=min(v0,v2); v2=max(v0,v2); v0=t;
65027
+ t=min(v1,v3); v3=max(v1,v3); v1=t;
65028
+ t=min(v4,v6); v6=max(v4,v6); v4=t;
65029
+ t=min(v5,v7); v7=max(v5,v7); v5=t;
65030
+ t=min(v0,v4); v4=max(v0,v4); v0=t;
65031
+ t=min(v1,v5); v5=max(v1,v5); v1=t;
65032
+ t=min(v2,v6); v6=max(v2,v6); v2=t;
65033
+ t=min(v3,v7); v7=max(v3,v7); v3=t;
65034
+ t=min(v1,v2); v2=max(v1,v2); v1=t;
65035
+ t=min(v3,v4); v4=max(v3,v4); v3=t;
65036
+ t=min(v5,v6); v6=max(v5,v6); v5=t;
65037
+ t=min(v3,v5); v5=max(v3,v5); v3=t;
65038
+ t=min(v2,v4); v4=max(v2,v4); v2=t;
65039
+ t=min(v3,v4); v4=max(v3,v4); v3=t;
65040
+ return (v3 + v4) * 0.5;
65041
+ }
63528
65042
  `;
63529
65043
  var GPU_COLOR_PREAMBLE_GLSL = `
63530
65044
  float _gpu_srgb_to_linear(float c) {
@@ -64203,6 +65717,12 @@ fn _gpu_apca(lch_bg: vec3f, lch_fg: vec3f) -> f32 {
64203
65717
  if (code.includes("_fractal_")) {
64204
65718
  preamble += this.languageId === "wgsl" ? GPU_FRACTAL_PREAMBLE_WGSL : GPU_FRACTAL_PREAMBLE_GLSL;
64205
65719
  }
65720
+ if (code.includes("_gpu_random"))
65721
+ preamble += this.languageId === "wgsl" ? GPU_RANDOM_PREAMBLE_WGSL : GPU_RANDOM_PREAMBLE_GLSL;
65722
+ if (code.includes("_gpu_gcd"))
65723
+ preamble += this.languageId === "wgsl" ? GPU_GCD_PREAMBLE_WGSL : GPU_GCD_PREAMBLE_GLSL;
65724
+ if (code.includes("_gpu_median_"))
65725
+ preamble += this.languageId === "wgsl" ? GPU_MEDIAN_PREAMBLE_WGSL : GPU_MEDIAN_PREAMBLE_GLSL;
64206
65726
  if (code.includes("_gpu_srgb_to") || code.includes("_gpu_oklab") || code.includes("_gpu_oklch") || code.includes("_gpu_color_mix") || code.includes("_gpu_apca")) {
64207
65727
  preamble += this.languageId === "wgsl" ? GPU_COLOR_PREAMBLE_WGSL : GPU_COLOR_PREAMBLE_GLSL;
64208
65728
  }
@@ -66428,6 +67948,7 @@ ${workgroupAttr}fn main(${paramStr})${returnStr} {
66428
67948
  _timeLimit = 2e3;
66429
67949
  _iterationLimit = 1024;
66430
67950
  _recursionLimit = 1024;
67951
+ _maxCollectionSize = 1e4;
66431
67952
  _deadline = void 0;
66432
67953
  _isVerifying = false;
66433
67954
  get timeLimit() {
@@ -66448,6 +67969,12 @@ ${workgroupAttr}fn main(${paramStr})${returnStr} {
66448
67969
  set recursionLimit(value) {
66449
67970
  this._recursionLimit = value <= 0 ? Number.POSITIVE_INFINITY : value;
66450
67971
  }
67972
+ get maxCollectionSize() {
67973
+ return this._maxCollectionSize;
67974
+ }
67975
+ set maxCollectionSize(value) {
67976
+ this._maxCollectionSize = value <= 0 ? Number.POSITIVE_INFINITY : value;
67977
+ }
66451
67978
  get deadline() {
66452
67979
  return this._deadline;
66453
67980
  }
@@ -68908,6 +70435,23 @@ ${code}`;
68908
70435
  set recursionLimit(t) {
68909
70436
  this._runtimeState.recursionLimit = t;
68910
70437
  }
70438
+ /** Maximum number of elements a collection may have when materialized
70439
+ * (converted from a lazy form to a `List`). Default: 10,000.
70440
+ *
70441
+ * When a materialization would exceed this size, the operation leaves
70442
+ * the expression in its lazy form. Consumers can detect oversize
70443
+ * collections via the symbolic form's `count`.
70444
+ *
70445
+ * Set to `Infinity` (or `0` / a negative number) to disable the cap.
70446
+ *
70447
+ * @experimental
70448
+ */
70449
+ get maxCollectionSize() {
70450
+ return this._runtimeState.maxCollectionSize;
70451
+ }
70452
+ set maxCollectionSize(t) {
70453
+ this._runtimeState.maxCollectionSize = t;
70454
+ }
68911
70455
  /**
68912
70456
  * Flag to prevent infinite recursion in the verify/ask/equality checking cycle.
68913
70457
  *
@@ -69184,6 +70728,36 @@ ${code}`;
69184
70728
  lookupDefinition(id) {
69185
70729
  return lookupDefinition(this, id);
69186
70730
  }
70731
+ normalizeIdentifier(latex) {
70732
+ if (!latex) return "";
70733
+ if (isValidSymbol(latex)) return latex;
70734
+ this.pushScope();
70735
+ try {
70736
+ const expr2 = this.parse(latex);
70737
+ if (isSymbol2(expr2)) return expr2.symbol;
70738
+ } finally {
70739
+ this.popScope();
70740
+ }
70741
+ return "";
70742
+ }
70743
+ operatorInfo(head) {
70744
+ const def = this.lookupDefinition(head);
70745
+ if (!def || !isOperatorDef(def)) return void 0;
70746
+ const op = def.operator;
70747
+ return {
70748
+ kind: op.evaluate || op.collection ? "function" : "opaque",
70749
+ signature: op.signature
70750
+ };
70751
+ }
70752
+ symbolInfo(name) {
70753
+ const def = this.lookupDefinition(name);
70754
+ if (!def || !isValueDef(def)) return void 0;
70755
+ const v = def.value;
70756
+ return {
70757
+ kind: v.isConstant ? "constant" : "variable",
70758
+ type: v.type
70759
+ };
70760
+ }
69187
70761
  /**
69188
70762
  * Associate a new definition to a symbol in the current context.
69189
70763
  *
@@ -69471,6 +71045,7 @@ ${code}`;
69471
71045
  const def = this.lookupDefinition(id);
69472
71046
  return !!(isValueDef(def) && def.value.subscriptEvaluate);
69473
71047
  },
71048
+ tolerance: this.tolerance,
69474
71049
  ...this._latexOptions,
69475
71050
  ...parseOpts
69476
71051
  });
@@ -69635,14 +71210,14 @@ ${code}`;
69635
71210
  _setDefaultEngineFactory(() => new ComputeEngine());
69636
71211
 
69637
71212
  // src/compute-engine.ts
69638
- var version = "0.56.0";
71213
+ var version = "0.58.0";
69639
71214
  ComputeEngine._latexSyntaxFactory = () => new LatexSyntax();
69640
71215
  _setDefaultEngineFactory(
69641
71216
  () => new ComputeEngine({ latexSyntax: new LatexSyntax() })
69642
71217
  );
69643
71218
  globalThis[/* @__PURE__ */ Symbol.for("io.cortexjs.compute-engine")] = {
69644
71219
  ComputeEngine: ComputeEngine.prototype.constructor,
69645
- version: "0.56.0"
71220
+ version: "0.58.0"
69646
71221
  };
69647
71222
  return __toCommonJS(compute_engine_exports);
69648
71223
  })();