@cortex-js/compute-engine 0.56.0 → 0.58.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (259) hide show
  1. package/dist/compile.esm.js +1137 -92
  2. package/dist/compile.min.esm.js +261 -61
  3. package/dist/compile.min.umd.cjs +262 -62
  4. package/dist/compile.umd.cjs +1137 -92
  5. package/dist/compute-engine.esm.js +1751 -176
  6. package/dist/compute-engine.min.esm.js +273 -73
  7. package/dist/compute-engine.min.umd.cjs +272 -72
  8. package/dist/compute-engine.umd.cjs +1751 -176
  9. package/dist/core.esm.js +1750 -175
  10. package/dist/core.min.esm.js +271 -71
  11. package/dist/core.min.umd.cjs +271 -71
  12. package/dist/core.umd.cjs +1750 -175
  13. package/dist/interval.esm.js +357 -28
  14. package/dist/interval.min.esm.js +6 -6
  15. package/dist/interval.min.umd.cjs +6 -6
  16. package/dist/interval.umd.cjs +357 -28
  17. package/dist/latex-syntax.esm.js +398 -28
  18. package/dist/latex-syntax.min.esm.js +6 -6
  19. package/dist/latex-syntax.min.umd.cjs +6 -6
  20. package/dist/latex-syntax.umd.cjs +398 -28
  21. package/dist/math-json.esm.js +2 -2
  22. package/dist/math-json.min.esm.js +2 -2
  23. package/dist/math-json.min.umd.cjs +2 -2
  24. package/dist/math-json.umd.cjs +2 -2
  25. package/dist/numerics.esm.js +38 -3
  26. package/dist/numerics.min.esm.js +3 -3
  27. package/dist/numerics.min.umd.cjs +4 -4
  28. package/dist/numerics.umd.cjs +38 -3
  29. package/dist/types/big-decimal/big-decimal.d.ts +1 -1
  30. package/dist/types/big-decimal/index.d.ts +1 -1
  31. package/dist/types/big-decimal/transcendentals.d.ts +1 -1
  32. package/dist/types/big-decimal/utils.d.ts +1 -1
  33. package/dist/types/common/ansi-codes.d.ts +1 -1
  34. package/dist/types/common/configuration-change.d.ts +1 -1
  35. package/dist/types/common/fuzzy-string-match.d.ts +1 -1
  36. package/dist/types/common/grapheme-splitter.d.ts +1 -1
  37. package/dist/types/common/interruptible.d.ts +1 -1
  38. package/dist/types/common/one-of.d.ts +1 -1
  39. package/dist/types/common/signals.d.ts +1 -1
  40. package/dist/types/common/type/ast-nodes.d.ts +1 -1
  41. package/dist/types/common/type/boxed-type.d.ts +1 -1
  42. package/dist/types/common/type/lexer.d.ts +1 -1
  43. package/dist/types/common/type/parse.d.ts +1 -1
  44. package/dist/types/common/type/parser.d.ts +1 -1
  45. package/dist/types/common/type/primitive.d.ts +1 -1
  46. package/dist/types/common/type/reduce.d.ts +1 -1
  47. package/dist/types/common/type/serialize.d.ts +1 -1
  48. package/dist/types/common/type/subtype.d.ts +1 -1
  49. package/dist/types/common/type/type-builder.d.ts +1 -1
  50. package/dist/types/common/type/types.d.ts +1 -1
  51. package/dist/types/common/type/utils.d.ts +1 -1
  52. package/dist/types/common/utils.d.ts +1 -1
  53. package/dist/types/compile.d.ts +1 -1
  54. package/dist/types/compute-engine/assume.d.ts +1 -1
  55. package/dist/types/compute-engine/boxed-expression/abstract-boxed-expression.d.ts +8 -1
  56. package/dist/types/compute-engine/boxed-expression/apply.d.ts +1 -1
  57. package/dist/types/compute-engine/boxed-expression/arithmetic-add.d.ts +1 -1
  58. package/dist/types/compute-engine/boxed-expression/arithmetic-mul-div.d.ts +1 -1
  59. package/dist/types/compute-engine/boxed-expression/arithmetic-power.d.ts +1 -1
  60. package/dist/types/compute-engine/boxed-expression/ascii-math.d.ts +1 -1
  61. package/dist/types/compute-engine/boxed-expression/box.d.ts +1 -1
  62. package/dist/types/compute-engine/boxed-expression/boxed-dictionary.d.ts +1 -1
  63. package/dist/types/compute-engine/boxed-expression/boxed-function.d.ts +1 -1
  64. package/dist/types/compute-engine/boxed-expression/boxed-number.d.ts +1 -1
  65. package/dist/types/compute-engine/boxed-expression/boxed-operator-definition.d.ts +7 -1
  66. package/dist/types/compute-engine/boxed-expression/boxed-patterns.d.ts +1 -1
  67. package/dist/types/compute-engine/boxed-expression/boxed-string.d.ts +1 -1
  68. package/dist/types/compute-engine/boxed-expression/boxed-symbol.d.ts +1 -1
  69. package/dist/types/compute-engine/boxed-expression/boxed-tensor.d.ts +1 -1
  70. package/dist/types/compute-engine/boxed-expression/boxed-value-definition.d.ts +1 -1
  71. package/dist/types/compute-engine/boxed-expression/cache.d.ts +1 -1
  72. package/dist/types/compute-engine/boxed-expression/canonical-utils.d.ts +1 -1
  73. package/dist/types/compute-engine/boxed-expression/canonical.d.ts +1 -1
  74. package/dist/types/compute-engine/boxed-expression/compare.d.ts +1 -1
  75. package/dist/types/compute-engine/boxed-expression/constants.d.ts +1 -1
  76. package/dist/types/compute-engine/boxed-expression/expand.d.ts +1 -1
  77. package/dist/types/compute-engine/boxed-expression/expression-map.d.ts +1 -1
  78. package/dist/types/compute-engine/boxed-expression/factor.d.ts +1 -1
  79. package/dist/types/compute-engine/boxed-expression/flatten.d.ts +1 -1
  80. package/dist/types/compute-engine/boxed-expression/hold.d.ts +1 -1
  81. package/dist/types/compute-engine/boxed-expression/inequality-bounds.d.ts +22 -10
  82. package/dist/types/compute-engine/boxed-expression/init-lazy-refs.d.ts +1 -1
  83. package/dist/types/compute-engine/boxed-expression/invisible-operator.d.ts +1 -1
  84. package/dist/types/compute-engine/boxed-expression/match.d.ts +1 -1
  85. package/dist/types/compute-engine/boxed-expression/negate.d.ts +1 -1
  86. package/dist/types/compute-engine/boxed-expression/numerics.d.ts +1 -1
  87. package/dist/types/compute-engine/boxed-expression/order.d.ts +1 -1
  88. package/dist/types/compute-engine/boxed-expression/pattern-utils.d.ts +1 -1
  89. package/dist/types/compute-engine/boxed-expression/polynomial-degree.d.ts +1 -1
  90. package/dist/types/compute-engine/boxed-expression/polynomials.d.ts +1 -1
  91. package/dist/types/compute-engine/boxed-expression/predicates.d.ts +1 -1
  92. package/dist/types/compute-engine/boxed-expression/rules.d.ts +1 -1
  93. package/dist/types/compute-engine/boxed-expression/serialize.d.ts +1 -1
  94. package/dist/types/compute-engine/boxed-expression/sgn.d.ts +1 -1
  95. package/dist/types/compute-engine/boxed-expression/simplify.d.ts +1 -1
  96. package/dist/types/compute-engine/boxed-expression/solve-linear-system.d.ts +1 -1
  97. package/dist/types/compute-engine/boxed-expression/solve.d.ts +1 -1
  98. package/dist/types/compute-engine/boxed-expression/stochastic-equal.d.ts +1 -1
  99. package/dist/types/compute-engine/boxed-expression/trigonometry.d.ts +1 -1
  100. package/dist/types/compute-engine/boxed-expression/type-guards.d.ts +1 -1
  101. package/dist/types/compute-engine/boxed-expression/utils.d.ts +1 -1
  102. package/dist/types/compute-engine/boxed-expression/validate.d.ts +1 -1
  103. package/dist/types/compute-engine/collection-utils.d.ts +1 -1
  104. package/dist/types/compute-engine/compilation/base-compiler.d.ts +62 -6
  105. package/dist/types/compute-engine/compilation/compile-expression.d.ts +1 -1
  106. package/dist/types/compute-engine/compilation/constant-folding.d.ts +1 -1
  107. package/dist/types/compute-engine/compilation/glsl-target.d.ts +1 -1
  108. package/dist/types/compute-engine/compilation/gpu-target.d.ts +44 -1
  109. package/dist/types/compute-engine/compilation/interval-javascript-target.d.ts +1 -1
  110. package/dist/types/compute-engine/compilation/javascript-target.d.ts +1 -1
  111. package/dist/types/compute-engine/compilation/python-target.d.ts +1 -1
  112. package/dist/types/compute-engine/compilation/types.d.ts +1 -1
  113. package/dist/types/compute-engine/compilation/wgsl-target.d.ts +1 -1
  114. package/dist/types/compute-engine/cost-function.d.ts +1 -1
  115. package/dist/types/compute-engine/engine-assumptions.d.ts +1 -1
  116. package/dist/types/compute-engine/engine-cache.d.ts +1 -1
  117. package/dist/types/compute-engine/engine-common-symbols.d.ts +1 -1
  118. package/dist/types/compute-engine/engine-compilation-targets.d.ts +1 -1
  119. package/dist/types/compute-engine/engine-configuration-lifecycle.d.ts +1 -1
  120. package/dist/types/compute-engine/engine-declarations.d.ts +1 -1
  121. package/dist/types/compute-engine/engine-expression-entrypoints.d.ts +1 -1
  122. package/dist/types/compute-engine/engine-extension-contracts.d.ts +1 -1
  123. package/dist/types/compute-engine/engine-library-bootstrap.d.ts +1 -1
  124. package/dist/types/compute-engine/engine-numeric-configuration.d.ts +1 -1
  125. package/dist/types/compute-engine/engine-runtime-state.d.ts +4 -1
  126. package/dist/types/compute-engine/engine-scope.d.ts +1 -1
  127. package/dist/types/compute-engine/engine-sequences.d.ts +1 -1
  128. package/dist/types/compute-engine/engine-simplification-rules.d.ts +1 -1
  129. package/dist/types/compute-engine/engine-startup-coordinator.d.ts +1 -1
  130. package/dist/types/compute-engine/engine-type-resolver.d.ts +1 -1
  131. package/dist/types/compute-engine/engine-validation-entrypoints.d.ts +1 -1
  132. package/dist/types/compute-engine/free-functions.d.ts +1 -1
  133. package/dist/types/compute-engine/function-utils.d.ts +1 -1
  134. package/dist/types/compute-engine/global-types.d.ts +1 -1
  135. package/dist/types/compute-engine/index.d.ts +18 -2
  136. package/dist/types/compute-engine/interval/arithmetic.d.ts +1 -1
  137. package/dist/types/compute-engine/interval/comparison.d.ts +1 -1
  138. package/dist/types/compute-engine/interval/elementary.d.ts +1 -1
  139. package/dist/types/compute-engine/interval/index.d.ts +1 -1
  140. package/dist/types/compute-engine/interval/trigonometric.d.ts +1 -1
  141. package/dist/types/compute-engine/interval/types.d.ts +1 -1
  142. package/dist/types/compute-engine/interval/util.d.ts +1 -1
  143. package/dist/types/compute-engine/latex-syntax/dictionary/default-dictionary.d.ts +1 -1
  144. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-algebra.d.ts +1 -1
  145. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-arithmetic.d.ts +1 -1
  146. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-calculus.d.ts +1 -1
  147. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-colors.d.ts +1 -1
  148. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-complex.d.ts +1 -1
  149. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-core.d.ts +1 -1
  150. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-linear-algebra.d.ts +1 -1
  151. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-logic.d.ts +1 -1
  152. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-other.d.ts +1 -1
  153. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-relational-operators.d.ts +1 -1
  154. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-sets.d.ts +1 -1
  155. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-statistics.d.ts +1 -1
  156. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-symbols.d.ts +1 -1
  157. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-trigonometry.d.ts +1 -1
  158. package/dist/types/compute-engine/latex-syntax/dictionary/definitions-units.d.ts +1 -1
  159. package/dist/types/compute-engine/latex-syntax/dictionary/definitions.d.ts +1 -1
  160. package/dist/types/compute-engine/latex-syntax/dictionary/indexed-types.d.ts +1 -1
  161. package/dist/types/compute-engine/latex-syntax/latex-syntax.d.ts +1 -1
  162. package/dist/types/compute-engine/latex-syntax/parse-number.d.ts +1 -1
  163. package/dist/types/compute-engine/latex-syntax/parse-symbol.d.ts +1 -1
  164. package/dist/types/compute-engine/latex-syntax/parse.d.ts +1 -1
  165. package/dist/types/compute-engine/latex-syntax/serialize-dms.d.ts +1 -1
  166. package/dist/types/compute-engine/latex-syntax/serialize-number.d.ts +1 -1
  167. package/dist/types/compute-engine/latex-syntax/serializer-style.d.ts +1 -1
  168. package/dist/types/compute-engine/latex-syntax/serializer.d.ts +1 -1
  169. package/dist/types/compute-engine/latex-syntax/tokenizer.d.ts +1 -1
  170. package/dist/types/compute-engine/latex-syntax/types.d.ts +40 -1
  171. package/dist/types/compute-engine/latex-syntax/utils.d.ts +1 -1
  172. package/dist/types/compute-engine/library/arithmetic.d.ts +1 -1
  173. package/dist/types/compute-engine/library/calculus.d.ts +1 -1
  174. package/dist/types/compute-engine/library/collections.d.ts +5 -3
  175. package/dist/types/compute-engine/library/colors.d.ts +1 -1
  176. package/dist/types/compute-engine/library/combinatorics.d.ts +1 -1
  177. package/dist/types/compute-engine/library/complex.d.ts +1 -1
  178. package/dist/types/compute-engine/library/control-structures.d.ts +1 -1
  179. package/dist/types/compute-engine/library/core.d.ts +1 -1
  180. package/dist/types/compute-engine/library/fractals.d.ts +1 -1
  181. package/dist/types/compute-engine/library/library.d.ts +1 -1
  182. package/dist/types/compute-engine/library/linear-algebra.d.ts +1 -1
  183. package/dist/types/compute-engine/library/logic-analysis.d.ts +1 -1
  184. package/dist/types/compute-engine/library/logic.d.ts +1 -1
  185. package/dist/types/compute-engine/library/number-theory.d.ts +1 -1
  186. package/dist/types/compute-engine/library/polynomials.d.ts +1 -1
  187. package/dist/types/compute-engine/library/quantity-arithmetic.d.ts +1 -1
  188. package/dist/types/compute-engine/library/random-expression.d.ts +1 -1
  189. package/dist/types/compute-engine/library/relational-operator.d.ts +1 -1
  190. package/dist/types/compute-engine/library/sets.d.ts +1 -1
  191. package/dist/types/compute-engine/library/statistics.d.ts +1 -1
  192. package/dist/types/compute-engine/library/trigonometry.d.ts +1 -1
  193. package/dist/types/compute-engine/library/type-handlers.d.ts +1 -1
  194. package/dist/types/compute-engine/library/unit-data.d.ts +1 -1
  195. package/dist/types/compute-engine/library/units.d.ts +1 -1
  196. package/dist/types/compute-engine/library/utils.d.ts +1 -1
  197. package/dist/types/compute-engine/numeric-value/big-numeric-value.d.ts +1 -1
  198. package/dist/types/compute-engine/numeric-value/exact-numeric-value.d.ts +1 -1
  199. package/dist/types/compute-engine/numeric-value/machine-numeric-value.d.ts +1 -1
  200. package/dist/types/compute-engine/numeric-value/types.d.ts +1 -1
  201. package/dist/types/compute-engine/numerics/bigint.d.ts +1 -1
  202. package/dist/types/compute-engine/numerics/expression.d.ts +1 -1
  203. package/dist/types/compute-engine/numerics/interval.d.ts +1 -1
  204. package/dist/types/compute-engine/numerics/linear-algebra.d.ts +1 -1
  205. package/dist/types/compute-engine/numerics/monte-carlo.d.ts +1 -1
  206. package/dist/types/compute-engine/numerics/numeric-bigint.d.ts +1 -1
  207. package/dist/types/compute-engine/numerics/numeric-bignum.d.ts +1 -1
  208. package/dist/types/compute-engine/numerics/numeric-complex.d.ts +1 -1
  209. package/dist/types/compute-engine/numerics/numeric.d.ts +1 -1
  210. package/dist/types/compute-engine/numerics/primes.d.ts +1 -1
  211. package/dist/types/compute-engine/numerics/random.d.ts +23 -0
  212. package/dist/types/compute-engine/numerics/rationals.d.ts +1 -1
  213. package/dist/types/compute-engine/numerics/richardson.d.ts +1 -1
  214. package/dist/types/compute-engine/numerics/special-functions.d.ts +1 -1
  215. package/dist/types/compute-engine/numerics/statistics.d.ts +1 -1
  216. package/dist/types/compute-engine/numerics/strings.d.ts +1 -1
  217. package/dist/types/compute-engine/numerics/types.d.ts +1 -1
  218. package/dist/types/compute-engine/numerics/unit-data.d.ts +1 -1
  219. package/dist/types/compute-engine/oeis.d.ts +1 -1
  220. package/dist/types/compute-engine/sequence.d.ts +1 -1
  221. package/dist/types/compute-engine/symbolic/antiderivative.d.ts +1 -1
  222. package/dist/types/compute-engine/symbolic/derivative.d.ts +1 -1
  223. package/dist/types/compute-engine/symbolic/distribute.d.ts +1 -1
  224. package/dist/types/compute-engine/symbolic/fu-cost.d.ts +1 -1
  225. package/dist/types/compute-engine/symbolic/fu-transforms.d.ts +1 -1
  226. package/dist/types/compute-engine/symbolic/fu.d.ts +1 -1
  227. package/dist/types/compute-engine/symbolic/logic-utils.d.ts +1 -1
  228. package/dist/types/compute-engine/symbolic/simplify-abs.d.ts +1 -1
  229. package/dist/types/compute-engine/symbolic/simplify-divide.d.ts +1 -1
  230. package/dist/types/compute-engine/symbolic/simplify-factorial.d.ts +1 -1
  231. package/dist/types/compute-engine/symbolic/simplify-hyperbolic.d.ts +1 -1
  232. package/dist/types/compute-engine/symbolic/simplify-infinity.d.ts +1 -1
  233. package/dist/types/compute-engine/symbolic/simplify-log.d.ts +1 -1
  234. package/dist/types/compute-engine/symbolic/simplify-logic.d.ts +1 -1
  235. package/dist/types/compute-engine/symbolic/simplify-power.d.ts +1 -1
  236. package/dist/types/compute-engine/symbolic/simplify-product.d.ts +1 -1
  237. package/dist/types/compute-engine/symbolic/simplify-rules.d.ts +1 -1
  238. package/dist/types/compute-engine/symbolic/simplify-sum.d.ts +1 -1
  239. package/dist/types/compute-engine/symbolic/simplify-trig.d.ts +1 -1
  240. package/dist/types/compute-engine/tensor/tensor-fields.d.ts +1 -1
  241. package/dist/types/compute-engine/tensor/tensors.d.ts +1 -1
  242. package/dist/types/compute-engine/types-definitions.d.ts +1 -1
  243. package/dist/types/compute-engine/types-engine.d.ts +51 -1
  244. package/dist/types/compute-engine/types-evaluation.d.ts +1 -1
  245. package/dist/types/compute-engine/types-expression.d.ts +69 -1
  246. package/dist/types/compute-engine/types-kernel-evaluation.d.ts +1 -1
  247. package/dist/types/compute-engine/types-kernel-serialization.d.ts +1 -1
  248. package/dist/types/compute-engine/types-serialization.d.ts +1 -1
  249. package/dist/types/compute-engine/types.d.ts +1 -1
  250. package/dist/types/compute-engine.d.ts +1 -1
  251. package/dist/types/core.d.ts +1 -1
  252. package/dist/types/interval.d.ts +1 -1
  253. package/dist/types/latex-syntax.d.ts +2 -2
  254. package/dist/types/math-json/symbols.d.ts +1 -1
  255. package/dist/types/math-json/types.d.ts +1 -1
  256. package/dist/types/math-json/utils.d.ts +1 -1
  257. package/dist/types/math-json.d.ts +2 -2
  258. package/dist/types/numerics.d.ts +1 -1
  259. package/package.json +1 -1
package/dist/core.umd.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /** ComputeEngineCore 0.56.0 */
1
+ /** ComputeEngineCore 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.ComputeEngineCore = {}));})(this, (function (exports) { 'use strict';
3
3
  var ComputeEngineCore = (() => {
4
4
  var __defProp = Object.defineProperty;
@@ -4315,7 +4315,42 @@ var ComputeEngineCore = (() => {
4315
4315
  if (b === "nothing") return a;
4316
4316
  if (isSubtype(a, b)) return b;
4317
4317
  if (isSubtype(b, a)) return a;
4318
- return superType(a, b);
4318
+ const sup = superType(a, b);
4319
+ if (LOSSY_SUPERTYPE.has(sup)) return unionTypes(a, b);
4320
+ return sup;
4321
+ }
4322
+ var LOSSY_SUPERTYPE = /* @__PURE__ */ new Set([
4323
+ "scalar",
4324
+ "value",
4325
+ "function",
4326
+ "expression",
4327
+ "collection",
4328
+ "indexed_collection",
4329
+ "list",
4330
+ "set",
4331
+ "tuple",
4332
+ "record",
4333
+ "dictionary",
4334
+ "map",
4335
+ "any"
4336
+ ]);
4337
+ function unionTypes(a, b) {
4338
+ const members = [];
4339
+ const push = (t) => {
4340
+ if (typeof t === "object" && t.kind === "union") {
4341
+ for (const m of t.types) push(m);
4342
+ return;
4343
+ }
4344
+ const key = typeof t === "string" ? t : JSON.stringify(t);
4345
+ if (!members.some(
4346
+ (m) => (typeof m === "string" ? m : JSON.stringify(m)) === key
4347
+ ))
4348
+ members.push(t);
4349
+ };
4350
+ push(a);
4351
+ push(b);
4352
+ if (members.length === 1) return members[0];
4353
+ return { kind: "union", types: members };
4319
4354
  }
4320
4355
  function narrow(...types) {
4321
4356
  if (types.length === 0) return "nothing";
@@ -4393,7 +4428,7 @@ var ComputeEngineCore = (() => {
4393
4428
  if (type2.kind === "set") return type2.elements;
4394
4429
  if (type2.kind === "tuple") return widen(...type2.elements.map((x) => x.type));
4395
4430
  if (type2.kind === "dictionary")
4396
- return parseType(`tuple<string, ${type2.values}>`);
4431
+ return parseType(`tuple<string, ${typeToString(type2.values)}>`);
4397
4432
  if (type2.kind === "record") {
4398
4433
  return parseType(
4399
4434
  `tuple<string, ${typeToString(widen(...Object.values(type2.elements)))}>`
@@ -5939,6 +5974,84 @@ var ComputeEngineCore = (() => {
5939
5974
  }
5940
5975
 
5941
5976
  // src/compute-engine/boxed-expression/inequality-bounds.ts
5977
+ function extractIntervalBounds(expr2, symbol2) {
5978
+ if (isFunction2(expr2, "When")) {
5979
+ const cond = expr2.op2;
5980
+ if (!cond) return void 0;
5981
+ return extractIntervalBounds(cond, symbol2);
5982
+ }
5983
+ if (isFunction2(expr2, "Multiply")) {
5984
+ const ops = expr2.ops;
5985
+ if (!ops) return void 0;
5986
+ const merged = {};
5987
+ for (const sub2 of ops) {
5988
+ if (isFunction2(sub2, "When")) {
5989
+ const sub_ = extractIntervalBounds(sub2, symbol2);
5990
+ if (sub_ !== void 0) _mergeBounds(merged, sub_);
5991
+ }
5992
+ }
5993
+ return _hasAnyBound(merged) ? merged : void 0;
5994
+ }
5995
+ const result = {};
5996
+ if (isFunction2(expr2, "And")) {
5997
+ const ops = expr2.ops;
5998
+ if (!ops) return void 0;
5999
+ for (const sub2 of ops) {
6000
+ const subBounds = extractIntervalBounds(sub2, symbol2);
6001
+ if (subBounds === void 0) continue;
6002
+ _mergeBounds(result, subBounds);
6003
+ }
6004
+ return _hasAnyBound(result) ? result : void 0;
6005
+ }
6006
+ const op = expr2.operator;
6007
+ if ((op === "Less" || op === "LessEqual" || op === "Greater" || op === "GreaterEqual") && isFunction2(expr2)) {
6008
+ const isStrict = op === "Less" || op === "Greater";
6009
+ const ops = expr2.ops;
6010
+ if (!ops || ops.length < 2) return void 0;
6011
+ const flipped = op === "Greater" || op === "GreaterEqual" ? [...ops].reverse() : ops;
6012
+ for (let i = 0; i < flipped.length; i++) {
6013
+ if (isSymbol2(flipped[i], symbol2)) {
6014
+ if (i > 0) {
6015
+ const candidate = flipped[i - 1];
6016
+ if (result.lower === void 0 || candidate.isGreater(result.lower) === true) {
6017
+ result.lower = candidate;
6018
+ result.lowerStrict = isStrict;
6019
+ }
6020
+ }
6021
+ if (i < flipped.length - 1) {
6022
+ const candidate = flipped[i + 1];
6023
+ if (result.upper === void 0 || candidate.isLess(result.upper) === true) {
6024
+ result.upper = candidate;
6025
+ result.upperStrict = isStrict;
6026
+ }
6027
+ }
6028
+ }
6029
+ }
6030
+ return _hasAnyBound(result) ? result : void 0;
6031
+ }
6032
+ return void 0;
6033
+ }
6034
+ function _mergeBounds(into, from) {
6035
+ if (from.lower !== void 0) {
6036
+ if (into.lower === void 0 || from.lower.isGreater(into.lower) === true) {
6037
+ into.lower = from.lower;
6038
+ into.lowerStrict = from.lowerStrict;
6039
+ } else if (from.lower.isSame(into.lower)) {
6040
+ into.lowerStrict = into.lowerStrict || from.lowerStrict;
6041
+ }
6042
+ }
6043
+ if (from.upper !== void 0) {
6044
+ if (into.upper === void 0 || from.upper.isLess(into.upper) === true) {
6045
+ into.upper = from.upper;
6046
+ into.upperStrict = from.upperStrict;
6047
+ } else if (from.upper.isSame(into.upper)) {
6048
+ into.upperStrict = into.upperStrict || from.upperStrict;
6049
+ }
6050
+ }
6051
+ }
6052
+ function _hasAnyBound(b) {
6053
+ return b.lower !== void 0 || b.upper !== void 0;
6054
+ }
5942
6055
  function getInequalityBoundsFromAssumptions(ce, symbol2) {
5943
6056
  const result = {};
5944
6057
  const assumptions = ce.context?.assumptions;
@@ -5955,8 +6068,8 @@ var ComputeEngineCore = (() => {
5955
6068
  const isStrict = op === "Less";
5956
6069
  if (isFunction2(lhs, "Negate") && isSymbol2(lhs.op1, symbol2)) {
5957
6070
  const bound = ce.Zero;
5958
- if (result.lowerBound === void 0 || bound.isGreater(result.lowerBound) === true) {
5959
- result.lowerBound = bound;
6071
+ if (result.lower === void 0 || bound.isGreater(result.lower) === true) {
6072
+ result.lower = bound;
5960
6073
  result.lowerStrict = isStrict;
5961
6074
  }
5962
6075
  }
@@ -5975,16 +6088,16 @@ var ComputeEngineCore = (() => {
5975
6088
  }
5976
6089
  if (hasNegatedSymbol && constantSum !== 0) {
5977
6090
  const bound = ce.expr(constantSum);
5978
- if (result.lowerBound === void 0 || bound.isGreater(result.lowerBound) === true) {
5979
- result.lowerBound = bound;
6091
+ if (result.lower === void 0 || bound.isGreater(result.lower) === true) {
6092
+ result.lower = bound;
5980
6093
  result.lowerStrict = isStrict;
5981
6094
  }
5982
6095
  }
5983
6096
  }
5984
6097
  if (isSymbol2(lhs, symbol2)) {
5985
6098
  const bound = ce.Zero;
5986
- if (result.upperBound === void 0 || bound.isLess(result.upperBound) === true) {
5987
- result.upperBound = bound;
6099
+ if (result.upper === void 0 || bound.isLess(result.upper) === true) {
6100
+ result.upper = bound;
5988
6101
  result.upperStrict = isStrict;
5989
6102
  }
5990
6103
  }
@@ -6003,8 +6116,8 @@ var ComputeEngineCore = (() => {
6003
6116
  }
6004
6117
  if (hasSymbol && constantSum !== 0) {
6005
6118
  const bound = ce.expr(-constantSum);
6006
- if (result.upperBound === void 0 || bound.isLess(result.upperBound) === true) {
6007
- result.upperBound = bound;
6119
+ if (result.upper === void 0 || bound.isLess(result.upper) === true) {
6120
+ result.upper = bound;
6008
6121
  result.upperStrict = isStrict;
6009
6122
  }
6010
6123
  }
@@ -6224,8 +6337,8 @@ var ComputeEngineCore = (() => {
6224
6337
  const bounds = getInequalityBoundsFromAssumptions(a.engine, b.symbol);
6225
6338
  const aNum = typeof a.numericValue === "number" ? a.numericValue : a.numericValue.re;
6226
6339
  if (aNum !== void 0 && Number.isFinite(aNum)) {
6227
- if (bounds.lowerBound !== void 0) {
6228
- const lb = bounds.lowerBound;
6340
+ if (bounds.lower !== void 0) {
6341
+ const lb = bounds.lower;
6229
6342
  const lowerNum = isNumber(lb) ? typeof lb.numericValue === "number" ? lb.numericValue : lb.numericValue.re : void 0;
6230
6343
  if (lowerNum !== void 0 && Number.isFinite(lowerNum)) {
6231
6344
  if (lowerNum > aNum) return "<";
@@ -6233,8 +6346,8 @@ var ComputeEngineCore = (() => {
6233
6346
  if (lowerNum === aNum && !bounds.lowerStrict) return "<=";
6234
6347
  }
6235
6348
  }
6236
- if (bounds.upperBound !== void 0) {
6237
- const ub = bounds.upperBound;
6349
+ if (bounds.upper !== void 0) {
6350
+ const ub = bounds.upper;
6238
6351
  const upperNum = isNumber(ub) ? typeof ub.numericValue === "number" ? ub.numericValue : ub.numericValue.re : void 0;
6239
6352
  if (upperNum !== void 0 && Number.isFinite(upperNum)) {
6240
6353
  if (upperNum < aNum) return ">";
@@ -6264,8 +6377,8 @@ var ComputeEngineCore = (() => {
6264
6377
  if (typeof b === "number") {
6265
6378
  if (isSymbol2(a)) {
6266
6379
  const bounds = getInequalityBoundsFromAssumptions(a.engine, a.symbol);
6267
- if (bounds.lowerBound !== void 0) {
6268
- const lb = bounds.lowerBound;
6380
+ if (bounds.lower !== void 0) {
6381
+ const lb = bounds.lower;
6269
6382
  const lowerNum = isNumber(lb) ? typeof lb.numericValue === "number" ? lb.numericValue : lb.numericValue.re : void 0;
6270
6383
  if (lowerNum !== void 0 && Number.isFinite(lowerNum)) {
6271
6384
  if (lowerNum > b) return ">";
@@ -6273,8 +6386,8 @@ var ComputeEngineCore = (() => {
6273
6386
  if (lowerNum === b && !bounds.lowerStrict) return ">=";
6274
6387
  }
6275
6388
  }
6276
- if (bounds.upperBound !== void 0) {
6277
- const ub = bounds.upperBound;
6389
+ if (bounds.upper !== void 0) {
6390
+ const ub = bounds.upper;
6278
6391
  const upperNum = isNumber(ub) ? typeof ub.numericValue === "number" ? ub.numericValue : ub.numericValue.re : void 0;
6279
6392
  if (upperNum !== void 0 && Number.isFinite(upperNum)) {
6280
6393
  if (upperNum < b) return "<";
@@ -6330,8 +6443,8 @@ var ComputeEngineCore = (() => {
6330
6443
  const bounds = getInequalityBoundsFromAssumptions(a.engine, a.symbol);
6331
6444
  const bNum = typeof b.numericValue === "number" ? b.numericValue : b.numericValue.re;
6332
6445
  if (bNum !== void 0 && Number.isFinite(bNum)) {
6333
- if (bounds.lowerBound !== void 0) {
6334
- const lb = bounds.lowerBound;
6446
+ if (bounds.lower !== void 0) {
6447
+ const lb = bounds.lower;
6335
6448
  const lowerNum = isNumber(lb) ? typeof lb.numericValue === "number" ? lb.numericValue : lb.numericValue.re : void 0;
6336
6449
  if (lowerNum !== void 0 && Number.isFinite(lowerNum)) {
6337
6450
  if (lowerNum > bNum) return ">";
@@ -6339,8 +6452,8 @@ var ComputeEngineCore = (() => {
6339
6452
  if (lowerNum === bNum && !bounds.lowerStrict) return ">=";
6340
6453
  }
6341
6454
  }
6342
- if (bounds.upperBound !== void 0) {
6343
- const ub = bounds.upperBound;
6455
+ if (bounds.upper !== void 0) {
6456
+ const ub = bounds.upper;
6344
6457
  const upperNum = isNumber(ub) ? typeof ub.numericValue === "number" ? ub.numericValue : ub.numericValue.re : void 0;
6345
6458
  if (upperNum !== void 0 && Number.isFinite(upperNum)) {
6346
6459
  if (upperNum < bNum) return "<";
@@ -6878,6 +6991,12 @@ var ComputeEngineCore = (() => {
6878
6991
  scoped = false;
6879
6992
  signature;
6880
6993
  inferredSignature = true;
6994
+ /** True if this operator definition was created from a user-defined
6995
+ * function literal (e.g. via `ce.assign('f', ce.parse('x \\mapsto x^2'))`).
6996
+ * Used to enable auto-broadcasting when applied to indexed collections.
6997
+ * @internal
6998
+ */
6999
+ _isLambda = false;
6881
7000
  type;
6882
7001
  sgn;
6883
7002
  eq;
@@ -7035,6 +7154,8 @@ var ComputeEngineCore = (() => {
7035
7154
  this.engine._typeResolver
7036
7155
  );
7037
7156
  }
7157
+ if (isFunction2(boxedFn) && boxedFn.operator === "Function")
7158
+ this._isLambda = true;
7038
7159
  const fn = applicable(boxedFn);
7039
7160
  evaluate2 = (xs, _options) => fn(xs);
7040
7161
  Object.defineProperty(evaluate2, "toString", {
@@ -7885,8 +8006,15 @@ var ComputeEngineCore = (() => {
7885
8006
  *
7886
8007
  * Numeric values are rounded to `ce.precision` significant digits
7887
8008
  * (via `fractionalDigits: 'auto'`).
8009
+ *
8010
+ * If `options.verbatim` is `true` and `verbatimLatex` is set on this
8011
+ * expression (i.e. it was parsed with `preserveLatex: true`), return
8012
+ * the verbatim source instead of re-serializing. Falls through to
8013
+ * re-serialization if no verbatim is available.
7888
8014
  */
7889
8015
  toLatex(options) {
8016
+ if (options?.verbatim === true && this.verbatimLatex !== void 0)
8017
+ return this.verbatimLatex;
7890
8018
  if (this.isLazyCollection) {
7891
8019
  const materialized = this.evaluate({
7892
8020
  materialization: options?.materialization ?? true
@@ -8344,6 +8472,29 @@ var ComputeEngineCore = (() => {
8344
8472
  get isReal() {
8345
8473
  return void 0;
8346
8474
  }
8475
+ toSignedFunction() {
8476
+ const op = this.operator;
8477
+ if (op === void 0 || this.ops === void 0 || this.ops.length < 2) {
8478
+ return void 0;
8479
+ }
8480
+ const [lhs, rhs] = this.ops;
8481
+ const engine = this.engine;
8482
+ switch (op) {
8483
+ case "Equal":
8484
+ case "NotEqual":
8485
+ case "Less":
8486
+ case "LessEqual":
8487
+ return engine.function("Subtract", [lhs, rhs]);
8488
+ case "Greater":
8489
+ case "GreaterEqual":
8490
+ return engine.function("Subtract", [rhs, lhs]);
8491
+ default:
8492
+ return void 0;
8493
+ }
8494
+ }
8495
+ getInterval(symbol2) {
8496
+ return extractIntervalBounds(this, symbol2);
8497
+ }
8347
8498
  simplify(_options) {
8348
8499
  return this;
8349
8500
  }
@@ -10029,6 +10180,64 @@ var ComputeEngineCore = (() => {
10029
10180
  }
10030
10181
 
10031
10182
  // src/compute-engine/latex-syntax/dictionary/definitions-core.ts
10183
+ var COMPONENT_ACCESS_HEADS = {
10184
+ x: "First",
10185
+ y: "Second",
10186
+ z: "Third",
10187
+ real: "Real",
10188
+ re: "Real",
10189
+ imag: "Imaginary",
10190
+ im: "Imaginary",
10191
+ count: "Length",
10192
+ total: "Sum",
10193
+ max: "Max",
10194
+ min: "Min"
10195
+ };
10196
+ function memberHead(name) {
10197
+ return COMPONENT_ACCESS_HEADS[name] ?? null;
10198
+ }
10199
+ function parseComponentAccess(parser, lhs) {
10200
+ parser.skipVisualSpace();
10201
+ if (parser.match("\\operatorname")) {
10202
+ const name = parser.parseStringGroup();
10203
+ if (name === null) return null;
10204
+ const head = memberHead(name.trim());
10205
+ if (head === null) return null;
10206
+ return [head, lhs];
10207
+ }
10208
+ const tok = parser.peek;
10209
+ if (typeof tok === "string" && tok.startsWith("\\")) {
10210
+ const bare = tok.slice(1);
10211
+ const head = memberHead(bare);
10212
+ if (head !== null) {
10213
+ parser.nextToken();
10214
+ return [head, lhs];
10215
+ }
10216
+ return null;
10217
+ }
10218
+ if (typeof tok === "string" && /^[a-zA-Z]$/.test(tok)) {
10219
+ const head = memberHead(tok);
10220
+ if (head === null) return null;
10221
+ parser.nextToken();
10222
+ return [head, lhs];
10223
+ }
10224
+ return null;
10225
+ }
10226
+ function parseWhenRestriction(parser, lhs, close) {
10227
+ parser.addBoundary(close);
10228
+ parser.skipVisualSpace();
10229
+ const cond = parser.parseExpression({ minPrec: 0 });
10230
+ if (cond === null) {
10231
+ parser.removeBoundary();
10232
+ return null;
10233
+ }
10234
+ parser.skipVisualSpace();
10235
+ if (!parser.matchBoundary()) {
10236
+ parser.removeBoundary();
10237
+ return null;
10238
+ }
10239
+ return ["When", lhs, cond];
10240
+ }
10032
10241
  function parseSequence(parser, terminator, lhs, prec, sep) {
10033
10242
  if (terminator && terminator.minPrec >= prec) return null;
10034
10243
  const result = lhs ? [lhs] : ["Nothing"];
@@ -10304,15 +10513,16 @@ var ComputeEngineCore = (() => {
10304
10513
  precedence: ASSIGNMENT_PRECEDENCE,
10305
10514
  parse: parseAssign
10306
10515
  },
10307
- // General colon operator (type annotation, mapping notation)
10308
- // Precedence below assignment (260) so `:=` takes priority,
10309
- // and below arrows (270) so `f: A \to B` parses as `Colon(f, To(A, B))`
10516
+ // General colon operator (type annotation, mapping notation, Desmos piecewise)
10517
+ // Precedence below comparisons (245) so `cond : val` (Desmos compact piecewise)
10518
+ // parses as `Colon(cond, val)`, and below arrows (270) so
10519
+ // `f: A \to B` parses as `Colon(f, To(A, B))`.
10310
10520
  {
10311
10521
  name: "Colon",
10312
10522
  latexTrigger: ":",
10313
10523
  kind: "infix",
10314
10524
  associativity: "right",
10315
- precedence: 250,
10525
+ precedence: 240,
10316
10526
  serialize: (serializer, expr2) => joinLatex([
10317
10527
  serializer.serialize(operand(expr2, 1)),
10318
10528
  "\\colon",
@@ -10323,7 +10533,7 @@ var ComputeEngineCore = (() => {
10323
10533
  latexTrigger: "\\colon",
10324
10534
  kind: "infix",
10325
10535
  associativity: "right",
10326
- precedence: 250,
10536
+ precedence: 240,
10327
10537
  parse: "Colon"
10328
10538
  },
10329
10539
  {
@@ -10500,6 +10710,15 @@ var ComputeEngineCore = (() => {
10500
10710
  }
10501
10711
  },
10502
10712
  { name: "LatexTokens", serialize: serializeLatexTokens },
10713
+ // Component-access postfix: expr.member (C3)
10714
+ // The '.' trigger is consumed before the parse function is called.
10715
+ // Precedence 850 > 810 (At/indexing) so .x chains tightly.
10716
+ {
10717
+ kind: "postfix",
10718
+ precedence: 850,
10719
+ latexTrigger: ["."],
10720
+ parse: parseComponentAccess
10721
+ },
10503
10722
  {
10504
10723
  name: "At",
10505
10724
  kind: "postfix",
@@ -10520,6 +10739,29 @@ var ComputeEngineCore = (() => {
10520
10739
  latexTrigger: ["\\left", "\\lbrack"],
10521
10740
  parse: parseAt("\\right", "\\rbrack")
10522
10741
  },
10742
+ // When-restriction: `expr\left\{cond\right\}` → `When(expr, cond)` (D3)
10743
+ {
10744
+ name: "When",
10745
+ kind: "postfix",
10746
+ precedence: 800,
10747
+ latexTrigger: ["\\left", "\\{"],
10748
+ parse: (parser, lhs) => parseWhenRestriction(parser, lhs, ["\\right", "\\}"]),
10749
+ serialize: (serializer, expr2) => {
10750
+ const e = operand(expr2, 1);
10751
+ const cond = operand(expr2, 2);
10752
+ if (!e || !cond) return "";
10753
+ const clauses = operator(cond) === "And" ? operands(cond) ?? [] : [cond];
10754
+ const inner = clauses.map((c) => `\\left\\{${serializer.serialize(c)}\\right\\}`).join("");
10755
+ return `${serializer.serialize(e)}${inner}`;
10756
+ }
10757
+ },
10758
+ // When-restriction: bare `expr\{cond\}` → `When(expr, cond)`
10759
+ {
10760
+ kind: "postfix",
10761
+ precedence: 800,
10762
+ latexTrigger: ["\\{"],
10763
+ parse: (parser, lhs) => parseWhenRestriction(parser, lhs, ["\\}"])
10764
+ },
10523
10765
  {
10524
10766
  kind: "postfix",
10525
10767
  latexTrigger: ["_"],
@@ -10602,6 +10844,29 @@ var ComputeEngineCore = (() => {
10602
10844
  return "";
10603
10845
  }
10604
10846
  },
10847
+ // Additional triggers for Range: `...`, `\ldots`, and `\dots` are
10848
+ // equivalent to `..` when used as infix operators (e.g. `[1...9]`).
10849
+ // No `name` field here — names must be unique per the dictionary rules;
10850
+ // the first Range entry owns the name. When there is no LHS the symbol
10851
+ // entries near the top of the file still fire (ContinuationPlaceholder).
10852
+ {
10853
+ latexTrigger: [".", ".", "."],
10854
+ kind: "infix",
10855
+ precedence: 800,
10856
+ parse: parseRange
10857
+ },
10858
+ {
10859
+ latexTrigger: ["\\ldots"],
10860
+ kind: "infix",
10861
+ precedence: 800,
10862
+ parse: parseRange
10863
+ },
10864
+ {
10865
+ latexTrigger: ["\\dots"],
10866
+ kind: "infix",
10867
+ precedence: 800,
10868
+ parse: parseRange
10869
+ },
10605
10870
  {
10606
10871
  latexTrigger: [";"],
10607
10872
  kind: "infix",
@@ -10786,13 +11051,24 @@ var ComputeEngineCore = (() => {
10786
11051
  const args = operands(expr2);
10787
11052
  if (!args || args.length < 2) return "";
10788
11053
  const body = args[0];
10789
- const indexing = args[1];
10790
- if (operator(indexing) === "Element") {
10791
- const index = operand(indexing, 1);
10792
- const range2 = operand(indexing, 2);
10793
- if (operator(range2) === "Range") {
10794
- const lo = operand(range2, 1);
10795
- const hi = operand(range2, 2);
11054
+ const elements = args.slice(1);
11055
+ const allElements = elements.every((e) => operator(e) === "Element");
11056
+ if (!allElements) {
11057
+ return joinLatex([
11058
+ "\\operatorname{Loop}(",
11059
+ serializer.serialize(body),
11060
+ ", ",
11061
+ serializer.serialize(elements[0]),
11062
+ ")"
11063
+ ]);
11064
+ }
11065
+ if (elements.length === 1) {
11066
+ const elem = elements[0];
11067
+ const index = operand(elem, 1);
11068
+ const coll = operand(elem, 2);
11069
+ if (operator(coll) === "Range") {
11070
+ const lo = operand(coll, 1);
11071
+ const hi = operand(coll, 2);
10796
11072
  return joinLatex([
10797
11073
  "\\text{for }",
10798
11074
  serializer.serialize(index),
@@ -10804,13 +11080,27 @@ var ComputeEngineCore = (() => {
10804
11080
  serializer.serialize(body)
10805
11081
  ]);
10806
11082
  }
11083
+ return joinLatex([
11084
+ serializer.serialize(body),
11085
+ " \\operatorname{for} ",
11086
+ serializer.serialize(index),
11087
+ " = ",
11088
+ serializer.serialize(coll)
11089
+ ]);
10807
11090
  }
11091
+ const bindings = elements.map((elem) => {
11092
+ const name = operand(elem, 1);
11093
+ const coll = operand(elem, 2);
11094
+ return joinLatex([
11095
+ serializer.serialize(name),
11096
+ " = ",
11097
+ serializer.serialize(coll)
11098
+ ]);
11099
+ }).join(", ");
10808
11100
  return joinLatex([
10809
- "\\operatorname{Loop}(",
10810
11101
  serializer.serialize(body),
10811
- ", ",
10812
- serializer.serialize(indexing),
10813
- ")"
11102
+ " \\operatorname{for} ",
11103
+ bindings
10814
11104
  ]);
10815
11105
  }
10816
11106
  },
@@ -10843,6 +11133,18 @@ var ComputeEngineCore = (() => {
10843
11133
  precedence: 245,
10844
11134
  parse: (parser, until) => parseForExpression(parser, until)
10845
11135
  },
11136
+ // \operatorname{for} as postfix infix (list comprehension):
11137
+ // `body \operatorname{for} x = L_1, y = L_2`
11138
+ // Precedence 19 — just below comma (20) so the body is allowed to use
11139
+ // any operator (including comma sequencing) up to the keyword, and the
11140
+ // bindings can be comma-separated below us.
11141
+ {
11142
+ symbolTrigger: "for",
11143
+ kind: "infix",
11144
+ associativity: "none",
11145
+ precedence: 19,
11146
+ parse: (parser, lhs, until) => parseForComprehension(parser, lhs, until)
11147
+ },
10846
11148
  // \operatorname{break}
10847
11149
  {
10848
11150
  symbolTrigger: "break",
@@ -11047,7 +11349,10 @@ var ComputeEngineCore = (() => {
11047
11349
  if (!sym2 || !parser.getSymbolType(sym2).matches("function")) return null;
11048
11350
  parser.addBoundary([")"]);
11049
11351
  const expr2 = parser.parseExpression(until);
11050
- if (!parser.matchBoundary()) return null;
11352
+ if (!parser.matchBoundary()) {
11353
+ parser.removeBoundary();
11354
+ return null;
11355
+ }
11051
11356
  if (!parser.match("<}>")) return null;
11052
11357
  return ["Derivative", lhs, expr2];
11053
11358
  }
@@ -11488,7 +11793,12 @@ var ComputeEngineCore = (() => {
11488
11793
  if (isEmptySequence(body)) return ["List"];
11489
11794
  const h = operator(body);
11490
11795
  if (h === "Range" || h === "Linspace") return body;
11491
- if (h === "Sequence") return ["List", ...operands(body)];
11796
+ if (h === "Sequence") {
11797
+ const elems = operands(body);
11798
+ const inferred = tryInferRangeFromElements(elems, parser);
11799
+ if (inferred) return inferred;
11800
+ return ["List", ...elems];
11801
+ }
11492
11802
  if (h === "Delimiter") {
11493
11803
  const delim = stringValue(operand(body, 2)) ?? "...";
11494
11804
  if (delim === ";" || delim === ".;.") {
@@ -11501,12 +11811,37 @@ var ComputeEngineCore = (() => {
11501
11811
  }
11502
11812
  if (delim === "," || delim === ".,.") {
11503
11813
  body = operand(body, 1);
11504
- if (operator(body) === "Sequence") return ["List", ...operands(body)];
11814
+ if (operator(body) === "Sequence") {
11815
+ const elems = operands(body);
11816
+ const inferred = tryInferRangeFromElements(elems, parser);
11817
+ if (inferred) return inferred;
11818
+ return ["List", ...elems];
11819
+ }
11505
11820
  return ["List", body ?? "Nothing"];
11506
11821
  }
11507
11822
  }
11508
11823
  return ["List", body];
11509
11824
  }
11825
+ function tryInferRangeFromElements(elems, parser) {
11826
+ if (elems.length < 4) return null;
11827
+ const penultimate = elems[elems.length - 2];
11828
+ if (symbol(penultimate) !== "ContinuationPlaceholder") return null;
11829
+ const samples = elems.slice(0, -2);
11830
+ const endExpr = elems[elems.length - 1];
11831
+ if (samples.length < 2) return null;
11832
+ const sampleNums = samples.map(machineValue);
11833
+ if (sampleNums.some((n) => n === null)) return null;
11834
+ const nums = sampleNums;
11835
+ const step = nums[nums.length - 1] - nums[nums.length - 2];
11836
+ const tol = parser.options.tolerance;
11837
+ if (Math.abs(step) < tol)
11838
+ return parser.error("degenerate-range-step", parser.index);
11839
+ for (let i = 1; i < nums.length; i++) {
11840
+ if (Math.abs(nums[i] - nums[i - 1] - step) > tol)
11841
+ return parser.error("inconsistent-range-samples", parser.index);
11842
+ }
11843
+ return ["Range", nums[0], endExpr, step];
11844
+ }
11510
11845
  function serializeList(serializer, expr2) {
11511
11846
  if (nops(expr2) > 1 && operands(expr2).every((x) => {
11512
11847
  const op = operator(x);
@@ -11758,6 +12093,38 @@ var ComputeEngineCore = (() => {
11758
12093
  ["Element", index, ["Range", lower, upper]]
11759
12094
  ];
11760
12095
  }
12096
+ function parseForComprehension(parser, lhs, until) {
12097
+ const bindingTerminator = {
12098
+ minPrec: 21,
12099
+ // Above comma (20) and ; (19), so `x = L_1` is captured whole
12100
+ condition: (p) => {
12101
+ if (until?.condition?.(p)) return true;
12102
+ const saved = p.index;
12103
+ p.skipVisualSpace();
12104
+ const isComma = p.peek === ",";
12105
+ p.index = saved;
12106
+ if (isComma) return true;
12107
+ if (peekKeyword(p, "where")) return true;
12108
+ if (peekKeyword(p, "with")) return true;
12109
+ return false;
12110
+ }
12111
+ };
12112
+ const elements = [];
12113
+ do {
12114
+ parser.skipVisualSpace();
12115
+ const binding = parser.parseExpression(bindingTerminator);
12116
+ if (binding === null) break;
12117
+ const op = operator(binding);
12118
+ if (op !== "Equal" && op !== "Assign") return null;
12119
+ const name = operand(binding, 1);
12120
+ const list = operand(binding, 2);
12121
+ if (!name || !list) return null;
12122
+ elements.push(["Element", name, list]);
12123
+ parser.skipVisualSpace();
12124
+ } while (parser.match(","));
12125
+ if (elements.length === 0) return null;
12126
+ return ["Loop", lhs, ...elements];
12127
+ }
11761
12128
  function parseWhereExpression(parser, lhs, until) {
11762
12129
  const bindingTerminator = {
11763
12130
  minPrec: 21,
@@ -11780,6 +12147,25 @@ var ComputeEngineCore = (() => {
11780
12147
  parser.skipVisualSpace();
11781
12148
  } while (parser.match(","));
11782
12149
  if (bindings.length === 0) return null;
12150
+ const forStart = parser.index;
12151
+ if (matchKeyword(parser, "for")) {
12152
+ const loop = parseForComprehension(parser, lhs, until);
12153
+ if (loop) {
12154
+ const block2 = [];
12155
+ for (const b of bindings) {
12156
+ const normalized = normalizeLocalAssign(b);
12157
+ if (operator(normalized) === "Assign") {
12158
+ block2.push(["Declare", operand(normalized, 1)]);
12159
+ block2.push(normalized);
12160
+ } else {
12161
+ block2.push(normalized);
12162
+ }
12163
+ }
12164
+ block2.push(loop);
12165
+ return ["Block", ...block2];
12166
+ }
12167
+ parser.index = forStart;
12168
+ }
11783
12169
  const block = [];
11784
12170
  for (const b of bindings) {
11785
12171
  const normalized = normalizeLocalAssign(b);
@@ -11993,6 +12379,17 @@ var ComputeEngineCore = (() => {
11993
12379
  const upperExpr = openRight ? ["Open", upper] : upper;
11994
12380
  return ["Interval", lowerExpr, upperExpr];
11995
12381
  }
12382
+ var COMPARISON_HEADS = /* @__PURE__ */ new Set([
12383
+ "Less",
12384
+ "LessEqual",
12385
+ "Greater",
12386
+ "GreaterEqual",
12387
+ "Equal",
12388
+ "NotEqual",
12389
+ "And",
12390
+ "Or",
12391
+ "Not"
12392
+ ]);
11996
12393
  var DEFINITIONS_SETS = [
11997
12394
  //
11998
12395
  // Constants
@@ -12251,18 +12648,58 @@ var ComputeEngineCore = (() => {
12251
12648
  closeTrigger: "}",
12252
12649
  parse: (_parser, body) => {
12253
12650
  if (isEmptySequence(body)) return "EmptySet";
12651
+ if (operator(body) == "Delimiter" && stringValue(operand(body, 2)) === ",") {
12652
+ body = operand(body, 1);
12653
+ }
12254
12654
  const h = operator(body);
12255
- if (h === "Divides" || h === "Colon") {
12655
+ if (h === "Divides") {
12256
12656
  const expr2 = operand(body, 1);
12257
12657
  const condition = operand(body, 2);
12258
12658
  if (expr2 !== null && condition !== null)
12259
12659
  return ["Set", expr2, ["Condition", condition]];
12260
12660
  }
12261
- if (operator(body) == "Delimiter" && stringValue(operand(body, 2)) === ",") {
12262
- body = operand(body, 1);
12661
+ if (h === "Colon") {
12662
+ const lhs = operand(body, 1);
12663
+ const rhs = operand(body, 2);
12664
+ if (lhs !== null && rhs !== null) {
12665
+ const lhsOp = operator(lhs);
12666
+ if (lhsOp !== null && COMPARISON_HEADS.has(lhsOp)) {
12667
+ return ["Which", lhs, rhs];
12668
+ }
12669
+ return ["Set", lhs, ["Condition", rhs]];
12670
+ }
12263
12671
  }
12264
- if (operator(body) !== "Sequence") return ["Set", body];
12265
- return ["Set", ...operands(body)];
12672
+ if (h === "Sequence") {
12673
+ const elements = operands(body);
12674
+ const colonElements = elements.filter((el) => operator(el) === "Colon");
12675
+ const allPiecewise = colonElements.length > 0 && colonElements.every((el) => {
12676
+ const lhs = operand(el, 1);
12677
+ const lhsOp = lhs !== null ? operator(lhs) : null;
12678
+ return lhsOp !== null && COMPARISON_HEADS.has(lhsOp);
12679
+ });
12680
+ if (allPiecewise) {
12681
+ const whichOps = [];
12682
+ for (let i = 0; i < elements.length; i++) {
12683
+ const el = elements[i];
12684
+ if (operator(el) === "Colon") {
12685
+ const cond = operand(el, 1);
12686
+ const val = operand(el, 2);
12687
+ if (cond === null || val === null) {
12688
+ return ["Set", ...elements];
12689
+ }
12690
+ whichOps.push(cond, val);
12691
+ } else {
12692
+ if (i !== elements.length - 1) {
12693
+ return ["Set", ...elements];
12694
+ }
12695
+ whichOps.push("True", el);
12696
+ }
12697
+ }
12698
+ return ["Which", ...whichOps];
12699
+ }
12700
+ return ["Set", ...elements];
12701
+ }
12702
+ return ["Set", body];
12266
12703
  },
12267
12704
  serialize: (serializer, expr2) => {
12268
12705
  if (nops(expr2) === 2 && operator(operand(expr2, 2)) === "Condition") {
@@ -14266,7 +14703,8 @@ var ComputeEngineCore = (() => {
14266
14703
  minPrec: MULTIPLICATION_PRECEDENCE,
14267
14704
  condition: (parser2) => trigCommands[parser2.peek] || (until?.condition?.(parser2) ?? false)
14268
14705
  });
14269
- const appliedFn = args === null ? fn : typeof fn === "string" ? [fn, ...args] : ["Apply", fn, ...args];
14706
+ const head = fn === "Arctan" && args?.length === 2 ? "Arctan2" : fn;
14707
+ const appliedFn = args === null ? fn : typeof head === "string" ? [head, ...args] : ["Apply", head, ...args];
14270
14708
  return sup === null ? appliedFn : ["Power", appliedFn, sup];
14271
14709
  };
14272
14710
  }
@@ -16482,10 +16920,17 @@ var ComputeEngineCore = (() => {
16482
16920
  // The capitalized library entries already exist; these are pure parse
16483
16921
  // aliases so the lowercase names don't land in `unsupported-operator`.
16484
16922
  // ---------------------------------------------------------------------------
16923
+ { latexTrigger: "\\operatorname{count}", parse: "Length" },
16485
16924
  { latexTrigger: "\\operatorname{random}", parse: "Random" },
16486
16925
  { latexTrigger: "\\operatorname{shuffle}", parse: "Shuffle" },
16487
16926
  { latexTrigger: "\\operatorname{repeat}", parse: "Repeat" },
16488
16927
  { latexTrigger: "\\operatorname{join}", parse: "Join" },
16928
+ { latexTrigger: "\\operatorname{range}", parse: "Range" },
16929
+ // Note: `\operatorname{with}` (Desmos's local-binding clause) is intentionally
16930
+ // NOT registered here. Use the math-notation equivalent `\operatorname{where}`
16931
+ // (with `\coloneq` for bindings), or register `with` as a custom dictionary
16932
+ // entry at the integration layer — see the "Desmos-Specific Syntax — Prefer
16933
+ // Custom LaTeX Dictionary" section in COMPUTE_ENGINE.md for a worked example.
16489
16934
  // ---------------------------------------------------------------------------
16490
16935
  // Geometric primitive heads. Registered as known typed heads so consumers
16491
16936
  // can branch on the operator name; CE itself doesn't render them. The
@@ -19529,6 +19974,19 @@ var ComputeEngineCore = (() => {
19529
19974
  } while (postfix !== null);
19530
19975
  }
19531
19976
  if (result !== null) result = this.parseSupsub(result);
19977
+ if (result !== null) {
19978
+ let postfix = null;
19979
+ let index = this.index;
19980
+ do {
19981
+ postfix = this.parsePostfixOperator(result, until);
19982
+ result = postfix ?? result;
19983
+ if (this.index === index && postfix !== null) {
19984
+ console.assert(this.index !== index, "No token consumed");
19985
+ break;
19986
+ }
19987
+ index = this.index;
19988
+ } while (postfix !== null);
19989
+ }
19532
19990
  if (result === null) {
19533
19991
  result = this.options.parseUnexpectedToken?.(null, this) ?? null;
19534
19992
  if (result === null && this.peek.startsWith("\\")) {
@@ -20037,6 +20495,28 @@ var ComputeEngineCore = (() => {
20037
20495
  }
20038
20496
 
20039
20497
  // src/compute-engine/latex-syntax/serializer.ts
20498
+ var DOT_NOTATION_MAP = {
20499
+ First: ".x",
20500
+ Second: ".y",
20501
+ Third: ".z",
20502
+ Real: ".\\operatorname{real}",
20503
+ Imaginary: ".\\operatorname{imag}",
20504
+ Length: ".\\operatorname{count}",
20505
+ Sum: ".\\operatorname{total}",
20506
+ Max: ".\\max",
20507
+ Min: ".\\min"
20508
+ };
20509
+ function trySerializeDotNotation(serializer, expr2) {
20510
+ if (!serializer.options.dotNotation) return null;
20511
+ const ops = operands(expr2);
20512
+ if (!ops || ops.length !== 1) return null;
20513
+ const head = operator(expr2);
20514
+ if (!head) return null;
20515
+ const suffix = DOT_NOTATION_MAP[head];
20516
+ if (suffix === void 0) return null;
20517
+ const lhs = serializer.wrap(ops[0], 810);
20518
+ return `${lhs}${suffix}`;
20519
+ }
20040
20520
  var ACCENT_MODIFIERS = {
20041
20521
  deg: (s) => `${s}\\degree`,
20042
20522
  prime: (s) => `${s}^{\\prime}`,
@@ -20080,6 +20560,7 @@ var ComputeEngineCore = (() => {
20080
20560
  constructor(dictionary, options) {
20081
20561
  this.dictionary = dictionary;
20082
20562
  this.options = {
20563
+ dotNotation: false,
20083
20564
  dmsFormat: false,
20084
20565
  angleNormalization: "none",
20085
20566
  ...options
@@ -20178,6 +20659,8 @@ var ComputeEngineCore = (() => {
20178
20659
  return def?.serialize?.(this, expr2) ?? serializeSymbol2(symbol(expr2)) ?? "";
20179
20660
  }
20180
20661
  serializeFunction(expr2, def) {
20662
+ const dotResult = trySerializeDotNotation(this, expr2);
20663
+ if (dotResult !== null) return dotResult;
20181
20664
  if (def?.serialize) return def.serialize(this, expr2);
20182
20665
  const h = operator(expr2);
20183
20666
  return serializeSymbol2(h, "auto") + this.wrapArguments(expr2);
@@ -20420,6 +20903,8 @@ var ComputeEngineCore = (() => {
20420
20903
  preserveLatex: opts.preserveLatex ?? false,
20421
20904
  quantifierScope: opts.quantifierScope ?? "tight",
20422
20905
  timeDerivativeVariable: opts.timeDerivativeVariable ?? "t",
20906
+ // Standalone mode has no engine; use the same default as ComputeEngine
20907
+ tolerance: 1e-7,
20423
20908
  // Callbacks -- standalone mode has no engine, so these are stubs
20424
20909
  getSymbolType: (_id) => BoxedType.unknown,
20425
20910
  hasSubscriptEvaluate: (_id) => false,
@@ -20452,6 +20937,7 @@ var ComputeEngineCore = (() => {
20452
20937
  invisiblePlus: "",
20453
20938
  multiply: "\\times",
20454
20939
  missingSymbol: "\\blacksquare",
20940
+ dotNotation: false,
20455
20941
  dmsFormat: false,
20456
20942
  angleNormalization: "none",
20457
20943
  // Style callbacks -- use same defaults as the engine
@@ -22286,6 +22772,15 @@ var ComputeEngineCore = (() => {
22286
22772
  }
22287
22773
  } else {
22288
22774
  for (const item of t) {
22775
+ const op = item.operator;
22776
+ if (op === "Tuple" || op === "Pair" || op === "Single" || op === "Triple" || op === "Quadruple" || op === "KeyValuePair" || op === "Dictionary" || op === "Set" || op === "Record") {
22777
+ valid = false;
22778
+ return;
22779
+ }
22780
+ if (item.type.type === "string") {
22781
+ valid = false;
22782
+ return;
22783
+ }
22289
22784
  dtype = getSupertype(dtype, getExpressionDatatype(item));
22290
22785
  }
22291
22786
  }
@@ -27321,6 +27816,15 @@ ${lines.join("\n")}`;
27321
27816
  return void 0;
27322
27817
  }
27323
27818
 
27819
+ // src/compute-engine/numerics/random.ts
27820
+ function deterministicRandom(seed) {
27821
+ const v = Math.sin(seed * 12.9898) * 43758.5453;
27822
+ return v - Math.floor(v);
27823
+ }
27824
+ function nextSeed(seed) {
27825
+ return seed + 0.6180339887498949;
27826
+ }
27827
+
27324
27828
  // src/compute-engine/boxed-expression/canonical-utils.ts
27325
27829
  function canonical(ce, xs, scope) {
27326
27830
  if (xs.every((x) => x.isCanonical)) return xs;
@@ -27370,6 +27874,19 @@ ${lines.join("\n")}`;
27370
27874
  indexWhere: void 0
27371
27875
  }
27372
27876
  },
27877
+ Length: {
27878
+ description: "Number of elements in a collection. Returns undefined for non-collections and for infinite collections.",
27879
+ complexity: 4e3,
27880
+ signature: "(any) -> integer",
27881
+ type: () => "integer",
27882
+ evaluate: ([xs], { engine }) => {
27883
+ if (!xs.isCollection) return void 0;
27884
+ if (xs.isEmptyCollection) return engine.Zero;
27885
+ const n = xs.count;
27886
+ if (n === void 0 || !isFinite(n)) return void 0;
27887
+ return engine.number(n);
27888
+ }
27889
+ },
27373
27890
  Tuple: {
27374
27891
  description: "A fixed number of heterogeneous elements",
27375
27892
  complexity: 8200,
@@ -27422,7 +27939,11 @@ ${lines.join("\n")}`;
27422
27939
  //
27423
27940
  Range: {
27424
27941
  complexity: 8200,
27425
- signature: "(number, number?, step: number?) -> indexed_collection<integer>",
27942
+ signature: "(number, number?, step: number?) -> indexed_collection<number>",
27943
+ type: (ops) => {
27944
+ const allInt = ops.every((op) => op.isInteger);
27945
+ return allInt ? parseType("indexed_collection<integer>") : parseType("indexed_collection<number>");
27946
+ },
27426
27947
  canonical: (ops, { engine: ce }) => {
27427
27948
  if (ops.length === 0) return null;
27428
27949
  if (ops.length === 1) return ce._fn("Range", [ce.One, ops[0].canonical]);
@@ -27446,19 +27967,26 @@ ${lines.join("\n")}`;
27446
27967
  const [lower, upper, step] = range(expr2);
27447
27968
  if (step === 0) return 0;
27448
27969
  if (!isFinite(lower) || !isFinite(upper)) return Infinity;
27449
- return 1 + Math.max(0, Math.floor((upper - lower) / step));
27970
+ return Math.max(0, Math.floor((upper - lower) / step) + 1);
27450
27971
  },
27451
27972
  contains: (expr2, target) => {
27452
- if (!target.type.matches("integer")) return false;
27453
27973
  const t = target.re;
27974
+ if (!isFinite(t)) return false;
27454
27975
  const [lower, upper, step] = range(expr2);
27455
27976
  if (step === 0) return false;
27456
- if (step > 0) return t >= lower && t <= upper;
27457
- return t <= lower && t >= upper;
27977
+ if (step > 0) {
27978
+ if (t < lower || t > upper) return false;
27979
+ } else {
27980
+ if (t > lower || t < upper) return false;
27981
+ }
27982
+ const k = (t - lower) / step;
27983
+ const tol = expr2.engine.tolerance;
27984
+ const kRounded = Math.round(k);
27985
+ return kRounded >= 0 && Math.abs(k - kRounded) < tol;
27458
27986
  },
27459
27987
  iterator: (expr2) => {
27460
27988
  const [lower, upper, step] = range(expr2);
27461
- const maxCount = step === 0 ? 0 : Math.floor((upper - lower) / step) + 1;
27989
+ const maxCount = step === 0 ? 0 : Math.max(0, Math.floor((upper - lower) / step) + 1);
27462
27990
  let index = 1;
27463
27991
  return {
27464
27992
  next: () => {
@@ -27476,7 +28004,9 @@ ${lines.join("\n")}`;
27476
28004
  at: (expr2, index) => {
27477
28005
  if (typeof index !== "number") return void 0;
27478
28006
  const [lower, upper, step] = range(expr2);
27479
- if (index < 1 || index > 1 + (upper - lower) / step) return void 0;
28007
+ if (step === 0) return void 0;
28008
+ const maxCount = Math.max(0, Math.floor((upper - lower) / step) + 1);
28009
+ if (index < 1 || index > maxCount) return void 0;
27480
28010
  return expr2.engine.number(lower + step * (index - 1));
27481
28011
  },
27482
28012
  indexWhere: void 0,
@@ -27501,7 +28031,13 @@ ${lines.join("\n")}`;
27501
28031
  if (step > 0) return lower <= upper ? "positive" : "negative";
27502
28032
  return lower >= upper ? "positive" : "negative";
27503
28033
  },
27504
- elttype: (_expr) => "finite_integer"
28034
+ elttype: (expr2) => {
28035
+ if (!isFunction2(expr2)) return "finite_integer";
28036
+ for (let i = 1; i <= expr2.nops; i++) {
28037
+ if (!expr2[`op${i}`].isInteger) return "finite_real";
28038
+ }
28039
+ return "finite_integer";
28040
+ }
27505
28041
  }
27506
28042
  },
27507
28043
  Interval: {
@@ -27604,10 +28140,12 @@ ${lines.join("\n")}`;
27604
28140
  const upper = expr2.op2.re;
27605
28141
  let count = expr2.op3.re;
27606
28142
  if (!isFinite(count)) count = DEFAULT_LINSPACE_COUNT;
28143
+ count = Math.floor(count);
27607
28144
  if (!isFinite(lower) || !isFinite(upper)) return void 0;
27608
28145
  if (index < 1 || index > count) return void 0;
28146
+ if (count === 1) return expr2.engine.number(lower);
27609
28147
  return expr2.engine.number(
27610
- lower + (upper - lower) * (index - 1) / count
28148
+ lower + (upper - lower) * (index - 1) / (count - 1)
27611
28149
  );
27612
28150
  },
27613
28151
  iterator: (expr2) => {
@@ -27626,6 +28164,8 @@ ${lines.join("\n")}`;
27626
28164
  !isFinite(expr2.op3.re) ? DEFAULT_LINSPACE_COUNT : expr2.op3.re
27627
28165
  );
27628
28166
  }
28167
+ totalCount = Math.floor(totalCount);
28168
+ const denom = totalCount > 1 ? totalCount - 1 : 1;
27629
28169
  let index = 1;
27630
28170
  return {
27631
28171
  next: () => {
@@ -27634,7 +28174,7 @@ ${lines.join("\n")}`;
27634
28174
  index += 1;
27635
28175
  return {
27636
28176
  value: expr2.engine.number(
27637
- lower + (upper - lower) * (index - 1 - 1) / totalCount
28177
+ lower + (upper - lower) * (index - 1 - 1) / denom
27638
28178
  ),
27639
28179
  done: false
27640
28180
  };
@@ -27650,9 +28190,14 @@ ${lines.join("\n")}`;
27650
28190
  if (t < lower || t > upper) return false;
27651
28191
  let count = expr2.op3.re;
27652
28192
  if (!isFinite(count)) count = DEFAULT_LINSPACE_COUNT;
28193
+ count = Math.floor(count);
27653
28194
  if (count === 0) return false;
27654
- const step = (upper - lower) / count;
27655
- return (t - lower) % step === 0;
28195
+ if (count === 1) return t === lower;
28196
+ const step = (upper - lower) / (count - 1);
28197
+ const k = (t - lower) / step;
28198
+ const tol = expr2.engine.tolerance;
28199
+ const kRounded = Math.round(k);
28200
+ return kRounded >= 0 && kRounded <= count - 1 && Math.abs(k - kRounded) < tol;
27656
28201
  }
27657
28202
  }
27658
28203
  },
@@ -27977,10 +28522,12 @@ ${lines.join("\n")}`;
27977
28522
  description: [
27978
28523
  "Access an element of an indexed collection.",
27979
28524
  "If the index is negative, it is counted from the end.",
27980
- "Multiple indices can be provided to access nested collections (e.g., matrices)."
28525
+ "Multiple indices can be provided to access nested collections (e.g., matrices).",
28526
+ "If the index is a finite collection of booleans, returns the elements where the mask is True.",
28527
+ "If the index is a finite collection of integers, returns the elements at those indices."
27981
28528
  ],
27982
28529
  complexity: 8200,
27983
- signature: "(value: indexed_collection, index: (number|string)+) -> unknown",
28530
+ signature: "(value: indexed_collection, index: (number|string|indexed_collection)+) -> unknown",
27984
28531
  type: ([xs]) => xs.operatorDefinition?.collection?.elttype?.(xs) ?? collectionElementType(xs.type.type) ?? "any",
27985
28532
  evaluate: (ops, { engine: ce }) => {
27986
28533
  let expr2 = ops[0];
@@ -27991,12 +28538,39 @@ ${lines.join("\n")}`;
27991
28538
  if (!at) return void 0;
27992
28539
  const opAtIndex = ops[index];
27993
28540
  const s = isString(opAtIndex) ? opAtIndex.string : void 0;
27994
- if (s !== void 0) expr2 = at(expr2, s) ?? ce.Nothing;
27995
- else {
27996
- const i = ops[index].re;
27997
- if (!Number.isInteger(i)) return void 0;
27998
- expr2 = at(expr2, i) ?? ce.Nothing;
28541
+ if (s !== void 0) {
28542
+ expr2 = at(expr2, s) ?? ce.Nothing;
28543
+ index += 1;
28544
+ continue;
27999
28545
  }
28546
+ if (opAtIndex.isCollection && opAtIndex.isFiniteCollection) {
28547
+ const indices = Array.from(opAtIndex.each());
28548
+ const isMask = indices.every((m) => {
28549
+ const name = sym(m);
28550
+ return name === "True" || name === "False";
28551
+ });
28552
+ const picked = [];
28553
+ if (isMask) {
28554
+ indices.forEach((m, i2) => {
28555
+ if (sym(m) !== "True") return;
28556
+ const v = at(expr2, i2 + 1);
28557
+ if (v !== void 0) picked.push(v);
28558
+ });
28559
+ } else {
28560
+ for (const m of indices) {
28561
+ const k = m.re;
28562
+ if (!Number.isInteger(k)) return void 0;
28563
+ const v = at(expr2, k);
28564
+ if (v !== void 0) picked.push(v);
28565
+ }
28566
+ }
28567
+ expr2 = ce._fn("List", picked);
28568
+ index += 1;
28569
+ continue;
28570
+ }
28571
+ const i = opAtIndex.re;
28572
+ if (!Number.isInteger(i)) return void 0;
28573
+ expr2 = at(expr2, i) ?? ce.Nothing;
28000
28574
  index += 1;
28001
28575
  }
28002
28576
  return expr2;
@@ -28007,7 +28581,7 @@ ${lines.join("\n")}`;
28007
28581
  description: ["Return `n` elements from a collection."],
28008
28582
  complexity: 8200,
28009
28583
  signature: "(xs: indexed_collection, count: number) -> indexed_collection",
28010
- type: ([xs]) => `list<${collectionElementType(xs.type.type)}>`,
28584
+ type: ([xs]) => `list<${typeToString(collectionElementType(xs.type.type) ?? "any")}>`,
28011
28585
  evaluate: (ops, { engine, materialization: eager }) => {
28012
28586
  if (!eager) return void 0;
28013
28587
  const takeExpr = engine._fn("Take", ops);
@@ -28054,7 +28628,7 @@ ${lines.join("\n")}`;
28054
28628
  description: ["Return the collection without the first n elements."],
28055
28629
  complexity: 8200,
28056
28630
  signature: "(xs: indexed_collection, count: number) -> indexed_collection",
28057
- type: ([xs]) => `list<${collectionElementType(xs.type.type)}>`,
28631
+ type: ([xs]) => `list<${typeToString(collectionElementType(xs.type.type) ?? "any")}>`,
28058
28632
  collection: {
28059
28633
  isLazy: (_expr) => true,
28060
28634
  count: (expr2) => {
@@ -28099,15 +28673,45 @@ ${lines.join("\n")}`;
28099
28673
  },
28100
28674
  First: {
28101
28675
  complexity: 8200,
28102
- signature: "(collection) -> any",
28676
+ signature: "(any) -> any",
28103
28677
  type: ([xs]) => xs.operatorDefinition?.collection?.elttype?.(xs) ?? "any",
28104
- evaluate: ([xs], { engine: ce }) => xs.at(1) ?? ce.Nothing
28678
+ evaluate: ([xs], { engine: ce }) => {
28679
+ if (!xs.isCollection)
28680
+ return ce.error([
28681
+ "incompatible-type",
28682
+ `'collection'`,
28683
+ xs.type.toString()
28684
+ ]);
28685
+ return xs.at(1) ?? ce.Nothing;
28686
+ }
28105
28687
  },
28106
28688
  Second: {
28107
28689
  complexity: 8200,
28108
- signature: "(collection) -> any",
28690
+ signature: "(any) -> any",
28109
28691
  type: ([xs]) => xs.operatorDefinition?.collection?.elttype?.(xs) ?? "any",
28110
- evaluate: ([xs], { engine: ce }) => xs.at(2) ?? ce.Nothing
28692
+ evaluate: ([xs], { engine: ce }) => {
28693
+ if (!xs.isCollection)
28694
+ return ce.error([
28695
+ "incompatible-type",
28696
+ `'collection'`,
28697
+ xs.type.toString()
28698
+ ]);
28699
+ return xs.at(2) ?? ce.Nothing;
28700
+ }
28701
+ },
28702
+ Third: {
28703
+ complexity: 8200,
28704
+ signature: "(any) -> any",
28705
+ type: ([xs]) => xs.operatorDefinition?.collection?.elttype?.(xs) ?? "any",
28706
+ evaluate: ([xs], { engine: ce }) => {
28707
+ if (!xs.isCollection)
28708
+ return ce.error([
28709
+ "incompatible-type",
28710
+ `'collection'`,
28711
+ xs.type.toString()
28712
+ ]);
28713
+ return xs.at(3) ?? ce.Nothing;
28714
+ }
28111
28715
  },
28112
28716
  Last: {
28113
28717
  complexity: 8200,
@@ -28220,7 +28824,9 @@ ${lines.join("\n")}`;
28220
28824
  ],
28221
28825
  complexity: 8200,
28222
28826
  signature: "(value: indexed_collection, start: number, end: number) -> list",
28223
- type: ([xs]) => parseType(`list<${collectionElementType(xs.type.type)}>`),
28827
+ type: ([xs]) => parseType(
28828
+ `list<${typeToString(collectionElementType(xs.type.type) ?? "any")}>`
28829
+ ),
28224
28830
  collection: {
28225
28831
  isLazy: (_expr) => true,
28226
28832
  count: (expr2) => {
@@ -28552,16 +29158,26 @@ ${lines.join("\n")}`;
28552
29158
  },
28553
29159
  // Randomize the order of the elements in the collection.
28554
29160
  Shuffle: {
28555
- description: "Randomize the order of the elements in the collection.",
29161
+ description: "Randomize the order of the elements in the collection. With an optional `seed` argument, the shuffle is deterministic.",
28556
29162
  complexity: 8200,
28557
- signature: "(indexed_collection) -> indexed_collection",
29163
+ signature: "(indexed_collection, real?) -> indexed_collection",
28558
29164
  type: (ops) => ops[0].type,
28559
- evaluate: ([xs], { engine: ce }) => {
29165
+ evaluate: ([xs, seedOp], { engine: ce }) => {
28560
29166
  if (!xs.isFiniteCollection) return void 0;
28561
29167
  const data = Array.from(xs.each());
28562
- for (let i = data.length - 1; i > 0; i--) {
28563
- const j = Math.floor(Math.random() * (i + 1));
28564
- [data[i], data[j]] = [data[j], data[i]];
29168
+ const seed = seedOp?.re;
29169
+ if (seed !== void 0 && !Number.isNaN(seed)) {
29170
+ let s = seed;
29171
+ for (let i = data.length - 1; i > 0; i--) {
29172
+ const j = Math.floor(deterministicRandom(s) * (i + 1));
29173
+ [data[i], data[j]] = [data[j], data[i]];
29174
+ s = nextSeed(s);
29175
+ }
29176
+ } else {
29177
+ for (let i = data.length - 1; i > 0; i--) {
29178
+ const j = Math.floor(Math.random() * (i + 1));
29179
+ [data[i], data[j]] = [data[j], data[i]];
29180
+ }
28565
29181
  }
28566
29182
  return ce.function(xs.operator, data);
28567
29183
  }
@@ -28628,7 +29244,9 @@ ${lines.join("\n")}`;
28628
29244
  if (t === "string")
28629
29245
  return parseType(`tuple<list<string>, list<integer>>`);
28630
29246
  return parseType(
28631
- `tuple<list<${collectionElementType(t)}>, list<integer>>`
29247
+ `tuple<list<${typeToString(
29248
+ collectionElementType(t) ?? "any"
29249
+ )}>, list<integer>>`
28632
29250
  );
28633
29251
  },
28634
29252
  evaluate: (ops, { engine: ce }) => {
@@ -28644,7 +29262,7 @@ ${lines.join("\n")}`;
28644
29262
  description: "Return a list of the unique elements of the collection.",
28645
29263
  complexity: 8200,
28646
29264
  signature: "(collection) -> list",
28647
- type: ([xs]) => `list<${collectionElementType(xs.type.type)}>`,
29265
+ type: ([xs]) => `list<${typeToString(collectionElementType(xs.type.type) ?? "any")}>`,
28648
29266
  evaluate: (ops, { engine: ce }) => {
28649
29267
  if (!ops[0].isFiniteCollection) return void 0;
28650
29268
  const [values, _counts] = tally(ops[0]);
@@ -28656,7 +29274,7 @@ ${lines.join("\n")}`;
28656
29274
  wikidata: "Q381060",
28657
29275
  complexity: 8200,
28658
29276
  signature: "(collection, integer | function) -> list",
28659
- type: ([xs]) => `list<${collectionElementType(xs.type.type)}>`,
29277
+ type: ([xs]) => `list<${typeToString(collectionElementType(xs.type.type) ?? "any")}>`,
28660
29278
  evaluate: ([xs, arg], { engine: ce }) => {
28661
29279
  if (!xs.isFiniteCollection) return void 0;
28662
29280
  const k = toInteger(arg);
@@ -28830,32 +29448,74 @@ ${lines.join("\n")}`;
28830
29448
  }
28831
29449
  }
28832
29450
  },
28833
- // Repeat(x) -> [x, x, ...]
28834
- // This is an infinite series. Can use Take(Repeat(x), n) to get a finite series
28835
- // x is evaluated once. Although could use Hold()?
28836
- // So that First(Repeat(Hold(Random(5))), 10) would return 10 random numbers...
29451
+ // Repeat(x) -> [x, x, ...] — infinite sequence
29452
+ // Repeat(x, n) -> [x, x, ..., x] — finite list of n copies
28837
29453
  Repeat: {
28838
- description: "Produce an infinite sequence by repeating a single value.",
29454
+ 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.",
28839
29455
  complexity: 8200,
28840
- signature: "(value: any) -> list",
29456
+ signature: "(value: any, count: integer?) -> list",
29457
+ evaluate: (ops, { engine }) => {
29458
+ if (ops.length !== 2) return void 0;
29459
+ const raw = toInteger(ops[1]);
29460
+ if (raw === null) return void 0;
29461
+ const n = Math.max(0, raw);
29462
+ if (n > engine.maxCollectionSize) return void 0;
29463
+ return engine._fn("List", Array(n).fill(ops[0]));
29464
+ },
28841
29465
  collection: {
28842
- isLazy: (_expr) => true,
28843
- count: () => Infinity,
28844
- isEmpty: (_expr) => false,
28845
- // Never empty
28846
- isFinite: () => false,
28847
- // Infinite collection
29466
+ isLazy: (expr2) => isFunction2(expr2) && expr2.ops?.length === 1,
29467
+ count: (expr2) => {
29468
+ if (!isFunction2(expr2)) return void 0;
29469
+ if (expr2.ops?.length === 2) {
29470
+ const n = toInteger(expr2.op2);
29471
+ return n !== null ? Math.max(0, n) : void 0;
29472
+ }
29473
+ return Infinity;
29474
+ },
29475
+ isEmpty: (expr2) => {
29476
+ if (!isFunction2(expr2)) return void 0;
29477
+ if (expr2.ops?.length === 2) {
29478
+ const n = toInteger(expr2.op2);
29479
+ return n !== null ? n <= 0 : void 0;
29480
+ }
29481
+ return false;
29482
+ },
29483
+ isFinite: (expr2) => isFunction2(expr2) && expr2.ops?.length === 2,
28848
29484
  contains: (expr2, target) => {
28849
29485
  if (!isFunction2(expr2)) return false;
29486
+ if (expr2.ops?.length === 2) {
29487
+ const n = toInteger(expr2.op2);
29488
+ if (n !== null && n <= 0) return false;
29489
+ }
28850
29490
  return expr2.op1.isSame(target);
28851
29491
  },
28852
29492
  iterator: (expr2) => {
28853
29493
  if (!isFunction2(expr2))
28854
29494
  return { next: () => ({ value: void 0, done: true }) };
29495
+ if (expr2.ops?.length === 2) {
29496
+ const n = toInteger(expr2.op2);
29497
+ if (n === null) {
29498
+ return { next: () => ({ value: void 0, done: true }) };
29499
+ }
29500
+ const count = Math.max(0, n);
29501
+ let i = 0;
29502
+ return {
29503
+ next: () => i++ < count ? { value: expr2.op1, done: false } : { value: void 0, done: true }
29504
+ };
29505
+ }
28855
29506
  return { next: () => ({ value: expr2.op1, done: false }) };
28856
29507
  },
28857
- at: (expr2, _index) => {
29508
+ // at is 1-based (consistent with Range, Take, and other collection handlers)
29509
+ at: (expr2, index) => {
28858
29510
  if (!isFunction2(expr2)) return void 0;
29511
+ if (typeof index !== "number") return void 0;
29512
+ if (expr2.ops?.length === 2) {
29513
+ const n = toInteger(expr2.op2);
29514
+ const count = n !== null ? Math.max(0, n) : 0;
29515
+ if (index < 1 || index > count) return void 0;
29516
+ } else {
29517
+ if (index < 1) return void 0;
29518
+ }
28859
29519
  return expr2.op1;
28860
29520
  }
28861
29521
  }
@@ -29086,17 +29746,14 @@ ${lines.join("\n")}`;
29086
29746
  if (!isFunction2(expr2)) return [1, 0, 0];
29087
29747
  if (expr2.nops === 0) return [1, 0, 0];
29088
29748
  let op1 = expr2.op1.re;
29089
- if (!isFinite(op1)) op1 = 1;
29090
- else op1 = Math.round(op1);
29749
+ if (!isFinite(op1) && !op1) op1 = 1;
29091
29750
  if (expr2.nops === 1) return [1, op1, 1];
29092
29751
  let op2 = expr2.op2.re;
29093
29752
  if (!isFinite(op2) && !op2) op2 = 1;
29094
- else if (isFinite(op2)) op2 = Math.round(op2);
29095
- if (expr2.nops === 2) return [op1, op2, op2 > op1 ? 1 : -1];
29753
+ if (expr2.nops === 2) return [op1, op2, op2 >= op1 ? 1 : -1];
29096
29754
  let op3 = expr2.op3.re;
29097
- if (!isFinite(op3)) op3 = 1;
29098
- else op3 = Math.abs(Math.round(op3));
29099
- return [op1, op2, op1 < op2 ? op3 : -op3];
29755
+ if (!isFinite(op3) && !op3) op3 = 1;
29756
+ return [op1, op2, op3];
29100
29757
  }
29101
29758
  function rangeLast(r) {
29102
29759
  const [lower, upper, step] = r;
@@ -31144,11 +31801,12 @@ ${lines.join("\n")}`;
31144
31801
  );
31145
31802
  }
31146
31803
  },
31147
- // Complex: {
31148
- // // This function is converted during boxing, so unlikely to encounter
31149
- // wikidata: 'Q11567',
31150
- // complexity: 500,
31151
- // },
31804
+ Complex: {
31805
+ 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.',
31806
+ wikidata: "Q11567",
31807
+ complexity: 500,
31808
+ signature: "(real: number, imaginary: number) -> complex"
31809
+ },
31152
31810
  Divide: {
31153
31811
  description: "Quotient of a numerator and one or more denominators.",
31154
31812
  wikidata: "Q1226939",
@@ -32498,48 +33156,83 @@ ${lines.join("\n")}`;
32498
33156
  }
32499
33157
  },
32500
33158
  Sum: {
32501
- description: "`Sum(f, [a, b])` computes the sum of `f` from `a` to `b`",
33159
+ description: "`Sum(f, [a, b])` computes the sum of `f` from `a` to `b`; `Sum(L)` sums the elements of a collection `L`",
32502
33160
  wikidata: "Q218005",
32503
33161
  complexity: 1e3,
32504
33162
  broadcastable: false,
32505
33163
  scoped: true,
32506
33164
  lazy: true,
32507
- signature: "((number) -> number, bounds:tuple+) -> number",
32508
- canonical: ([body, ...bounds], { scope }) => canonicalBigop("Sum", body, bounds, scope),
32509
- evaluate: ([body, ...indexes], { engine, numericApproximation: numericApproximation2 }) => {
33165
+ signature: "(any, tuple*) -> number",
33166
+ canonical: ([body, ...bounds], { scope, engine: ce }) => {
33167
+ if (bounds.length === 0) {
33168
+ const canon = body?.canonical;
33169
+ if (canon?.isCollection) return ce._fn("Sum", [canon]);
33170
+ }
33171
+ return canonicalBigop("Sum", body, bounds, scope);
33172
+ },
33173
+ evaluate: ([first, ...rest], { engine, numericApproximation: numericApproximation2 }) => {
33174
+ if (rest.length === 0 && first?.isCollection) {
33175
+ if (first.isFiniteCollection !== true) return void 0;
33176
+ const result2 = run(
33177
+ reduceCollection2(
33178
+ first,
33179
+ engine.Zero,
33180
+ (acc, x) => acc.add(x.evaluate({ numericApproximation: numericApproximation2 }))
33181
+ ),
33182
+ engine._timeRemaining
33183
+ );
33184
+ return result2?.evaluate({ numericApproximation: numericApproximation2 }) ?? engine.NaN;
33185
+ }
32510
33186
  const result = run(
32511
33187
  reduceBigOp(
32512
- body,
32513
- indexes,
33188
+ first,
33189
+ rest,
32514
33190
  (acc, x) => acc.add(x.evaluate({ numericApproximation: numericApproximation2 })),
32515
33191
  engine.Zero
32516
33192
  ),
32517
33193
  engine._timeRemaining
32518
33194
  );
32519
- if (result === NON_ENUMERABLE_DOMAIN) {
32520
- return void 0;
32521
- }
33195
+ if (result === NON_ENUMERABLE_DOMAIN) return void 0;
32522
33196
  return result?.evaluate({ numericApproximation: numericApproximation2 }) ?? engine.NaN;
32523
33197
  },
32524
- evaluateAsync: async (xs, { engine, signal, numericApproximation: numericApproximation2 }) => {
33198
+ evaluateAsync: async ([first, ...rest], { engine, signal, numericApproximation: numericApproximation2 }) => {
33199
+ if (rest.length === 0 && first?.isCollection) {
33200
+ if (first.isFiniteCollection !== true) return void 0;
33201
+ const result2 = await runAsync(
33202
+ reduceCollection2(
33203
+ first,
33204
+ engine.Zero,
33205
+ (acc, x) => acc.add(x.evaluate({ numericApproximation: numericApproximation2 }))
33206
+ ),
33207
+ engine._timeRemaining,
33208
+ signal
33209
+ );
33210
+ return result2?.evaluate({ numericApproximation: numericApproximation2 }) ?? engine.NaN;
33211
+ }
32525
33212
  const result = await runAsync(
32526
33213
  reduceBigOp(
32527
- xs[0],
32528
- xs.slice(1),
33214
+ first,
33215
+ rest,
32529
33216
  (acc, x) => acc.add(x.evaluate({ numericApproximation: numericApproximation2 })),
32530
33217
  engine.Zero
32531
33218
  ),
32532
33219
  engine._timeRemaining,
32533
33220
  signal
32534
33221
  );
32535
- if (result === NON_ENUMERABLE_DOMAIN) {
32536
- return void 0;
32537
- }
33222
+ if (result === NON_ENUMERABLE_DOMAIN) return void 0;
32538
33223
  return result?.evaluate({ numericApproximation: numericApproximation2 }) ?? engine.NaN;
32539
33224
  }
32540
33225
  }
32541
33226
  }
32542
33227
  ];
33228
+ function* reduceCollection2(collection, init, combine) {
33229
+ let acc = init;
33230
+ for (const x of collection.each()) {
33231
+ acc = combine(acc, x);
33232
+ yield acc;
33233
+ }
33234
+ return acc;
33235
+ }
32543
33236
  function evaluateAbs(arg) {
32544
33237
  const ce = arg.engine;
32545
33238
  if (isNumber(arg)) {
@@ -42776,7 +43469,7 @@ ${e.message}
42776
43469
  var CONTROL_STRUCTURES_LIBRARY = [
42777
43470
  {
42778
43471
  Block: {
42779
- description: "Evaluate a sequence of expressions in a local scope.",
43472
+ 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.",
42780
43473
  lazy: true,
42781
43474
  scoped: true,
42782
43475
  signature: "(unknown*) -> unknown",
@@ -42826,12 +43519,42 @@ ${e.message}
42826
43519
  }
42827
43520
  },
42828
43521
  Loop: {
42829
- description: "Evaluate a body expression over elements of a collection.",
43522
+ description: "Evaluate a body expression in nested iteration over Element clauses. Later clauses see earlier bindings; independent clauses produce a Cartesian product.",
42830
43523
  lazy: true,
42831
- signature: "(body:expression, collection:expression) -> any",
42832
- type: ([body]) => body.type,
42833
- evaluate: ([body, collection], { engine: ce }) => run(runLoop(body, collection, ce), ce._timeRemaining),
42834
- evaluateAsync: async ([body, collection], { engine: ce, signal }) => runAsync(runLoop(body, collection, ce), ce._timeRemaining, signal)
43524
+ signature: "(body:expression, iterators:expression*) -> any",
43525
+ type: ([body]) => {
43526
+ if (!body) return "nothing";
43527
+ return parseType(`indexed_collection<${String(body.type)}>`);
43528
+ },
43529
+ canonical: canonicalLoop,
43530
+ evaluate: (ops, { engine: ce }) => run(runLoop(ops[0], ops.slice(1), ce), ce._timeRemaining),
43531
+ evaluateAsync: async (ops, { engine: ce, signal }) => runAsync(runLoop(ops[0], ops.slice(1), ce), ce._timeRemaining, signal)
43532
+ },
43533
+ When: {
43534
+ 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.',
43535
+ lazy: true,
43536
+ signature: "(expression, boolean) -> any",
43537
+ type: ([expr2]) => expr2.type,
43538
+ canonical: (args, { engine: ce }) => {
43539
+ if (args.length !== 2) return null;
43540
+ const [expr2, cond] = args;
43541
+ if (isFunction2(expr2, "When")) {
43542
+ const inner = expr2.op1.canonical;
43543
+ const innerCond = expr2.op2.canonical;
43544
+ return ce._fn("When", [
43545
+ inner,
43546
+ ce._fn("And", [innerCond, cond.canonical])
43547
+ ]);
43548
+ }
43549
+ return ce._fn("When", [expr2.canonical, cond.canonical]);
43550
+ },
43551
+ evaluate: ([expr2, cond], { engine: ce }) => {
43552
+ const c = cond.evaluate();
43553
+ const cs = sym(c);
43554
+ if (cs === "True") return expr2.evaluate();
43555
+ if (cs === "False") return ce.symbol("Undefined");
43556
+ return ce._fn("When", [expr2, c]);
43557
+ }
42835
43558
  },
42836
43559
  Which: {
42837
43560
  description: "Return the value for the first condition that is true.",
@@ -42891,9 +43614,141 @@ ${e.message}
42891
43614
  );
42892
43615
  return result;
42893
43616
  }
42894
- function* runLoop(body, collection, ce) {
43617
+ function canonicalLoop(ops, options) {
43618
+ const { engine: ce, scope } = options;
43619
+ if (ops.length === 0) return null;
43620
+ if (ops.length === 1) {
43621
+ return ce._fn("Loop", [ops[0].canonical]);
43622
+ }
43623
+ const body = ops[0];
43624
+ const iterators = ops.slice(1);
43625
+ const allElement = iterators.every((it) => it.operator === "Element");
43626
+ if (!allElement) {
43627
+ return ce._fn(
43628
+ "Loop",
43629
+ ops.map((op) => op.canonical)
43630
+ );
43631
+ }
43632
+ const loopScope = scope ?? {
43633
+ parent: ce.context.lexicalScope,
43634
+ bindings: /* @__PURE__ */ new Map()
43635
+ };
43636
+ loopScope.noAutoDeclare = true;
43637
+ ce.pushScope(loopScope);
43638
+ let canonicalIterators;
43639
+ let canonicalBody;
43640
+ try {
43641
+ canonicalIterators = iterators.map((it) => {
43642
+ if (!isFunction2(it, "Element")) {
43643
+ return ce._fn("Element", [
43644
+ ce.error("missing").canonical,
43645
+ ce.error("missing").canonical
43646
+ ]);
43647
+ }
43648
+ const indexExpr = it.ops[0];
43649
+ const collExpr = it.ops[1];
43650
+ if (!indexExpr || !collExpr) {
43651
+ return ce._fn("Element", [
43652
+ (indexExpr ?? ce.error("missing")).canonical,
43653
+ (collExpr ?? ce.error("missing")).canonical
43654
+ ]);
43655
+ }
43656
+ if (isSymbol2(indexExpr) && indexExpr.symbol !== "Nothing") {
43657
+ if (!ce.context.lexicalScope.bindings.has(indexExpr.symbol))
43658
+ ce.declare(indexExpr.symbol, "unknown");
43659
+ }
43660
+ return ce._fn("Element", [indexExpr.canonical, collExpr.canonical]);
43661
+ });
43662
+ canonicalBody = body.canonical;
43663
+ } finally {
43664
+ ce.popScope();
43665
+ loopScope.noAutoDeclare = false;
43666
+ }
43667
+ return ce._fn("Loop", [canonicalBody, ...canonicalIterators], {
43668
+ scope: loopScope
43669
+ });
43670
+ }
43671
+ function* runLoop(body, elements, ce) {
42895
43672
  body ??= ce.Nothing;
42896
43673
  if (sym(body) === "Nothing") return body;
43674
+ if (elements.length === 0) {
43675
+ const result = body.evaluate();
43676
+ yield result;
43677
+ return result;
43678
+ }
43679
+ if (elements.length === 1 && elements[0].operator !== "Element") {
43680
+ return yield* runLoopLegacy(body, elements[0], ce);
43681
+ }
43682
+ const results = [];
43683
+ const state = { stopped: false, broke: false, count: 0 };
43684
+ const freshScope = {
43685
+ parent: ce.context.lexicalScope,
43686
+ bindings: /* @__PURE__ */ new Map()
43687
+ };
43688
+ ce._pushEvalContext(freshScope);
43689
+ try {
43690
+ for (const elem of elements) {
43691
+ if (!isFunction2(elem, "Element")) continue;
43692
+ const idx = elem.ops[0];
43693
+ if (idx && isSymbol2(idx) && idx.symbol !== "Nothing") {
43694
+ if (!freshScope.bindings.has(idx.symbol))
43695
+ ce.declare(idx.symbol, "unknown");
43696
+ }
43697
+ }
43698
+ yield* runLoopNested(body, elements, 0, ce, results, state);
43699
+ } finally {
43700
+ ce._popEvalContext();
43701
+ }
43702
+ if (state.stopped && state.value !== void 0) {
43703
+ if (!state.broke) return state.value;
43704
+ return state.value;
43705
+ }
43706
+ return ce.function("List", results);
43707
+ }
43708
+ function* runLoopNested(body, elements, index, ce, results, state) {
43709
+ if (state.stopped) return;
43710
+ if (index === elements.length) {
43711
+ const result = body.evaluate();
43712
+ state.count += 1;
43713
+ if (state.count > ce.iterationLimit)
43714
+ throw new CancellationError({ cause: "iteration-limit-exceeded" });
43715
+ if (isFunction2(result, "Break")) {
43716
+ state.stopped = true;
43717
+ state.broke = true;
43718
+ state.value = result.op1;
43719
+ return;
43720
+ }
43721
+ if (result.operator === "Return") {
43722
+ state.stopped = true;
43723
+ state.value = result;
43724
+ return;
43725
+ }
43726
+ results.push(result);
43727
+ yield result;
43728
+ return;
43729
+ }
43730
+ const elem = elements[index];
43731
+ if (!isFunction2(elem, "Element")) {
43732
+ return;
43733
+ }
43734
+ const indexExpr = elem.ops[0];
43735
+ const collExpr = elem.ops[1];
43736
+ if (!indexExpr || !isSymbol2(indexExpr) || !collExpr) {
43737
+ return;
43738
+ }
43739
+ const name = indexExpr.symbol;
43740
+ const collection = collExpr.evaluate();
43741
+ if (!collection?.isCollection) {
43742
+ return;
43743
+ }
43744
+ const skipAssign = name === "Nothing";
43745
+ for (const value of collection.each()) {
43746
+ if (!skipAssign) ce.assign(name, value);
43747
+ yield* runLoopNested(body, elements, index + 1, ce, results, state);
43748
+ if (state.stopped) return;
43749
+ }
43750
+ }
43751
+ function* runLoopLegacy(body, collection, ce) {
42897
43752
  if (collection?.isCollection) {
42898
43753
  let result = void 0;
42899
43754
  const fn = applicable(body);
@@ -44535,7 +45390,7 @@ ${e.message}
44535
45390
  evaluate: (ops) => apply(ops[0], ops.slice(1))
44536
45391
  },
44537
45392
  Assign: {
44538
- description: "Assign a value to a symbol or define a sequence",
45393
+ 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).",
44539
45394
  lazy: true,
44540
45395
  pure: false,
44541
45396
  signature: "(symbol | expression, any) -> any",
@@ -44666,7 +45521,12 @@ ${e.message}
44666
45521
  evaluate: (ops, { engine: ce }) => {
44667
45522
  const symbolName2 = sym(ops[0].evaluate());
44668
45523
  if (!symbolName2) return void 0;
45524
+ const currentScope = ce.context.lexicalScope;
45525
+ const existing = currentScope.bindings.get(symbolName2);
45526
+ const existingValueDef = existing && isValueDef(existing) ? existing : void 0;
45527
+ const isAutoDeclareHere = !!existingValueDef && existingValueDef.value.inferredType && existingValueDef.value.value === void 0;
44669
45528
  if (!ops[1]) {
45529
+ if (isAutoDeclareHere) return ce.Nothing;
44670
45530
  ce.declare(symbolName2, { inferred: true, type: "unknown" });
44671
45531
  return ce.Nothing;
44672
45532
  }
@@ -44675,6 +45535,11 @@ ${e.message}
44675
45535
  (isString(t) ? t.string : void 0) ?? sym(t) ?? void 0
44676
45536
  );
44677
45537
  if (!isValidType(type2)) return void 0;
45538
+ if (isAutoDeclareHere && existingValueDef) {
45539
+ existingValueDef.value.type = ce.type(type2);
45540
+ existingValueDef.value.inferredType = false;
45541
+ return ce.Nothing;
45542
+ }
44678
45543
  ce.declare(symbolName2, type2);
44679
45544
  return ce.Nothing;
44680
45545
  }
@@ -44792,33 +45657,41 @@ ${e.message}
44792
45657
  },
44793
45658
  Random: {
44794
45659
  description: [
44795
- "Random(): Return a random number between 0 and 1",
44796
- "Random(n): Return a random integer between 0 and n-1",
44797
- "Random(m, n): Return a random integer between m and n-1"
45660
+ "Random(): non-deterministic float in [0, 1)",
45661
+ "Random(seed: real): deterministic float in [0, 1) from a real seed",
45662
+ "Random(n: integer): non-deterministic integer in [0, n)",
45663
+ "Random(m: integer, n: integer): non-deterministic integer in [m, n)"
44798
45664
  ],
44799
45665
  pure: false,
44800
- signature: "(lower:integer?, upper:integer?) -> finite_number",
44801
- type: ([lower, upper]) => {
44802
- if (lower === void 0 && upper === void 0) return "finite_number";
44803
- return "finite_integer";
45666
+ // Signature accepts: nothing, one number, or two integers.
45667
+ // Use `number` (not `integer`) for the single-arg case so float seeds
45668
+ // type-check; runtime dispatch differentiates integer vs real.
45669
+ signature: "(number?, integer?) -> finite_number",
45670
+ type: ([first, second]) => {
45671
+ if (first === void 0) return "finite_number";
45672
+ if (second !== void 0) return "finite_integer";
45673
+ if (first.type.matches("integer")) return "finite_integer";
45674
+ return "finite_number";
44804
45675
  },
44805
45676
  sgn: () => "non-negative",
44806
45677
  evaluate: (ops, { engine: ce }) => {
44807
45678
  if (ops.length === 0) return ce.number(Math.random());
44808
- const [lowerOp, upperOp] = ops;
44809
- let lower;
44810
- let upper;
44811
- if (upperOp === void 0) {
44812
- lower = 0;
44813
- upper = Math.floor(lowerOp.re - 1);
44814
- if (isNaN(upper)) upper = 0;
44815
- } else {
44816
- lower = Math.floor(lowerOp.re);
44817
- upper = Math.floor(upperOp.re);
45679
+ const [firstOp, secondOp] = ops;
45680
+ if (secondOp !== void 0) {
45681
+ let lower = Math.floor(firstOp.re);
45682
+ let upper = Math.floor(secondOp.re);
44818
45683
  if (isNaN(lower)) lower = 0;
44819
45684
  if (isNaN(upper)) upper = 0;
45685
+ return ce.number(lower + Math.floor(Math.random() * (upper - lower)));
45686
+ }
45687
+ if (firstOp.type.matches("integer")) {
45688
+ let n = Math.floor(firstOp.re);
45689
+ if (isNaN(n)) n = 0;
45690
+ return ce.number(Math.floor(Math.random() * n));
44820
45691
  }
44821
- return ce.number(lower + Math.floor(Math.random() * (upper - lower)));
45692
+ const seed = firstOp.re;
45693
+ if (isNaN(seed)) return ce.number(0);
45694
+ return ce.number(deterministicRandom(seed));
44822
45695
  }
44823
45696
  },
44824
45697
  // @todo: need review
@@ -45274,6 +46147,14 @@ ${e.message}
45274
46147
  To: {
45275
46148
  description: "Action arrow / mapping (`a \\to b`) \u2014 opaque typed head.",
45276
46149
  signature: "(any, any) -> nothing"
46150
+ },
46151
+ Colon: {
46152
+ description: "Type annotation (`a : b`) \u2014 opaque typed head.",
46153
+ signature: "(any, any) -> expression"
46154
+ },
46155
+ Prime: {
46156
+ description: "Derivative or prime notation (`f'`, `f^{(n)}`) \u2014 opaque typed head until a derivative library handler runs.",
46157
+ signature: "(any, integer?) -> expression"
45277
46158
  }
45278
46159
  }
45279
46160
  ];
@@ -50314,18 +51195,28 @@ ${e.message}
50314
51195
  },
50315
51196
  {
50316
51197
  Sample: {
50317
- description: "Return a random sample of k elements from the collection, without replacement.",
51198
+ description: "Return a random sample of k elements from the collection, without replacement. With an optional `seed` argument, the sample is deterministic.",
50318
51199
  complexity: 8200,
50319
- signature: "(collection, integer) -> list",
50320
- evaluate: ([xs, nArg], { engine: ce }) => {
51200
+ signature: "(collection, integer, real?) -> list",
51201
+ evaluate: ([xs, nArg, seedArg], { engine: ce }) => {
50321
51202
  if (!xs.isFiniteCollection) return void 0;
50322
51203
  const k = toInteger(nArg);
50323
51204
  if (k === null || k < 0) return void 0;
50324
51205
  const data = Array.from(xs.each());
50325
51206
  if (k > data.length) return void 0;
50326
- for (let i = data.length - 1; i > 0; i--) {
50327
- const j = Math.floor(Math.random() * (i + 1));
50328
- [data[i], data[j]] = [data[j], data[i]];
51207
+ const seed = seedArg?.re;
51208
+ if (seed !== void 0 && !Number.isNaN(seed)) {
51209
+ let s = seed;
51210
+ for (let i = data.length - 1; i > 0; i--) {
51211
+ const j = Math.floor(deterministicRandom(s) * (i + 1));
51212
+ [data[i], data[j]] = [data[j], data[i]];
51213
+ s = nextSeed(s);
51214
+ }
51215
+ } else {
51216
+ for (let i = data.length - 1; i > 0; i--) {
51217
+ const j = Math.floor(Math.random() * (i + 1));
51218
+ [data[i], data[j]] = [data[j], data[i]];
51219
+ }
50329
51220
  }
50330
51221
  const sample = data.slice(0, k);
50331
51222
  return ce.function("List", sample);
@@ -53146,6 +54037,20 @@ Error in definition of "${name}"`,
53146
54037
  if (results.length === 1) return results[0];
53147
54038
  return this.engine._fn("List", results);
53148
54039
  }
54040
+ if (def instanceof _BoxedOperatorDefinition && def._isLambda && this.ops.some((x) => isFiniteIndexedCollection(x)) && paramsAreScalar(def)) {
54041
+ const items = zip(this._ops);
54042
+ if (items) {
54043
+ const results = [];
54044
+ while (true) {
54045
+ const { done, value } = items.next();
54046
+ if (done) break;
54047
+ results.push(
54048
+ this.engine._fn(this.operator, value).evaluate(options)
54049
+ );
54050
+ }
54051
+ return this.engine._fn("List", results);
54052
+ }
54053
+ }
53149
54054
  if (materialization !== false && !def.evaluate && this.isLazyCollection)
53150
54055
  return materialize(this, def, options);
53151
54056
  const tail = holdMap(this, (x) => x.evaluate(options));
@@ -53192,6 +54097,22 @@ Error in definition of "${name}"`,
53192
54097
  (resolved) => this.engine._fn("List", resolved)
53193
54098
  );
53194
54099
  }
54100
+ if (def instanceof _BoxedOperatorDefinition && def._isLambda && this.ops.some((x) => isFiniteIndexedCollection(x)) && paramsAreScalar(def)) {
54101
+ const items = zip(this._ops);
54102
+ if (items) {
54103
+ const results = [];
54104
+ while (true) {
54105
+ const { done, value } = items.next();
54106
+ if (done) break;
54107
+ results.push(
54108
+ this.engine._fn(this.operator, value).evaluateAsync(options)
54109
+ );
54110
+ }
54111
+ return Promise.all(results).then(
54112
+ (resolved) => this.engine._fn("List", resolved)
54113
+ );
54114
+ }
54115
+ }
53195
54116
  const tail = await holdMapAsync(
53196
54117
  this,
53197
54118
  async (x) => await x.evaluateAsync(options)
@@ -53406,8 +54327,47 @@ Error in definition of "${name}"`,
53406
54327
  const ops = expr2.ops.map((x) => x.evaluate(options));
53407
54328
  if (!value || value.type.isUnknown)
53408
54329
  return expr2.engine.function(expr2.operator, ops);
54330
+ if (ops.some((x) => isFiniteIndexedCollection(x)) && paramsAreScalar(value.type.type)) {
54331
+ const items = zip(ops);
54332
+ if (items) {
54333
+ const results = [];
54334
+ while (true) {
54335
+ const { done, value: zipped } = items.next();
54336
+ if (done) break;
54337
+ results.push(apply(value, zipped).evaluate(options));
54338
+ }
54339
+ return expr2.engine._fn("List", results);
54340
+ }
54341
+ }
53409
54342
  return apply(value, ops);
53410
54343
  }
54344
+ function paramsAreScalar(source) {
54345
+ const sigType = isOperatorDefinition(source) ? source.signature?.type : source;
54346
+ if (!sigType || typeof sigType === "string") return true;
54347
+ if (sigType.kind !== "signature") return true;
54348
+ const args = [
54349
+ ...sigType.args ?? [],
54350
+ ...sigType.optArgs ?? [],
54351
+ ...sigType.variadicArg ? [sigType.variadicArg] : []
54352
+ ];
54353
+ return args.every((arg) => isScalarType(arg.type));
54354
+ }
54355
+ function isOperatorDefinition(source) {
54356
+ return typeof source === "object" && source !== null && "signature" in source;
54357
+ }
54358
+ function isScalarType(t) {
54359
+ if (typeof t === "string") {
54360
+ if (t === "collection" || t === "indexed_collection" || t === "list" || t === "tuple" || t === "set" || t === "dictionary" || t === "record" || t === "function")
54361
+ return false;
54362
+ return true;
54363
+ }
54364
+ 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")
54365
+ return false;
54366
+ if (t.kind === "union" || t.kind === "intersection")
54367
+ return t.types.every((x) => isScalarType(x));
54368
+ if (t.kind === "negation") return isScalarType(t.type);
54369
+ return true;
54370
+ }
53411
54371
  function materialize(expr2, def, options) {
53412
54372
  if (!expr2.isValid || options?.materialization === false) return expr2;
53413
54373
  let materialization = options?.materialization ?? false;
@@ -53415,6 +54375,11 @@ Error in definition of "${name}"`,
53415
54375
  materialization = DEFAULT_MATERIALIZATION;
53416
54376
  const isIndexed = expr2.isIndexedCollection;
53417
54377
  const isFinite2 = expr2.isFiniteCollection;
54378
+ if (isIndexed && isFinite2) {
54379
+ const count = expr2.count;
54380
+ if (count !== void 0 && count > expr2.engine.maxCollectionSize)
54381
+ return expr2;
54382
+ }
53418
54383
  const xs = [];
53419
54384
  if (!expr2.isEmptyCollection) {
53420
54385
  if (!isIndexed || !isFinite2) {
@@ -53642,7 +54607,7 @@ Error in definition of "${name}"`,
53642
54607
  const eltType = widen(
53643
54608
  ...Object.values(this._keyValues).map((op) => op.type.type)
53644
54609
  );
53645
- this._type = this.engine.type(`dictionary<${eltType}>`);
54610
+ this._type = new BoxedType({ kind: "dictionary", values: eltType });
53646
54611
  return this._type;
53647
54612
  }
53648
54613
  get isPure() {
@@ -53920,6 +54885,7 @@ Error in definition of "${name}"`,
53920
54885
  }
53921
54886
  if (typeof expr2 === "number" || expr2 instanceof BigDecimal || expr2 instanceof Complex)
53922
54887
  return ce.number(expr2);
54888
+ if (typeof expr2 === "boolean") return ce.symbol(expr2 ? "True" : "False");
53923
54889
  if (typeof expr2 === "string") {
53924
54890
  if (matchesSymbol(expr2)) {
53925
54891
  const sym2 = symbol(expr2);
@@ -54976,6 +55942,23 @@ Error in definition of "${name}"`,
54976
55942
  };
54977
55943
  return compilePair(0);
54978
55944
  }
55945
+ if (h === "When") {
55946
+ if (args.length !== 2)
55947
+ throw new Error("When: expected exactly 2 arguments (expr, cond)");
55948
+ const fn2 = target.functions?.(h);
55949
+ if (fn2) {
55950
+ if (typeof fn2 === "function") {
55951
+ return fn2(args, (expr2) => _BaseCompiler.compile(expr2, target), target);
55952
+ }
55953
+ return `${fn2}(${args.map((x) => _BaseCompiler.compile(x, target)).join(", ")})`;
55954
+ }
55955
+ if (isSymbol2(args[1], "True"))
55956
+ return `(${_BaseCompiler.compile(args[0], target)})`;
55957
+ if (isSymbol2(args[1], "False")) return "NaN";
55958
+ const val = _BaseCompiler.compile(args[0], target);
55959
+ const cond = _BaseCompiler.compile(args[1], target);
55960
+ return `((${cond}) ? (${val}) : NaN)`;
55961
+ }
54979
55962
  if (h === "Block") {
54980
55963
  return _BaseCompiler.compileBlock(args, target);
54981
55964
  }
@@ -55050,17 +56033,98 @@ Error in definition of "${name}"`,
55050
56033
  )}${target.ws("\n")}})()`;
55051
56034
  }
55052
56035
  /**
55053
- * Compile a Loop expression with Element(index, Range(lo, hi)) indexing.
55054
- * Generates: (() => { for (let i = lo; i <= hi; i++) { body } })()
56036
+ * Compile a Loop expression.
56037
+ *
56038
+ * Two forms are supported:
56039
+ *
56040
+ * 1. **Imperative / single-Element form** (existing behaviour):
56041
+ * `Loop(body, Element(i, Range(lo, hi)))`
56042
+ * Generates a raw `for (let i = lo; i <= hi; i++) { body }` loop wrapped
56043
+ * in an IIFE. The loop counter is always a plain number. For targets
56044
+ * that wrap numeric values (e.g. interval-js uses `_IA.point()`),
56045
+ * references to the loop index inside the body are re-wrapped via
56046
+ * `target.number`. `break` / `continue` / `return` are preserved.
55055
56047
  *
55056
- * The loop counter is always a raw number. For targets that wrap numeric
55057
- * values (e.g. interval-js wraps with `_IA.point()`), references to the
55058
- * loop index inside the body are wrapped via `target.number`.
56048
+ * 2. **Comprehension / variadic-Element form** (new):
56049
+ * `Loop(body, Element(x, coll1), Element(y, coll2), )`
56050
+ * When two or more `Element` clauses are present or when the single
56051
+ * Element's collection is not a `Range` — the loop is compiled as a
56052
+ * comprehension that collects results into an array. Each clause
56053
+ * produces a `for (const name of collection)` loop, nested
56054
+ * outermost-to-innermost, and the innermost body pushes into `result`.
56055
+ *
56056
+ * Example output (JS):
56057
+ * ```js
56058
+ * (() => { const result = [];
56059
+ * for (const x of [1,2]) { for (const y of [3,4]) { result.push(body); } }
56060
+ * return result; })()
56061
+ * ```
56062
+ *
56063
+ * GLSL: multi-Element comprehension is not trivially representable in
56064
+ * GLSL (no dynamic arrays, no push). A compile-time error is thrown.
56065
+ * TODO(E3-GLSL): support GLSL multi-Element via a pre-declared fixed-size
56066
+ * array or by unrolling when bounds are known at compile time.
56067
+ *
56068
+ * Known issue (imperative form): the IIFE generated by form (1) has no
56069
+ * `return` statement, so `Loop(body, Element(i, Range(lo, hi)))` compiled
56070
+ * to JS evaluates to `undefined` at runtime, while CE evaluation returns a
56071
+ * `List` of body values. See `test/compute-engine/a1-c1-compile-parity.test.ts`
56072
+ * ("Loop compiles in JS") for the verify-only test that locks in the
56073
+ * current behavior.
55059
56074
  */
55060
56075
  static compileForLoop(args, target) {
55061
56076
  if (!args[0]) throw new Error("Loop: no body");
55062
56077
  if (!args[1]) throw new Error("Loop: no indexing set");
55063
- const indexing = args[1];
56078
+ const body = args[0];
56079
+ const elements = args.slice(1);
56080
+ const useComprehension = elements.length > 1 || elements.length === 1 && isFunction2(elements[0], "Element") && !_BaseCompiler.isLegacyCompatibleRange(elements[0].ops[1]);
56081
+ if (useComprehension) {
56082
+ const lang = target.language ?? "";
56083
+ if (lang === "glsl" || lang === "wgsl") {
56084
+ throw new Error(
56085
+ `${lang.toUpperCase()}: multi-Element Loop comprehension is not yet supported. TODO(E3-GLSL): unroll or use a fixed-size array.`
56086
+ );
56087
+ }
56088
+ const narrowedElements = [];
56089
+ for (let i = 0; i < elements.length; i++) {
56090
+ const elem = elements[i];
56091
+ if (!isFunction2(elem, "Element"))
56092
+ throw new Error(
56093
+ `Loop: argument ${i + 1} must be an Element clause, got ${elem.operator ?? "?"}`
56094
+ );
56095
+ if (!isSymbol2(elem.ops[0]))
56096
+ throw new Error(
56097
+ `Loop: Element index (argument ${i + 1}) must be a symbol`
56098
+ );
56099
+ narrowedElements.push(elem);
56100
+ }
56101
+ const loopVarSet = new Set(
56102
+ narrowedElements.map(
56103
+ (e) => e.ops[0].symbol
56104
+ )
56105
+ );
56106
+ const needsWrap2 = target.number(0) !== "0";
56107
+ const bodyTarget2 = needsWrap2 ? {
56108
+ ...target,
56109
+ var: (id) => loopVarSet.has(id) ? target.number(0).replace("0", id) : target.var(id)
56110
+ } : target;
56111
+ const bodyCode = _BaseCompiler.compile(body, bodyTarget2);
56112
+ let inner = `result.push(${bodyCode});`;
56113
+ for (let i = narrowedElements.length - 1; i >= 0; i--) {
56114
+ const elem = narrowedElements[i];
56115
+ const name = elem.ops[0].symbol;
56116
+ const collExpr = elem.ops[1];
56117
+ let collection;
56118
+ if (isFunction2(collExpr, "Range")) {
56119
+ collection = _BaseCompiler.compileRangeIterable(collExpr, bodyTarget2);
56120
+ } else {
56121
+ collection = _BaseCompiler.compile(collExpr, bodyTarget2);
56122
+ }
56123
+ inner = `for (const ${name} of ${collection}) { ${inner} }`;
56124
+ }
56125
+ return `(() => { const result = []; ${inner} return result; })()`;
56126
+ }
56127
+ const indexing = elements[0];
55064
56128
  if (!isFunction2(indexing, "Element"))
55065
56129
  throw new Error("Loop: expected Element(index, Range(lo, hi))");
55066
56130
  const indexExpr = indexing.ops[0];
@@ -55078,13 +56142,72 @@ Error in definition of "${name}"`,
55078
56142
  ...target,
55079
56143
  var: (id) => id === index ? needsWrap ? target.number(0).replace("0", index) : index : target.var(id)
55080
56144
  };
55081
- const bodyStmts = _BaseCompiler.compileLoopBody(args[0], bodyTarget);
56145
+ const bodyStmts = _BaseCompiler.compileLoopBody(body, bodyTarget);
55082
56146
  return `(() => {${target.ws(
55083
56147
  "\n"
55084
56148
  )}for (let ${index} = ${lower}; ${index} <= ${upper}; ${index}++) {${target.ws(
55085
56149
  "\n"
55086
56150
  )}${bodyStmts}${target.ws("\n")}}${target.ws("\n")}})()`;
55087
56151
  }
56152
+ /**
56153
+ * Returns `true` when the given collection expression is a `Range` whose
56154
+ * runtime semantics match the legacy imperative for-loop shape
56155
+ * `for (let i = lo; i <= hi; i++)`.
56156
+ *
56157
+ * Concretely: integer-ascending bounds and step omitted-or-1. When bounds
56158
+ * are not statically numeric we accept the Range (the historical
56159
+ * behaviour) — runtime mismatch in the descending-unknown-bounds case is
56160
+ * left as a known limitation; callers can force the iterable path by
56161
+ * supplying an explicit step.
56162
+ */
56163
+ static isLegacyCompatibleRange(coll) {
56164
+ if (!isFunction2(coll, "Range")) return false;
56165
+ if (coll.ops.length >= 3) {
56166
+ const stepExpr = coll.ops[2];
56167
+ if (!isNumber(stepExpr) || stepExpr.re !== 1) return false;
56168
+ }
56169
+ const lo = coll.ops[0];
56170
+ const hi = coll.ops[1];
56171
+ if (isNumber(lo) && !Number.isInteger(lo.re)) return false;
56172
+ if (isNumber(hi) && !Number.isInteger(hi.re)) return false;
56173
+ if (isNumber(lo) && isNumber(hi) && lo.re > hi.re) return false;
56174
+ return true;
56175
+ }
56176
+ /**
56177
+ * Compile a `Range(lo, hi)` or `Range(lo, hi, step)` expression into a JS
56178
+ * iterable expression. Mirrors the runtime semantics in
56179
+ * `library/collections.ts` Range:
56180
+ * count = step === 0 ? 0 : max(0, floor((hi - lo) / step) + 1)
56181
+ * element = lo + step * k (0-indexed)
56182
+ * Default step is 1 when omitted. Bounds and step may be fractional.
56183
+ *
56184
+ * Only used from the comprehension path in `compileForLoop`.
56185
+ * Caller must have already verified `isFunction(rangeExpr, 'Range')`.
56186
+ */
56187
+ static compileRangeIterable(rangeExpr, target) {
56188
+ const loExpr = rangeExpr.ops[0];
56189
+ const hiExpr = rangeExpr.ops[1];
56190
+ const stepExpr = rangeExpr.ops[2];
56191
+ if (isNumber(loExpr) && isNumber(hiExpr) && (stepExpr === void 0 || isNumber(stepExpr))) {
56192
+ const lo2 = loExpr.re;
56193
+ const hi2 = hiExpr.re;
56194
+ const step2 = stepExpr === void 0 ? hi2 >= lo2 ? 1 : -1 : stepExpr.re;
56195
+ if (step2 === 0) return "[]";
56196
+ const len = Math.max(0, Math.floor((hi2 - lo2) / step2) + 1);
56197
+ if (step2 === 1) {
56198
+ if (lo2 === 0) return `Array.from({length:${len}},(_,k)=>k)`;
56199
+ return `Array.from({length:${len}},(_,k)=>${lo2}+k)`;
56200
+ }
56201
+ return `Array.from({length:${len}},(_,k)=>${lo2}+(${step2})*k)`;
56202
+ }
56203
+ const lo = _BaseCompiler.compile(loExpr, target);
56204
+ const hi = _BaseCompiler.compile(hiExpr, target);
56205
+ if (stepExpr === void 0) {
56206
+ 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})`;
56207
+ }
56208
+ const step = _BaseCompiler.compile(stepExpr, target);
56209
+ 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})`;
56210
+ }
55088
56211
  /**
55089
56212
  * Compile a loop body expression as statements (not wrapped in IIFE).
55090
56213
  * Handles Break, Continue, Return as statements, and If as if-else when
@@ -59584,8 +60707,8 @@ Error in definition of "${name}"`,
59584
60707
  }
59585
60708
  if (effectiveOp === "greater" || effectiveOp === "greaterEqual") {
59586
60709
  const isStrict = effectiveOp === "greater";
59587
- if (bounds.lowerBound !== void 0) {
59588
- const lowerVal = isNumber(bounds.lowerBound) ? bounds.lowerBound.numericValue : void 0;
60710
+ if (bounds.lower !== void 0) {
60711
+ const lowerVal = isNumber(bounds.lower) ? bounds.lower.numericValue : void 0;
59589
60712
  if (typeof lowerVal === "number" && isFinite(lowerVal)) {
59590
60713
  if (isStrict) {
59591
60714
  if (lowerVal > k) return "tautology";
@@ -59597,8 +60720,8 @@ Error in definition of "${name}"`,
59597
60720
  }
59598
60721
  }
59599
60722
  }
59600
- if (bounds.upperBound !== void 0) {
59601
- const upperVal = isNumber(bounds.upperBound) ? bounds.upperBound.numericValue : void 0;
60723
+ if (bounds.upper !== void 0) {
60724
+ const upperVal = isNumber(bounds.upper) ? bounds.upper.numericValue : void 0;
59602
60725
  if (typeof upperVal === "number" && isFinite(upperVal)) {
59603
60726
  if (isStrict) {
59604
60727
  if (upperVal < k) return "contradiction";
@@ -59613,8 +60736,8 @@ Error in definition of "${name}"`,
59613
60736
  }
59614
60737
  } else {
59615
60738
  const isStrict = effectiveOp === "less";
59616
- if (bounds.upperBound !== void 0) {
59617
- const upperVal = isNumber(bounds.upperBound) ? bounds.upperBound.numericValue : void 0;
60739
+ if (bounds.upper !== void 0) {
60740
+ const upperVal = isNumber(bounds.upper) ? bounds.upper.numericValue : void 0;
59618
60741
  if (typeof upperVal === "number" && isFinite(upperVal)) {
59619
60742
  if (isStrict) {
59620
60743
  if (upperVal < k) return "tautology";
@@ -59625,8 +60748,8 @@ Error in definition of "${name}"`,
59625
60748
  }
59626
60749
  }
59627
60750
  }
59628
- if (bounds.lowerBound !== void 0) {
59629
- const lowerVal = isNumber(bounds.lowerBound) ? bounds.lowerBound.numericValue : void 0;
60751
+ if (bounds.lower !== void 0) {
60752
+ const lowerVal = isNumber(bounds.lower) ? bounds.lower.numericValue : void 0;
59630
60753
  if (typeof lowerVal === "number" && isFinite(lowerVal)) {
59631
60754
  if (isStrict) {
59632
60755
  if (lowerVal > k) return "contradiction";
@@ -59854,7 +60977,7 @@ Error in definition of "${name}"`,
59854
60977
  const patOp1B2 = pat.op1;
59855
60978
  if (isSymbol2(patOp1B2)) {
59856
60979
  const bounds = getInequalityBoundsFromAssumptions(ce, patOp1B2.symbol);
59857
- const bound = isLower ? bounds.lowerBound : bounds.upperBound;
60980
+ const bound = isLower ? bounds.lower : bounds.upper;
59858
60981
  const strictOk = isLower ? bounds.lowerStrict : bounds.upperStrict;
59859
60982
  if (bound !== void 0 && (!isStrict || strictOk === true))
59860
60983
  pushResult({ [boundWildcard]: bound });
@@ -59864,7 +60987,7 @@ Error in definition of "${name}"`,
59864
60987
  if (symbolWildcard && !symbolWildcard.startsWith("__")) {
59865
60988
  for (const s of candidatesFromAssumptions()) {
59866
60989
  const bounds = getInequalityBoundsFromAssumptions(ce, s);
59867
- const bound = isLower ? bounds.lowerBound : bounds.upperBound;
60990
+ const bound = isLower ? bounds.lower : bounds.upper;
59868
60991
  const strictOk = isLower ? bounds.lowerStrict : bounds.upperStrict;
59869
60992
  if (bound === void 0 || isStrict && strictOk !== true)
59870
60993
  continue;
@@ -60896,6 +62019,7 @@ Error in definition of "${name}"`,
60896
62019
  return `_SYS.cexp(${compile3(args[0])})`;
60897
62020
  return `Math.exp(${compile3(args[0])})`;
60898
62021
  },
62022
+ First: (args, compile3) => `${compile3(args[0])}[0]`,
60899
62023
  Floor: (args, compile3) => {
60900
62024
  if (BaseCompiler.isIntegerValued(args[0])) return compile3(args[0]);
60901
62025
  return `Math.floor(${compile3(args[0])})`;
@@ -61054,7 +62178,20 @@ Error in definition of "${name}"`,
61054
62178
  if (nConst !== void 0) return `Math.pow(${compile3(arg)}, ${1 / nConst})`;
61055
62179
  return `Math.pow(${compile3(arg)}, 1 / (${compile3(exp3)}))`;
61056
62180
  },
61057
- Random: "Math.random",
62181
+ Random: (args, compile3) => {
62182
+ if (args.length === 0) return "Math.random()";
62183
+ if (args.length === 2) {
62184
+ const m = compile3(args[0]);
62185
+ const n = compile3(args[1]);
62186
+ return `((${m}) + Math.floor(Math.random() * ((${n}) - (${m}))))`;
62187
+ }
62188
+ const arg = args[0];
62189
+ if (BaseCompiler.isIntegerValued(arg)) {
62190
+ return `Math.floor(Math.random() * (${compile3(arg)}))`;
62191
+ }
62192
+ const a = compile3(arg);
62193
+ return `(() => { const _s = (${a}) * 12.9898; const _v = Math.sin(_s) * 43758.5453; return _v - Math.floor(_v); })()`;
62194
+ },
61058
62195
  Round: (args, compile3) => {
61059
62196
  if (BaseCompiler.isIntegerValued(args[0])) return compile3(args[0]);
61060
62197
  return `Math.round(${compile3(args[0])})`;
@@ -61082,6 +62219,7 @@ Error in definition of "${name}"`,
61082
62219
  if (BaseCompiler.isComplexValued(arg)) return `_SYS.csech(${compile3(arg)})`;
61083
62220
  return `1 / Math.cosh(${compile3(arg)})`;
61084
62221
  },
62222
+ Second: (args, compile3) => `${compile3(args[0])}[1]`,
61085
62223
  Heaviside: "_SYS.heaviside",
61086
62224
  Sign: "Math.sign",
61087
62225
  Sinc: "_SYS.sinc",
@@ -61114,6 +62252,7 @@ Error in definition of "${name}"`,
61114
62252
  return `_SYS.ctanh(${compile3(args[0])})`;
61115
62253
  return `Math.tanh(${compile3(args[0])})`;
61116
62254
  },
62255
+ Third: (args, compile3) => `${compile3(args[0])}[2]`,
61117
62256
  Mod: ([a, b], compile3) => {
61118
62257
  if (a === null || b === null) throw new Error("Mod: missing argument");
61119
62258
  const ca = compile3(a);
@@ -62393,6 +63532,14 @@ Error in definition of "${name}"`,
62393
63532
  return `exp(${compile3(args[0])})`;
62394
63533
  },
62395
63534
  Exp2: "exp2",
63535
+ // Component access — assumes the argument compiles to a vec2/vec3/vec4
63536
+ // (the common case for 2D/3D points). For 5+-element tuples that compile
63537
+ // to `float[N]` arrays, swizzle access is invalid GLSL and the shader
63538
+ // will fail to compile; that's an edge case `First`/`Second`/`Third`
63539
+ // aren't designed for. Vec swizzles are identical between GLSL and WGSL.
63540
+ First: (args, compile3) => `${compile3(args[0])}.x`,
63541
+ Second: (args, compile3) => `${compile3(args[0])}.y`,
63542
+ Third: (args, compile3) => `${compile3(args[0])}.z`,
62396
63543
  Floor: (args, compile3) => {
62397
63544
  if (BaseCompiler.isIntegerValued(args[0])) return compile3(args[0]);
62398
63545
  return `floor(${compile3(args[0])})`;
@@ -62870,6 +64017,39 @@ Error in definition of "${name}"`,
62870
64017
  // Sum/Product — unrolled or for-loop
62871
64018
  Sum: (args, compile3, target) => compileGPUSumProduct("Sum", args, compile3, target),
62872
64019
  Product: (args, compile3, target) => compileGPUSumProduct("Product", args, compile3, target),
64020
+ // Range — inline constant array literal (bounds must be compile-time constants)
64021
+ Range: (args, _compile2, target) => {
64022
+ if (args.length < 2 || args.length > 3) {
64023
+ throw new Error(
64024
+ "Range: GPU compile expects 2 or 3 arguments (lo, hi, step?)"
64025
+ );
64026
+ }
64027
+ const lo = args[0].re;
64028
+ const hi = args[1].re;
64029
+ const step = args.length === 3 ? args[2].re : 1;
64030
+ if (!Number.isFinite(lo) || !Number.isFinite(hi) || !Number.isFinite(step)) {
64031
+ throw new Error(
64032
+ "Range: GPU compile requires constant numeric bounds (non-constant ranges must be materialized at JS host then uploaded as a uniform)"
64033
+ );
64034
+ }
64035
+ if (step === 0) throw new Error("Range: step cannot be zero");
64036
+ const count = Math.max(0, Math.floor((hi - lo) / step) + 1);
64037
+ if (count === 0) {
64038
+ throw new Error(
64039
+ "Range: empty range (lo > hi for positive step, or lo < hi for negative step)"
64040
+ );
64041
+ }
64042
+ if (count > 256) {
64043
+ throw new Error(
64044
+ `Range: GPU compile inlines ranges up to 256 elements (got ${count})`
64045
+ );
64046
+ }
64047
+ const values = [];
64048
+ for (let i = 0; i < count; i++) values.push(lo + i * step);
64049
+ const isWGSL = target.language === "wgsl";
64050
+ const arrayType = isWGSL ? `array<f32, ${count}>` : `float[${count}]`;
64051
+ return `${arrayType}(${values.map(formatGPUNumber).join(", ")})`;
64052
+ },
62873
64053
  // Loop — GPU for-loop (no IIFE, no let)
62874
64054
  Loop: (args, _compile2, target) => {
62875
64055
  if (!args[0]) throw new Error("Loop: no body");
@@ -62898,6 +64078,134 @@ Error in definition of "${name}"`,
62898
64078
  ${bodyCode};
62899
64079
  }`;
62900
64080
  },
64081
+ // Statistical functions
64082
+ /**
64083
+ * GCD of two scalar arguments.
64084
+ *
64085
+ * Uses a preamble helper `_gpu_gcd` (Euclidean algorithm via `mod`).
64086
+ * Only two-argument form is supported in GPU targets.
64087
+ */
64088
+ GCD: (args, compile3) => {
64089
+ if (args.length < 2) throw new Error("GCD: need at least two arguments");
64090
+ if (args.length > 2)
64091
+ throw new Error("GCD: GPU target supports only two-argument GCD");
64092
+ const a = args[0];
64093
+ const b = args[1];
64094
+ if (a === null || b === null) throw new Error("GCD: missing argument");
64095
+ return `_gpu_gcd(${compile3(a)}, ${compile3(b)})`;
64096
+ },
64097
+ /**
64098
+ * Variance of a compile-time-known list.
64099
+ *
64100
+ * Accepts either a single `List(...)` argument or N scalar arguments.
64101
+ * Generates fully inline code: computes mean then sum of squared deviations,
64102
+ * divided by (N-1) for sample variance (matches JS `_SYS.variance`).
64103
+ */
64104
+ Variance: (args, compile3) => {
64105
+ let elems;
64106
+ if (args.length === 1 && isFunction2(args[0], "List")) {
64107
+ elems = args[0].ops;
64108
+ } else if (args.length >= 2) {
64109
+ elems = args;
64110
+ } else {
64111
+ throw new Error(
64112
+ "Variance: GPU target requires a List argument or at least 2 scalar arguments"
64113
+ );
64114
+ }
64115
+ const n = elems.length;
64116
+ if (n < 2) throw new Error("Variance: need at least 2 elements");
64117
+ const compiled = elems.map((e) => compile3(e));
64118
+ const sum = compiled.join(" + ");
64119
+ const mean2 = `((${sum}) / ${formatGPUNumber(n)})`;
64120
+ const sqDiffs = compiled.map((c) => `(${c} - ${mean2}) * (${c} - ${mean2})`).join(" + ");
64121
+ return `((${sqDiffs}) / ${formatGPUNumber(n - 1)})`;
64122
+ },
64123
+ /**
64124
+ * Median of a compile-time-known list.
64125
+ *
64126
+ * Accepts either a single `List(...)` argument or N scalar arguments.
64127
+ * For N ≤ 8: generates a fully unrolled inline sorting network followed by
64128
+ * a middle-element pick. For larger N, throws (too large to inline cleanly).
64129
+ *
64130
+ * The sorting network uses the "odd-even merge sort" comparator pattern
64131
+ * inlined as `min`/`max` calls — no GPU statements required.
64132
+ */
64133
+ Median: (args, compile3) => {
64134
+ let elems;
64135
+ if (args.length === 1 && isFunction2(args[0], "List")) {
64136
+ elems = args[0].ops;
64137
+ } else if (args.length >= 1) {
64138
+ elems = args;
64139
+ } else {
64140
+ throw new Error(
64141
+ "Median: GPU target requires a List argument or at least 1 scalar argument"
64142
+ );
64143
+ }
64144
+ const n = elems.length;
64145
+ if (n === 0) throw new Error("Median: empty list");
64146
+ if (n > 8) {
64147
+ throw new Error(
64148
+ `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.`
64149
+ );
64150
+ }
64151
+ const compiled = elems.map((e) => compile3(e));
64152
+ if (n === 1) return compiled[0];
64153
+ return `_gpu_median_${n}(${compiled.join(", ")})`;
64154
+ },
64155
+ /**
64156
+ * Deterministic pseudorandom for GPU.
64157
+ *
64158
+ * All emitted forms return a GLSL `float` (or WGSL `f32`) so the result
64159
+ * composes with surrounding float arithmetic without explicit casts. The
64160
+ * "integer-bound" forms return an integer-valued float (the result of
64161
+ * `floor`), matching the convention used by `Floor` and other ostensibly
64162
+ * integer-returning operators in this target.
64163
+ *
64164
+ * - 0 args (GLSL only): fall back to a fragment-coord-derived seed.
64165
+ * Only meaningful in fragment shaders (gl_FragCoord is FS-only).
64166
+ * - 0 args (WGSL): throws — WGSL has no built-in fragment coordinate;
64167
+ * caller must provide an explicit seed.
64168
+ * - 1 arg, real-typed: `_gpu_random(seed)` — deterministic float in [0, 1)
64169
+ * - 1 arg, integer-typed: `floor(_gpu_random(float(n)) * float(n))` —
64170
+ * integer-valued float in {0, 1, ..., n-1}. The seed is derived from
64171
+ * `n` itself, so the result is per-pixel-and-n deterministic in GLSL.
64172
+ * - 2 args (integer m, n): float in [m, n), seeded from gl_FragCoord.
64173
+ *
64174
+ * JS-side `Random` has matching semantics (see `library/core.ts`'s
64175
+ * polymorphic dispatch). JS↔GLSL parity is approximate — same seed yields
64176
+ * a similar value, not bit-identical, due to fp64 vs fp32 and platform
64177
+ * `sin` differences.
64178
+ */
64179
+ Random: (args, compile3, target) => {
64180
+ if (args.length === 0) {
64181
+ if (target.language === "wgsl") {
64182
+ throw new Error(
64183
+ "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."
64184
+ );
64185
+ }
64186
+ return "_gpu_random(gl_FragCoord.x + gl_FragCoord.y * 1024.0)";
64187
+ }
64188
+ if (args.length === 1) {
64189
+ const arg = args[0];
64190
+ if (BaseCompiler.isIntegerValued(arg)) {
64191
+ const compiled = compile3(arg);
64192
+ return `floor(_gpu_random(float(${compiled})) * float(${compiled}))`;
64193
+ }
64194
+ return `_gpu_random(${compile3(arg)})`;
64195
+ }
64196
+ if (args.length === 2) {
64197
+ if (target.language === "wgsl") {
64198
+ throw new Error(
64199
+ "Random(m, n): WGSL compile requires explicit seeding. Use a seeded variant or compute the integer range manually."
64200
+ );
64201
+ }
64202
+ const m = compile3(args[0]);
64203
+ const n = compile3(args[1]);
64204
+ const seed = "_gpu_random(gl_FragCoord.x + gl_FragCoord.y * 1024.0)";
64205
+ return `(float(${m}) + floor(${seed} * float((${n}) - (${m}))))`;
64206
+ }
64207
+ throw new Error("Random: GPU compile expects 0, 1, or 2 arguments");
64208
+ },
62901
64209
  // Function (lambda) — not supported in GPU
62902
64210
  Function: () => {
62903
64211
  throw new Error(
@@ -63496,6 +64804,212 @@ fn _fractal_julia(z_in: vec2f, c: vec2f, maxIter: i32) -> f32 {
63496
64804
  }
63497
64805
  return 1.0;
63498
64806
  }
64807
+ `;
64808
+ var GPU_GCD_PREAMBLE_GLSL = `
64809
+ float _gpu_gcd(float a, float b) {
64810
+ a = abs(a); b = abs(b);
64811
+ for (int i = 0; i < 32; i++) {
64812
+ if (b < 0.5) break;
64813
+ float t = mod(a, b);
64814
+ a = b;
64815
+ b = t;
64816
+ }
64817
+ return a;
64818
+ }
64819
+ `;
64820
+ var GPU_GCD_PREAMBLE_WGSL = `
64821
+ fn _gpu_gcd(a_in: f32, b_in: f32) -> f32 {
64822
+ var a = abs(a_in); var b = abs(b_in);
64823
+ for (var i: i32 = 0; i < 32; i++) {
64824
+ if (b < 0.5) { break; }
64825
+ let t = a % b;
64826
+ a = b;
64827
+ b = t;
64828
+ }
64829
+ return a;
64830
+ }
64831
+ `;
64832
+ var GPU_RANDOM_PREAMBLE_GLSL = `
64833
+ // Deterministic pseudorandom in [0, 1) from a float seed.
64834
+ // Standard fract-sin hash; reproducible across runs for the same seed.
64835
+ // Note: this hash exhibits visible banding near seed \u2248 k\u03C0 for integer k.
64836
+ // For high-quality shader random, callers should use a more robust hash
64837
+ // (e.g. PCG or xxHash) and pre-seed it appropriately.
64838
+ float _gpu_random(float seed) {
64839
+ return fract(sin(seed * 12.9898) * 43758.5453);
64840
+ }
64841
+ `;
64842
+ var GPU_RANDOM_PREAMBLE_WGSL = `
64843
+ // Deterministic pseudorandom in [0, 1) from a float seed.
64844
+ // Standard fract-sin hash; reproducible across runs for the same seed.
64845
+ // Note: this hash exhibits visible banding near seed \u2248 k\u03C0 for integer k.
64846
+ // For high-quality shader random, callers should use a more robust hash
64847
+ // (e.g. PCG or xxHash) and pre-seed it appropriately.
64848
+ fn _gpu_random(seed: f32) -> f32 {
64849
+ return fract(sin(seed * 12.9898) * 43758.5453);
64850
+ }
64851
+ `;
64852
+ var GPU_MEDIAN_PREAMBLE_GLSL = `
64853
+ float _gpu_median_2(float a, float b) {
64854
+ return (a + b) * 0.5;
64855
+ }
64856
+ float _gpu_median_3(float a, float b, float c) {
64857
+ return max(min(a, b), min(max(a, b), c));
64858
+ }
64859
+ float _gpu_median_4(float a, float b, float c, float d) {
64860
+ float lo = max(min(a, b), min(c, d));
64861
+ float hi = min(max(a, b), max(c, d));
64862
+ return (lo + hi) * 0.5;
64863
+ }
64864
+ float _gpu_median_5(float a, float b, float c, float d, float e) {
64865
+ // 9-comparator Bose-Nelson sort; v2 holds the median.
64866
+ float t; float v0=a,v1=b,v2=c,v3=d,v4=e;
64867
+ t=min(v0,v1); v1=max(v0,v1); v0=t;
64868
+ t=min(v3,v4); v4=max(v3,v4); v3=t;
64869
+ t=min(v2,v4); v4=max(v2,v4); v2=t;
64870
+ t=min(v2,v3); v3=max(v2,v3); v2=t;
64871
+ t=min(v0,v3); v3=max(v0,v3); v0=t;
64872
+ t=min(v0,v2); v2=max(v0,v2); v0=t;
64873
+ t=min(v1,v4); v4=max(v1,v4); v1=t;
64874
+ t=min(v1,v3); v3=max(v1,v3); v1=t;
64875
+ t=min(v1,v2); v2=max(v1,v2); v1=t;
64876
+ return v2;
64877
+ }
64878
+ float _gpu_median_6(float a, float b, float c, float d, float e, float f) {
64879
+ float t; float v0=a,v1=b,v2=c,v3=d,v4=e,v5=f;
64880
+ t=min(v0,v1); v1=max(v0,v1); v0=t;
64881
+ t=min(v2,v3); v3=max(v2,v3); v2=t;
64882
+ t=min(v4,v5); v5=max(v4,v5); v4=t;
64883
+ t=min(v0,v2); v2=max(v0,v2); v0=t;
64884
+ t=min(v1,v3); v3=max(v1,v3); v1=t;
64885
+ t=min(v0,v4); v4=max(v0,v4); v0=t;
64886
+ t=min(v1,v5); v5=max(v1,v5); v1=t;
64887
+ t=min(v2,v4); v4=max(v2,v4); v2=t;
64888
+ t=min(v1,v2); v2=max(v1,v2); v1=t;
64889
+ t=min(v3,v5); v5=max(v3,v5); v3=t;
64890
+ t=min(v3,v4); v4=max(v3,v4); v3=t;
64891
+ return (v2 + v3) * 0.5;
64892
+ }
64893
+ float _gpu_median_7(float a, float b, float c, float d, float e, float f, float g) {
64894
+ float t; float v0=a,v1=b,v2=c,v3=d,v4=e,v5=f,v6=g;
64895
+ t=min(v0,v1); v1=max(v0,v1); v0=t;
64896
+ t=min(v2,v3); v3=max(v2,v3); v2=t;
64897
+ t=min(v4,v5); v5=max(v4,v5); v4=t;
64898
+ t=min(v0,v2); v2=max(v0,v2); v0=t;
64899
+ t=min(v1,v3); v3=max(v1,v3); v1=t;
64900
+ t=min(v4,v6); v6=max(v4,v6); v4=t;
64901
+ t=min(v0,v4); v4=max(v0,v4); v0=t;
64902
+ t=min(v1,v5); v5=max(v1,v5); v1=t;
64903
+ t=min(v2,v6); v6=max(v2,v6); v2=t;
64904
+ t=min(v1,v2); v2=max(v1,v2); v1=t;
64905
+ t=min(v3,v5); v5=max(v3,v5); v3=t;
64906
+ t=min(v2,v4); v4=max(v2,v4); v2=t;
64907
+ t=min(v3,v4); v4=max(v3,v4); v3=t;
64908
+ return v3;
64909
+ }
64910
+ float _gpu_median_8(float a, float b, float c, float d, float e, float f, float g, float h) {
64911
+ float t; float v0=a,v1=b,v2=c,v3=d,v4=e,v5=f,v6=g,v7=h;
64912
+ t=min(v0,v1); v1=max(v0,v1); v0=t;
64913
+ t=min(v2,v3); v3=max(v2,v3); v2=t;
64914
+ t=min(v4,v5); v5=max(v4,v5); v4=t;
64915
+ t=min(v6,v7); v7=max(v6,v7); v6=t;
64916
+ t=min(v0,v2); v2=max(v0,v2); v0=t;
64917
+ t=min(v1,v3); v3=max(v1,v3); v1=t;
64918
+ t=min(v4,v6); v6=max(v4,v6); v4=t;
64919
+ t=min(v5,v7); v7=max(v5,v7); v5=t;
64920
+ t=min(v0,v4); v4=max(v0,v4); v0=t;
64921
+ t=min(v1,v5); v5=max(v1,v5); v1=t;
64922
+ t=min(v2,v6); v6=max(v2,v6); v2=t;
64923
+ t=min(v3,v7); v7=max(v3,v7); v3=t;
64924
+ t=min(v1,v2); v2=max(v1,v2); v1=t;
64925
+ t=min(v3,v4); v4=max(v3,v4); v3=t;
64926
+ t=min(v5,v6); v6=max(v5,v6); v5=t;
64927
+ t=min(v3,v5); v5=max(v3,v5); v3=t;
64928
+ t=min(v2,v4); v4=max(v2,v4); v2=t;
64929
+ t=min(v3,v4); v4=max(v3,v4); v3=t;
64930
+ return (v3 + v4) * 0.5;
64931
+ }
64932
+ `;
64933
+ var GPU_MEDIAN_PREAMBLE_WGSL = `
64934
+ fn _gpu_median_2(a: f32, b: f32) -> f32 {
64935
+ return (a + b) * 0.5;
64936
+ }
64937
+ fn _gpu_median_3(a: f32, b: f32, c: f32) -> f32 {
64938
+ return max(min(a, b), min(max(a, b), c));
64939
+ }
64940
+ fn _gpu_median_4(a: f32, b: f32, c: f32, d: f32) -> f32 {
64941
+ let lo = max(min(a, b), min(c, d));
64942
+ let hi = min(max(a, b), max(c, d));
64943
+ return (lo + hi) * 0.5;
64944
+ }
64945
+ fn _gpu_median_5(a: f32, b: f32, c: f32, d: f32, e: f32) -> f32 {
64946
+ // 9-comparator Bose-Nelson sort; v2 holds the median.
64947
+ var v0=a; var v1=b; var v2=c; var v3=d; var v4=e; var t: f32;
64948
+ t=min(v0,v1); v1=max(v0,v1); v0=t;
64949
+ t=min(v3,v4); v4=max(v3,v4); v3=t;
64950
+ t=min(v2,v4); v4=max(v2,v4); v2=t;
64951
+ t=min(v2,v3); v3=max(v2,v3); v2=t;
64952
+ t=min(v0,v3); v3=max(v0,v3); v0=t;
64953
+ t=min(v0,v2); v2=max(v0,v2); v0=t;
64954
+ t=min(v1,v4); v4=max(v1,v4); v1=t;
64955
+ t=min(v1,v3); v3=max(v1,v3); v1=t;
64956
+ t=min(v1,v2); v2=max(v1,v2); v1=t;
64957
+ return v2;
64958
+ }
64959
+ fn _gpu_median_6(a: f32, b: f32, c: f32, d: f32, e: f32, f: f32) -> f32 {
64960
+ var v0=a; var v1=b; var v2=c; var v3=d; var v4=e; var v5=f; var t: f32;
64961
+ t=min(v0,v1); v1=max(v0,v1); v0=t;
64962
+ t=min(v2,v3); v3=max(v2,v3); v2=t;
64963
+ t=min(v4,v5); v5=max(v4,v5); v4=t;
64964
+ t=min(v0,v2); v2=max(v0,v2); v0=t;
64965
+ t=min(v1,v3); v3=max(v1,v3); v1=t;
64966
+ t=min(v0,v4); v4=max(v0,v4); v0=t;
64967
+ t=min(v1,v5); v5=max(v1,v5); v1=t;
64968
+ t=min(v2,v4); v4=max(v2,v4); v2=t;
64969
+ t=min(v1,v2); v2=max(v1,v2); v1=t;
64970
+ t=min(v3,v5); v5=max(v3,v5); v3=t;
64971
+ t=min(v3,v4); v4=max(v3,v4); v3=t;
64972
+ return (v2 + v3) * 0.5;
64973
+ }
64974
+ fn _gpu_median_7(a: f32, b: f32, c: f32, d: f32, e: f32, f: f32, g: f32) -> f32 {
64975
+ var v0=a; var v1=b; var v2=c; var v3=d; var v4=e; var v5=f; var v6=g; var t: f32;
64976
+ t=min(v0,v1); v1=max(v0,v1); v0=t;
64977
+ t=min(v2,v3); v3=max(v2,v3); v2=t;
64978
+ t=min(v4,v5); v5=max(v4,v5); v4=t;
64979
+ t=min(v0,v2); v2=max(v0,v2); v0=t;
64980
+ t=min(v1,v3); v3=max(v1,v3); v1=t;
64981
+ t=min(v4,v6); v6=max(v4,v6); v4=t;
64982
+ t=min(v0,v4); v4=max(v0,v4); v0=t;
64983
+ t=min(v1,v5); v5=max(v1,v5); v1=t;
64984
+ t=min(v2,v6); v6=max(v2,v6); v2=t;
64985
+ t=min(v1,v2); v2=max(v1,v2); v1=t;
64986
+ t=min(v3,v5); v5=max(v3,v5); v3=t;
64987
+ t=min(v2,v4); v4=max(v2,v4); v2=t;
64988
+ t=min(v3,v4); v4=max(v3,v4); v3=t;
64989
+ return v3;
64990
+ }
64991
+ fn _gpu_median_8(a: f32, b: f32, c: f32, d: f32, e: f32, f: f32, g: f32, h: f32) -> f32 {
64992
+ 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;
64993
+ t=min(v0,v1); v1=max(v0,v1); v0=t;
64994
+ t=min(v2,v3); v3=max(v2,v3); v2=t;
64995
+ t=min(v4,v5); v5=max(v4,v5); v4=t;
64996
+ t=min(v6,v7); v7=max(v6,v7); v6=t;
64997
+ t=min(v0,v2); v2=max(v0,v2); v0=t;
64998
+ t=min(v1,v3); v3=max(v1,v3); v1=t;
64999
+ t=min(v4,v6); v6=max(v4,v6); v4=t;
65000
+ t=min(v5,v7); v7=max(v5,v7); v5=t;
65001
+ t=min(v0,v4); v4=max(v0,v4); v0=t;
65002
+ t=min(v1,v5); v5=max(v1,v5); v1=t;
65003
+ t=min(v2,v6); v6=max(v2,v6); v2=t;
65004
+ t=min(v3,v7); v7=max(v3,v7); v3=t;
65005
+ t=min(v1,v2); v2=max(v1,v2); v1=t;
65006
+ t=min(v3,v4); v4=max(v3,v4); v3=t;
65007
+ t=min(v5,v6); v6=max(v5,v6); v5=t;
65008
+ t=min(v3,v5); v5=max(v3,v5); v3=t;
65009
+ t=min(v2,v4); v4=max(v2,v4); v2=t;
65010
+ t=min(v3,v4); v4=max(v3,v4); v3=t;
65011
+ return (v3 + v4) * 0.5;
65012
+ }
63499
65013
  `;
63500
65014
  var GPU_COLOR_PREAMBLE_GLSL = `
63501
65015
  float _gpu_srgb_to_linear(float c) {
@@ -64174,6 +65688,12 @@ fn _gpu_apca(lch_bg: vec3f, lch_fg: vec3f) -> f32 {
64174
65688
  if (code.includes("_fractal_")) {
64175
65689
  preamble += this.languageId === "wgsl" ? GPU_FRACTAL_PREAMBLE_WGSL : GPU_FRACTAL_PREAMBLE_GLSL;
64176
65690
  }
65691
+ if (code.includes("_gpu_random"))
65692
+ preamble += this.languageId === "wgsl" ? GPU_RANDOM_PREAMBLE_WGSL : GPU_RANDOM_PREAMBLE_GLSL;
65693
+ if (code.includes("_gpu_gcd"))
65694
+ preamble += this.languageId === "wgsl" ? GPU_GCD_PREAMBLE_WGSL : GPU_GCD_PREAMBLE_GLSL;
65695
+ if (code.includes("_gpu_median_"))
65696
+ preamble += this.languageId === "wgsl" ? GPU_MEDIAN_PREAMBLE_WGSL : GPU_MEDIAN_PREAMBLE_GLSL;
64177
65697
  if (code.includes("_gpu_srgb_to") || code.includes("_gpu_oklab") || code.includes("_gpu_oklch") || code.includes("_gpu_color_mix") || code.includes("_gpu_apca")) {
64178
65698
  preamble += this.languageId === "wgsl" ? GPU_COLOR_PREAMBLE_WGSL : GPU_COLOR_PREAMBLE_GLSL;
64179
65699
  }
@@ -66399,6 +67919,7 @@ ${workgroupAttr}fn main(${paramStr})${returnStr} {
66399
67919
  _timeLimit = 2e3;
66400
67920
  _iterationLimit = 1024;
66401
67921
  _recursionLimit = 1024;
67922
+ _maxCollectionSize = 1e4;
66402
67923
  _deadline = void 0;
66403
67924
  _isVerifying = false;
66404
67925
  get timeLimit() {
@@ -66419,6 +67940,12 @@ ${workgroupAttr}fn main(${paramStr})${returnStr} {
66419
67940
  set recursionLimit(value) {
66420
67941
  this._recursionLimit = value <= 0 ? Number.POSITIVE_INFINITY : value;
66421
67942
  }
67943
+ get maxCollectionSize() {
67944
+ return this._maxCollectionSize;
67945
+ }
67946
+ set maxCollectionSize(value) {
67947
+ this._maxCollectionSize = value <= 0 ? Number.POSITIVE_INFINITY : value;
67948
+ }
66422
67949
  get deadline() {
66423
67950
  return this._deadline;
66424
67951
  }
@@ -68457,6 +69984,23 @@ ${workgroupAttr}fn main(${paramStr})${returnStr} {
68457
69984
  set recursionLimit(t) {
68458
69985
  this._runtimeState.recursionLimit = t;
68459
69986
  }
69987
+ /** Maximum number of elements a collection may have when materialized
69988
+ * (converted from a lazy form to a `List`). Default: 10,000.
69989
+ *
69990
+ * When a materialization would exceed this size, the operation leaves
69991
+ * the expression in its lazy form. Consumers can detect oversize
69992
+ * collections via the symbolic form's `count`.
69993
+ *
69994
+ * Set to `Infinity` (or `0` / a negative number) to disable the cap.
69995
+ *
69996
+ * @experimental
69997
+ */
69998
+ get maxCollectionSize() {
69999
+ return this._runtimeState.maxCollectionSize;
70000
+ }
70001
+ set maxCollectionSize(t) {
70002
+ this._runtimeState.maxCollectionSize = t;
70003
+ }
68460
70004
  /**
68461
70005
  * Flag to prevent infinite recursion in the verify/ask/equality checking cycle.
68462
70006
  *
@@ -68733,6 +70277,36 @@ ${workgroupAttr}fn main(${paramStr})${returnStr} {
68733
70277
  lookupDefinition(id) {
68734
70278
  return lookupDefinition(this, id);
68735
70279
  }
70280
+ normalizeIdentifier(latex) {
70281
+ if (!latex) return "";
70282
+ if (isValidSymbol(latex)) return latex;
70283
+ this.pushScope();
70284
+ try {
70285
+ const expr2 = this.parse(latex);
70286
+ if (isSymbol2(expr2)) return expr2.symbol;
70287
+ } finally {
70288
+ this.popScope();
70289
+ }
70290
+ return "";
70291
+ }
70292
+ operatorInfo(head) {
70293
+ const def = this.lookupDefinition(head);
70294
+ if (!def || !isOperatorDef(def)) return void 0;
70295
+ const op = def.operator;
70296
+ return {
70297
+ kind: op.evaluate || op.collection ? "function" : "opaque",
70298
+ signature: op.signature
70299
+ };
70300
+ }
70301
+ symbolInfo(name) {
70302
+ const def = this.lookupDefinition(name);
70303
+ if (!def || !isValueDef(def)) return void 0;
70304
+ const v = def.value;
70305
+ return {
70306
+ kind: v.isConstant ? "constant" : "variable",
70307
+ type: v.type
70308
+ };
70309
+ }
68736
70310
  /**
68737
70311
  * Associate a new definition to a symbol in the current context.
68738
70312
  *
@@ -69020,6 +70594,7 @@ ${workgroupAttr}fn main(${paramStr})${returnStr} {
69020
70594
  const def = this.lookupDefinition(id);
69021
70595
  return !!(isValueDef(def) && def.value.subscriptEvaluate);
69022
70596
  },
70597
+ tolerance: this.tolerance,
69023
70598
  ...this._latexOptions,
69024
70599
  ...parseOpts
69025
70600
  });
@@ -69184,7 +70759,7 @@ ${workgroupAttr}fn main(${paramStr})${returnStr} {
69184
70759
  _setDefaultEngineFactory(() => new ComputeEngine());
69185
70760
 
69186
70761
  // src/core.ts
69187
- var version = "0.56.0";
70762
+ var version = "0.58.0";
69188
70763
  return __toCommonJS(core_exports);
69189
70764
  })();
69190
70765
  /*! Bundled license information: