@elaraai/east 0.0.1-beta.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 (251) hide show
  1. package/LICENSE.md +682 -0
  2. package/README.md +276 -0
  3. package/dist/src/analyze.d.ts +95 -0
  4. package/dist/src/analyze.d.ts.map +1 -0
  5. package/dist/src/analyze.js +1110 -0
  6. package/dist/src/analyze.js.map +1 -0
  7. package/dist/src/ast.d.ts +263 -0
  8. package/dist/src/ast.d.ts.map +1 -0
  9. package/dist/src/ast.js +151 -0
  10. package/dist/src/ast.js.map +1 -0
  11. package/dist/src/ast_to_ir.d.ts +24 -0
  12. package/dist/src/ast_to_ir.d.ts.map +1 -0
  13. package/dist/src/ast_to_ir.js +834 -0
  14. package/dist/src/ast_to_ir.js.map +1 -0
  15. package/dist/src/builtins.d.ts +18 -0
  16. package/dist/src/builtins.d.ts.map +1 -0
  17. package/dist/src/builtins.js +1105 -0
  18. package/dist/src/builtins.js.map +1 -0
  19. package/dist/src/comparison.d.ts +28 -0
  20. package/dist/src/comparison.d.ts.map +1 -0
  21. package/dist/src/comparison.js +1017 -0
  22. package/dist/src/comparison.js.map +1 -0
  23. package/dist/src/compile.d.ts +22 -0
  24. package/dist/src/compile.d.ts.map +1 -0
  25. package/dist/src/compile.js +3260 -0
  26. package/dist/src/compile.js.map +1 -0
  27. package/dist/src/containers/ref.d.ts +106 -0
  28. package/dist/src/containers/ref.d.ts.map +1 -0
  29. package/dist/src/containers/ref.js +100 -0
  30. package/dist/src/containers/ref.js.map +1 -0
  31. package/dist/src/containers/sortedmap.d.ts +165 -0
  32. package/dist/src/containers/sortedmap.d.ts.map +1 -0
  33. package/dist/src/containers/sortedmap.js +237 -0
  34. package/dist/src/containers/sortedmap.js.map +1 -0
  35. package/dist/src/containers/sortedset.d.ts +185 -0
  36. package/dist/src/containers/sortedset.d.ts.map +1 -0
  37. package/dist/src/containers/sortedset.js +312 -0
  38. package/dist/src/containers/sortedset.js.map +1 -0
  39. package/dist/src/containers/variant.d.ts +131 -0
  40. package/dist/src/containers/variant.d.ts.map +1 -0
  41. package/dist/src/containers/variant.js +68 -0
  42. package/dist/src/containers/variant.js.map +1 -0
  43. package/dist/src/datetime_format/parse.d.ts +50 -0
  44. package/dist/src/datetime_format/parse.d.ts.map +1 -0
  45. package/dist/src/datetime_format/parse.js +908 -0
  46. package/dist/src/datetime_format/parse.js.map +1 -0
  47. package/dist/src/datetime_format/print.d.ts +35 -0
  48. package/dist/src/datetime_format/print.d.ts.map +1 -0
  49. package/dist/src/datetime_format/print.js +157 -0
  50. package/dist/src/datetime_format/print.js.map +1 -0
  51. package/dist/src/datetime_format/tokenize.d.ts +76 -0
  52. package/dist/src/datetime_format/tokenize.d.ts.map +1 -0
  53. package/dist/src/datetime_format/tokenize.js +271 -0
  54. package/dist/src/datetime_format/tokenize.js.map +1 -0
  55. package/dist/src/datetime_format/types.d.ts +99 -0
  56. package/dist/src/datetime_format/types.d.ts.map +1 -0
  57. package/dist/src/datetime_format/types.js +103 -0
  58. package/dist/src/datetime_format/types.js.map +1 -0
  59. package/dist/src/datetime_format/validate.d.ts +51 -0
  60. package/dist/src/datetime_format/validate.d.ts.map +1 -0
  61. package/dist/src/datetime_format/validate.js +208 -0
  62. package/dist/src/datetime_format/validate.js.map +1 -0
  63. package/dist/src/default.d.ts +21 -0
  64. package/dist/src/default.d.ts.map +1 -0
  65. package/dist/src/default.js +82 -0
  66. package/dist/src/default.js.map +1 -0
  67. package/dist/src/eastir.d.ts +33 -0
  68. package/dist/src/eastir.d.ts.map +1 -0
  69. package/dist/src/eastir.js +92 -0
  70. package/dist/src/eastir.js.map +1 -0
  71. package/dist/src/error.d.ts +13 -0
  72. package/dist/src/error.d.ts.map +1 -0
  73. package/dist/src/error.js +8 -0
  74. package/dist/src/error.js.map +1 -0
  75. package/dist/src/expr/array.d.ts +1711 -0
  76. package/dist/src/expr/array.d.ts.map +1 -0
  77. package/dist/src/expr/array.js +1805 -0
  78. package/dist/src/expr/array.js.map +1 -0
  79. package/dist/src/expr/ast.d.ts +17 -0
  80. package/dist/src/expr/ast.d.ts.map +1 -0
  81. package/dist/src/expr/ast.js +302 -0
  82. package/dist/src/expr/ast.js.map +1 -0
  83. package/dist/src/expr/blob.d.ts +141 -0
  84. package/dist/src/expr/blob.d.ts.map +1 -0
  85. package/dist/src/expr/blob.js +198 -0
  86. package/dist/src/expr/blob.js.map +1 -0
  87. package/dist/src/expr/block.d.ts +201 -0
  88. package/dist/src/expr/block.d.ts.map +1 -0
  89. package/dist/src/expr/block.js +1505 -0
  90. package/dist/src/expr/block.js.map +1 -0
  91. package/dist/src/expr/boolean.d.ts +207 -0
  92. package/dist/src/expr/boolean.d.ts.map +1 -0
  93. package/dist/src/expr/boolean.js +261 -0
  94. package/dist/src/expr/boolean.js.map +1 -0
  95. package/dist/src/expr/datetime.d.ts +544 -0
  96. package/dist/src/expr/datetime.d.ts.map +1 -0
  97. package/dist/src/expr/datetime.js +980 -0
  98. package/dist/src/expr/datetime.js.map +1 -0
  99. package/dist/src/expr/dict.d.ts +1242 -0
  100. package/dist/src/expr/dict.d.ts.map +1 -0
  101. package/dist/src/expr/dict.js +1492 -0
  102. package/dist/src/expr/dict.js.map +1 -0
  103. package/dist/src/expr/expr.d.ts +95 -0
  104. package/dist/src/expr/expr.d.ts.map +1 -0
  105. package/dist/src/expr/expr.js +171 -0
  106. package/dist/src/expr/expr.js.map +1 -0
  107. package/dist/src/expr/float.d.ts +357 -0
  108. package/dist/src/expr/float.d.ts.map +1 -0
  109. package/dist/src/expr/float.js +637 -0
  110. package/dist/src/expr/float.js.map +1 -0
  111. package/dist/src/expr/function.d.ts +46 -0
  112. package/dist/src/expr/function.d.ts.map +1 -0
  113. package/dist/src/expr/function.js +58 -0
  114. package/dist/src/expr/function.js.map +1 -0
  115. package/dist/src/expr/index.d.ts +450 -0
  116. package/dist/src/expr/index.d.ts.map +1 -0
  117. package/dist/src/expr/index.js +423 -0
  118. package/dist/src/expr/index.js.map +1 -0
  119. package/dist/src/expr/integer.d.ts +256 -0
  120. package/dist/src/expr/integer.d.ts.map +1 -0
  121. package/dist/src/expr/integer.js +311 -0
  122. package/dist/src/expr/integer.js.map +1 -0
  123. package/dist/src/expr/libs/array.d.ts +106 -0
  124. package/dist/src/expr/libs/array.d.ts.map +1 -0
  125. package/dist/src/expr/libs/array.js +140 -0
  126. package/dist/src/expr/libs/array.js.map +1 -0
  127. package/dist/src/expr/libs/blob.d.ts +42 -0
  128. package/dist/src/expr/libs/blob.d.ts.map +1 -0
  129. package/dist/src/expr/libs/blob.js +70 -0
  130. package/dist/src/expr/libs/blob.js.map +1 -0
  131. package/dist/src/expr/libs/datetime.d.ts +479 -0
  132. package/dist/src/expr/libs/datetime.d.ts.map +1 -0
  133. package/dist/src/expr/libs/datetime.js +624 -0
  134. package/dist/src/expr/libs/datetime.js.map +1 -0
  135. package/dist/src/expr/libs/dict.d.ts +66 -0
  136. package/dist/src/expr/libs/dict.d.ts.map +1 -0
  137. package/dist/src/expr/libs/dict.js +77 -0
  138. package/dist/src/expr/libs/dict.js.map +1 -0
  139. package/dist/src/expr/libs/float.d.ts +299 -0
  140. package/dist/src/expr/libs/float.d.ts.map +1 -0
  141. package/dist/src/expr/libs/float.js +564 -0
  142. package/dist/src/expr/libs/float.js.map +1 -0
  143. package/dist/src/expr/libs/integer.d.ts +228 -0
  144. package/dist/src/expr/libs/integer.d.ts.map +1 -0
  145. package/dist/src/expr/libs/integer.js +398 -0
  146. package/dist/src/expr/libs/integer.js.map +1 -0
  147. package/dist/src/expr/libs/set.d.ts +59 -0
  148. package/dist/src/expr/libs/set.d.ts.map +1 -0
  149. package/dist/src/expr/libs/set.js +69 -0
  150. package/dist/src/expr/libs/set.js.map +1 -0
  151. package/dist/src/expr/libs/string.d.ts +71 -0
  152. package/dist/src/expr/libs/string.d.ts.map +1 -0
  153. package/dist/src/expr/libs/string.js +75 -0
  154. package/dist/src/expr/libs/string.js.map +1 -0
  155. package/dist/src/expr/never.d.ts +15 -0
  156. package/dist/src/expr/never.d.ts.map +1 -0
  157. package/dist/src/expr/never.js +12 -0
  158. package/dist/src/expr/never.js.map +1 -0
  159. package/dist/src/expr/null.d.ts +15 -0
  160. package/dist/src/expr/null.d.ts.map +1 -0
  161. package/dist/src/expr/null.js +12 -0
  162. package/dist/src/expr/null.js.map +1 -0
  163. package/dist/src/expr/ref.d.ts +103 -0
  164. package/dist/src/expr/ref.d.ts.map +1 -0
  165. package/dist/src/expr/ref.js +131 -0
  166. package/dist/src/expr/ref.js.map +1 -0
  167. package/dist/src/expr/regex_validation.d.ts +25 -0
  168. package/dist/src/expr/regex_validation.d.ts.map +1 -0
  169. package/dist/src/expr/regex_validation.js +130 -0
  170. package/dist/src/expr/regex_validation.js.map +1 -0
  171. package/dist/src/expr/set.d.ts +1071 -0
  172. package/dist/src/expr/set.d.ts.map +1 -0
  173. package/dist/src/expr/set.js +1137 -0
  174. package/dist/src/expr/set.js.map +1 -0
  175. package/dist/src/expr/string.d.ts +414 -0
  176. package/dist/src/expr/string.d.ts.map +1 -0
  177. package/dist/src/expr/string.js +683 -0
  178. package/dist/src/expr/string.js.map +1 -0
  179. package/dist/src/expr/struct.d.ts +48 -0
  180. package/dist/src/expr/struct.d.ts.map +1 -0
  181. package/dist/src/expr/struct.js +65 -0
  182. package/dist/src/expr/struct.js.map +1 -0
  183. package/dist/src/expr/types.d.ts +68 -0
  184. package/dist/src/expr/types.d.ts.map +1 -0
  185. package/dist/src/expr/types.js +6 -0
  186. package/dist/src/expr/types.js.map +1 -0
  187. package/dist/src/expr/variant.d.ts +137 -0
  188. package/dist/src/expr/variant.d.ts.map +1 -0
  189. package/dist/src/expr/variant.js +105 -0
  190. package/dist/src/expr/variant.js.map +1 -0
  191. package/dist/src/fuzz.d.ts +80 -0
  192. package/dist/src/fuzz.d.ts.map +1 -0
  193. package/dist/src/fuzz.js +300 -0
  194. package/dist/src/fuzz.js.map +1 -0
  195. package/dist/src/index.d.ts +21 -0
  196. package/dist/src/index.d.ts.map +1 -0
  197. package/dist/src/index.js +21 -0
  198. package/dist/src/index.js.map +1 -0
  199. package/dist/src/internal.d.ts +36 -0
  200. package/dist/src/internal.d.ts.map +1 -0
  201. package/dist/src/internal.js +11 -0
  202. package/dist/src/internal.js.map +1 -0
  203. package/dist/src/ir.d.ts +1571 -0
  204. package/dist/src/ir.d.ts.map +1 -0
  205. package/dist/src/ir.js +56 -0
  206. package/dist/src/ir.js.map +1 -0
  207. package/dist/src/location.d.ts +48 -0
  208. package/dist/src/location.d.ts.map +1 -0
  209. package/dist/src/location.js +62 -0
  210. package/dist/src/location.js.map +1 -0
  211. package/dist/src/platform.d.ts +21 -0
  212. package/dist/src/platform.d.ts.map +1 -0
  213. package/dist/src/platform.js +8 -0
  214. package/dist/src/platform.js.map +1 -0
  215. package/dist/src/serialization/beast.d.ts +39 -0
  216. package/dist/src/serialization/beast.d.ts.map +1 -0
  217. package/dist/src/serialization/beast.js +555 -0
  218. package/dist/src/serialization/beast.js.map +1 -0
  219. package/dist/src/serialization/beast2-stream.d.ts +38 -0
  220. package/dist/src/serialization/beast2-stream.d.ts.map +1 -0
  221. package/dist/src/serialization/beast2-stream.js +665 -0
  222. package/dist/src/serialization/beast2-stream.js.map +1 -0
  223. package/dist/src/serialization/beast2.d.ts +41 -0
  224. package/dist/src/serialization/beast2.d.ts.map +1 -0
  225. package/dist/src/serialization/beast2.js +489 -0
  226. package/dist/src/serialization/beast2.js.map +1 -0
  227. package/dist/src/serialization/binary-utils.d.ts +151 -0
  228. package/dist/src/serialization/binary-utils.d.ts.map +1 -0
  229. package/dist/src/serialization/binary-utils.js +929 -0
  230. package/dist/src/serialization/binary-utils.js.map +1 -0
  231. package/dist/src/serialization/east.d.ts +84 -0
  232. package/dist/src/serialization/east.d.ts.map +1 -0
  233. package/dist/src/serialization/east.js +1802 -0
  234. package/dist/src/serialization/east.js.map +1 -0
  235. package/dist/src/serialization/index.d.ts +11 -0
  236. package/dist/src/serialization/index.d.ts.map +1 -0
  237. package/dist/src/serialization/index.js +12 -0
  238. package/dist/src/serialization/index.js.map +1 -0
  239. package/dist/src/serialization/json.d.ts +36 -0
  240. package/dist/src/serialization/json.d.ts.map +1 -0
  241. package/dist/src/serialization/json.js +849 -0
  242. package/dist/src/serialization/json.js.map +1 -0
  243. package/dist/src/type_of_type.d.ts +115 -0
  244. package/dist/src/type_of_type.d.ts.map +1 -0
  245. package/dist/src/type_of_type.js +362 -0
  246. package/dist/src/type_of_type.js.map +1 -0
  247. package/dist/src/types.d.ts +648 -0
  248. package/dist/src/types.d.ts.map +1 -0
  249. package/dist/src/types.js +1631 -0
  250. package/dist/src/types.js.map +1 -0
  251. package/package.json +87 -0
@@ -0,0 +1,1805 @@
1
+ import { get_location } from "../location.js";
2
+ import { ArrayType, BooleanType, FunctionType, IntegerType, NullType, StringType, isSubtype, isTypeEqual, printType, NeverType, OptionType, isDataType, StructType, VariantType, SetType, FloatType, DictType } from "../types.js";
3
+ import { valueOrExprToAst, valueOrExprToAstTyped } from "./ast.js";
4
+ import { AstSymbol, Expr, FactorySymbol, TypeSymbol } from "./expr.js";
5
+ import { none, some } from "../containers/variant.js";
6
+ /**
7
+ * Expression representing mutable array values and operations.
8
+ *
9
+ * ArrayExpr provides comprehensive methods for array manipulation including element access,
10
+ * mutation (push/pop/sort/reverse), transformation (map/filter/reduce), searching, slicing,
11
+ * grouping, and aggregation. Arrays are mutable, ordered collections with zero-based indexing.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * // Array manipulation
16
+ * const processArray = East.function([ArrayType(IntegerType)], ArrayType(IntegerType), ($, arr) => {
17
+ * $(arr.pushLast(10n)); // Mutate: add element
18
+ * const doubled = arr.map(($, x, i) => x.multiply(2n));
19
+ * $.return(doubled);
20
+ * });
21
+ *
22
+ * // Filtering and mapping
23
+ * const filterMap = East.function([ArrayType(IntegerType)], ArrayType(IntegerType), ($, arr) => {
24
+ * const evens = arr.filter(($, x, i) => x.remainder(2n).equal(0n));
25
+ * $.return(evens.map(($, x, i) => x.add(1n)));
26
+ * });
27
+ *
28
+ * // Aggregation
29
+ * const sumArray = East.function([ArrayType(IntegerType)], IntegerType, ($, arr) => {
30
+ * $.return(arr.reduce(($, acc, x, i) => acc.add(x), 0n));
31
+ * });
32
+ * ```
33
+ */
34
+ export class ArrayExpr extends Expr {
35
+ value_type;
36
+ constructor(value_type, ast, createExpr) {
37
+ super(ast.type, ast, createExpr);
38
+ this.value_type = value_type;
39
+ }
40
+ /**
41
+ * Returns the size (length) of the array.
42
+ *
43
+ * @returns An IntegerExpr representing the number of elements
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * const getSize = East.function([ArrayType(IntegerType)], IntegerType, ($, arr) => {
48
+ * $.return(arr.size());
49
+ * });
50
+ * const compiled = East.compile(getSize.toIR(), []);
51
+ * compiled([1n, 2n, 3n]); // 3n
52
+ * compiled([]); // 0n
53
+ * ```
54
+ */
55
+ size() {
56
+ return this[FactorySymbol]({
57
+ ast_type: "Builtin",
58
+ type: IntegerType,
59
+ location: get_location(2),
60
+ builtin: "ArraySize",
61
+ type_parameters: [this.value_type],
62
+ arguments: [this[AstSymbol]],
63
+ });
64
+ }
65
+ /**
66
+ * Checks if an element exists at the specified index (i.e., if the index is in bounds).
67
+ *
68
+ * @param key - The zero-based index to check
69
+ * @returns A BooleanExpr that is true if the index is valid (0 <= index < size)
70
+ *
71
+ * @example
72
+ * ```ts
73
+ * const hasElement = East.function([ArrayType(IntegerType), IntegerType], BooleanType, ($, arr, index) => {
74
+ * $.return(arr.has(index));
75
+ * });
76
+ * const compiled = East.compile(hasElement.toIR(), []);
77
+ * compiled([10n, 20n, 30n], 1n); // true
78
+ * compiled([10n, 20n, 30n], 5n); // false
79
+ * compiled([10n, 20n, 30n], -1n); // false
80
+ * ```
81
+ */
82
+ has(key) {
83
+ const keyAst = valueOrExprToAstTyped(key, IntegerType);
84
+ return this[FactorySymbol]({
85
+ ast_type: "Builtin",
86
+ type: BooleanType,
87
+ location: get_location(2),
88
+ builtin: "ArrayHas",
89
+ type_parameters: [this.value_type],
90
+ arguments: [this[AstSymbol], keyAst],
91
+ });
92
+ }
93
+ /**
94
+ * Gets the element at the specified index.
95
+ *
96
+ * @param key - The zero-based index
97
+ * @param defaultFn - Optional function to provide a default value for out-of-bounds indices
98
+ * @returns An expression of the array's element type
99
+ *
100
+ * @throws East runtime error if index is out of bounds and no defaultFn is provided
101
+ *
102
+ * @example
103
+ * ```ts
104
+ * const getElement = East.function([ArrayType(IntegerType), IntegerType], IntegerType, ($, arr, index) => {
105
+ * $.return(arr.get(index));
106
+ * });
107
+ * const compiled = East.compile(getElement.toIR(), []);
108
+ * compiled([10n, 20n, 30n], 1n); // 20n
109
+ * // compiled([10n, 20n, 30n], 5n) would throw error
110
+ *
111
+ * // With default value
112
+ * const getOrDefault = East.function([ArrayType(IntegerType), IntegerType], IntegerType, ($, arr, index) => {
113
+ * $.return(arr.get(index, ($, i) => -1n));
114
+ * });
115
+ * compiled = East.compile(getOrDefault.toIR(), []);
116
+ * compiled([10n, 20n, 30n], 5n); // -1n (out of bounds)
117
+ * ```
118
+ */
119
+ get(key, defaultFn) {
120
+ const keyAst = valueOrExprToAstTyped(key, IntegerType);
121
+ if (defaultFn !== undefined) {
122
+ const defaultFnAst = valueOrExprToAstTyped(defaultFn, FunctionType([IntegerType], this.value_type, null));
123
+ return this[FactorySymbol]({
124
+ ast_type: "Builtin",
125
+ type: this.value_type,
126
+ location: get_location(2),
127
+ builtin: "ArrayGetOrDefault",
128
+ type_parameters: [this.value_type],
129
+ arguments: [this[AstSymbol], keyAst, defaultFnAst],
130
+ });
131
+ }
132
+ else {
133
+ return this[FactorySymbol]({
134
+ ast_type: "Builtin",
135
+ type: this.value_type,
136
+ location: get_location(2),
137
+ builtin: "ArrayGet",
138
+ type_parameters: [this.value_type],
139
+ arguments: [this[AstSymbol], keyAst],
140
+ });
141
+ }
142
+ }
143
+ /**
144
+ * Safely gets the element at the specified index, returning an Option variant.
145
+ *
146
+ * @param key - The zero-based index
147
+ * @returns An OptionType expression containing .some(value) if in bounds, or .none if out of bounds
148
+ *
149
+ * @example
150
+ * ```ts
151
+ * const tryGetElement = East.function([ArrayType(IntegerType), IntegerType], OptionType(IntegerType), ($, arr, index) => {
152
+ * $.return(arr.tryGet(index));
153
+ * });
154
+ * const compiled = East.compile(tryGetElement.toIR(), []);
155
+ * compiled([10n, 20n, 30n], 1n); // {_tag: "some", some: 20n}
156
+ * compiled([10n, 20n, 30n], 5n); // {_tag: "none", none: null}
157
+ * ```
158
+ */
159
+ tryGet(key) {
160
+ const keyAst = valueOrExprToAstTyped(key, IntegerType);
161
+ return this[FactorySymbol]({
162
+ ast_type: "Builtin",
163
+ type: OptionType(this.value_type),
164
+ location: get_location(2),
165
+ builtin: "ArrayTryGet",
166
+ type_parameters: [this.value_type],
167
+ arguments: [this[AstSymbol], keyAst],
168
+ });
169
+ }
170
+ /**
171
+ * Updates (replaces) the element at the specified index (mutates the array).
172
+ *
173
+ * @param key - The zero-based index to update
174
+ * @param value - The new value to set at that index
175
+ * @returns NullExpr (operation performed for side effect)
176
+ *
177
+ * @throws East runtime error if index is out of bounds
178
+ *
179
+ * @example
180
+ * ```ts
181
+ * const updateElement = East.function([ArrayType(IntegerType), IntegerType, IntegerType], ArrayType(IntegerType), ($, arr, index, value) => {
182
+ * $(arr.update(index, value)); // Mutate the array
183
+ * $.return(arr);
184
+ * });
185
+ * const compiled = East.compile(updateElement.toIR(), []);
186
+ * compiled([10n, 20n, 30n], 1n, 99n); // [10n, 99n, 30n]
187
+ * ```
188
+ */
189
+ update(key, value) {
190
+ const keyAst = valueOrExprToAstTyped(key, IntegerType);
191
+ const valueAst = valueOrExprToAstTyped(value, this.value_type);
192
+ return this[FactorySymbol]({
193
+ ast_type: "Builtin",
194
+ type: NullType,
195
+ location: get_location(2),
196
+ builtin: "ArrayUpdate",
197
+ type_parameters: [this.value_type],
198
+ arguments: [this[AstSymbol], keyAst, valueAst],
199
+ });
200
+ }
201
+ /** Modify an array element at index by merging it with a new value using a function accepting the new and current value. An error is thrown on missing key (index out of bounds).
202
+ *
203
+ * This is useful for patterns where you want to update an element based on its current value, e.g. incrementing a number, appending to a string, updating fields in a struct, or pushing to an array.
204
+ *
205
+ * @see {@link update} for simply replacing the value.
206
+ */
207
+ merge(key, value, updateFn) {
208
+ const location = get_location(2);
209
+ const keyAst = valueOrExprToAstTyped(key, IntegerType);
210
+ const value2Type = value[TypeSymbol];
211
+ const updateFnExpr = Expr.from(updateFn, FunctionType([value2Type, this.value_type, IntegerType], this.value_type, null));
212
+ return this[FactorySymbol]({
213
+ ast_type: "Builtin",
214
+ type: NullType,
215
+ location,
216
+ builtin: "ArrayMerge",
217
+ type_parameters: [this.value_type, value2Type],
218
+ arguments: [this[AstSymbol], keyAst, value[AstSymbol], updateFnExpr[AstSymbol]],
219
+ });
220
+ }
221
+ /**
222
+ * Adds an element to the end of the array (mutates the array).
223
+ *
224
+ * @param value - The value to append
225
+ * @returns NullExpr (operation performed for side effect)
226
+ *
227
+ * @example
228
+ * ```ts
229
+ * const appendValue = East.function([ArrayType(IntegerType), IntegerType], ArrayType(IntegerType), ($, arr, value) => {
230
+ * $(arr.pushLast(value)); // Mutate the array
231
+ * $.return(arr);
232
+ * });
233
+ * const compiled = East.compile(appendValue.toIR(), []);
234
+ * compiled([1n, 2n, 3n], 4n); // [1n, 2n, 3n, 4n]
235
+ * ```
236
+ */
237
+ pushLast(value) {
238
+ const valueAst = valueOrExprToAstTyped(value, this.value_type);
239
+ return this[FactorySymbol]({
240
+ ast_type: "Builtin",
241
+ type: NullType,
242
+ location: get_location(2),
243
+ builtin: "ArrayPushLast",
244
+ type_parameters: [this.value_type],
245
+ arguments: [this[AstSymbol], valueAst],
246
+ });
247
+ }
248
+ /**
249
+ * Removes and returns the last element from the array (mutates the array).
250
+ *
251
+ * @returns An expression of the array's element type containing the removed value
252
+ *
253
+ * @throws East runtime error if the array is empty
254
+ *
255
+ * @example
256
+ * ```ts
257
+ * const removeLastValue = East.function([ArrayType(IntegerType)], IntegerType, ($, arr) => {
258
+ * $.return(arr.popLast());
259
+ * });
260
+ * const compiled = East.compile(removeLastValue.toIR(), []);
261
+ * compiled([1n, 2n, 3n]); // 3n (arr is now [1n, 2n])
262
+ * ```
263
+ */
264
+ popLast() {
265
+ return this[FactorySymbol]({
266
+ ast_type: "Builtin",
267
+ type: this.value_type,
268
+ location: get_location(2),
269
+ builtin: "ArrayPopLast",
270
+ type_parameters: [this.value_type],
271
+ arguments: [this[AstSymbol]],
272
+ });
273
+ }
274
+ /**
275
+ * Adds an element to the beginning of the array (mutates the array).
276
+ *
277
+ * @param value - The value to prepend
278
+ * @returns NullExpr (operation performed for side effect)
279
+ *
280
+ * @example
281
+ * ```ts
282
+ * const prependValue = East.function([ArrayType(IntegerType), IntegerType], ArrayType(IntegerType), ($, arr, value) => {
283
+ * $(arr.pushFirst(value)); // Mutate the array
284
+ * $.return(arr);
285
+ * });
286
+ * const compiled = East.compile(prependValue.toIR(), []);
287
+ * compiled([1n, 2n, 3n], 0n); // [0n, 1n, 2n, 3n]
288
+ * ```
289
+ */
290
+ pushFirst(value) {
291
+ const valueAst = valueOrExprToAstTyped(value, this.value_type);
292
+ return this[FactorySymbol]({
293
+ ast_type: "Builtin",
294
+ type: NullType,
295
+ location: get_location(2),
296
+ builtin: "ArrayPushFirst",
297
+ type_parameters: [this.value_type],
298
+ arguments: [this[AstSymbol], valueAst],
299
+ });
300
+ }
301
+ /**
302
+ * Removes and returns the first element from the array (mutates the array).
303
+ *
304
+ * @returns An expression of the array's element type containing the removed value
305
+ *
306
+ * @throws East runtime error if the array is empty
307
+ *
308
+ * @example
309
+ * ```ts
310
+ * const removeFirstValue = East.function([ArrayType(IntegerType)], IntegerType, ($, arr) => {
311
+ * $.return(arr.popFirst());
312
+ * });
313
+ * const compiled = East.compile(removeFirstValue.toIR(), []);
314
+ * compiled([1n, 2n, 3n]); // 1n (arr is now [2n, 3n])
315
+ * ```
316
+ */
317
+ popFirst() {
318
+ return this[FactorySymbol]({
319
+ ast_type: "Builtin",
320
+ type: this.value_type,
321
+ location: get_location(2),
322
+ builtin: "ArrayPopFirst",
323
+ type_parameters: [this.value_type],
324
+ arguments: [this[AstSymbol]],
325
+ });
326
+ }
327
+ /**
328
+ * Appends all elements from another array to this array (mutates the array).
329
+ *
330
+ * @param array - The array whose elements will be appended
331
+ * @returns NullExpr (operation performed for side effect)
332
+ *
333
+ * @example
334
+ * ```ts
335
+ * const appendArray = East.function([ArrayType(IntegerType), ArrayType(IntegerType)], ArrayType(IntegerType), ($, arr1, arr2) => {
336
+ * $(arr1.append(arr2)); // Mutate arr1
337
+ * $.return(arr1);
338
+ * });
339
+ * const compiled = East.compile(appendArray.toIR(), []);
340
+ * compiled([1n, 2n], [3n, 4n]); // [1n, 2n, 3n, 4n]
341
+ * ```
342
+ */
343
+ append(array) {
344
+ const arrayExpr = Expr.from(array, this[TypeSymbol]);
345
+ return this[FactorySymbol]({
346
+ ast_type: "Builtin",
347
+ type: NullType,
348
+ location: get_location(2),
349
+ builtin: "ArrayAppend",
350
+ type_parameters: [this.value_type],
351
+ arguments: [this[AstSymbol], arrayExpr[AstSymbol]],
352
+ });
353
+ }
354
+ /**
355
+ * Prepends all elements from another array to this array (mutates the array).
356
+ *
357
+ * @param array - The array whose elements will be prepended
358
+ * @returns NullExpr (operation performed for side effect)
359
+ *
360
+ * @example
361
+ * ```ts
362
+ * const prependArray = East.function([ArrayType(IntegerType), ArrayType(IntegerType)], ArrayType(IntegerType), ($, arr1, arr2) => {
363
+ * $(arr1.prepend(arr2)); // Mutate arr1
364
+ * $.return(arr1);
365
+ * });
366
+ * const compiled = East.compile(prependArray.toIR(), []);
367
+ * compiled([3n, 4n], [1n, 2n]); // [1n, 2n, 3n, 4n]
368
+ * ```
369
+ */
370
+ prepend(array) {
371
+ // Convert the input array to an expression of the right type
372
+ const arrayExpr = Expr.from(array, this[TypeSymbol]);
373
+ return this[FactorySymbol]({
374
+ ast_type: "Builtin",
375
+ type: NullType,
376
+ location: get_location(2),
377
+ builtin: "ArrayPrepend",
378
+ type_parameters: [this.value_type],
379
+ arguments: [this[AstSymbol], arrayExpr[AstSymbol]],
380
+ });
381
+ }
382
+ /**
383
+ * Merges all elements from another array into this array using a merge function.
384
+ *
385
+ * @param array - The array to merge from
386
+ * @param mergeFn - Function taking (currentValue, otherValue, index) and returning the merged value
387
+ * @returns Null (mutates the array in place)
388
+ *
389
+ * @throws East runtime error if the other array has more elements than this array
390
+ *
391
+ * @example
392
+ * ```ts
393
+ * const mergeArrays = East.function([ArrayType(IntegerType), ArrayType(IntegerType)], NullType, ($, arr1, arr2) => {
394
+ * arr1.mergeAll(arr2, ($, current, other, index) => current.add(other));
395
+ * $.return(null);
396
+ * });
397
+ * const compiled = East.compile(mergeArrays.toIR(), []);
398
+ * const arr1 = [10n, 20n, 30n];
399
+ * const arr2 = [1n, 2n, 3n];
400
+ * compiled(arr1, arr2); // null (arr1 is now [11n, 22n, 33n])
401
+ * ```
402
+ *
403
+ * @example
404
+ * ```ts
405
+ * // Combining strings
406
+ * const mergeStrings = East.function([ArrayType(StringType), ArrayType(StringType)], ArrayType(StringType), ($, arr1, arr2) => {
407
+ * arr1.mergeAll(arr2, ($, current, other, index) => current.concat(" + ").concat(other));
408
+ * $.return(arr1);
409
+ * });
410
+ * const compiled = East.compile(mergeStrings.toIR(), []);
411
+ * compiled(["a", "b"], ["x", "y"]); // ["a + x", "b + y"]
412
+ * ```
413
+ */
414
+ mergeAll(array, mergeFn) {
415
+ const array_type = array[TypeSymbol];
416
+ if (!array_type || array_type.type !== "Array") {
417
+ throw new Error(`Expected array type for mergeAll, got ${array_type ? printType(array_type) : "unknown"}`);
418
+ }
419
+ const value2Type = array_type.value;
420
+ const mergeFnExpr = Expr.from(mergeFn, FunctionType([this.value_type, value2Type, IntegerType], this.value_type, null));
421
+ return this[FactorySymbol]({
422
+ ast_type: "Builtin",
423
+ type: NullType,
424
+ location: get_location(2),
425
+ builtin: "ArrayMergeAll",
426
+ type_parameters: [this.value_type, value2Type],
427
+ arguments: [this[AstSymbol], array[AstSymbol], mergeFnExpr[AstSymbol]],
428
+ });
429
+ }
430
+ /**
431
+ * Clears all elements from the array, making it empty.
432
+ *
433
+ * @returns Null (mutates the array in place)
434
+ *
435
+ * @example
436
+ * ```ts
437
+ * const clearArray = East.function([ArrayType(IntegerType)], ArrayType(IntegerType), ($, arr) => {
438
+ * arr.clear();
439
+ * $.return(arr);
440
+ * });
441
+ * const compiled = East.compile(clearArray.toIR(), []);
442
+ * compiled([1n, 2n, 3n]); // []
443
+ * ```
444
+ */
445
+ clear() {
446
+ return this[FactorySymbol]({
447
+ ast_type: "Builtin",
448
+ type: NullType,
449
+ location: get_location(2),
450
+ builtin: "ArrayClear",
451
+ type_parameters: [this.value_type],
452
+ arguments: [this[AstSymbol]],
453
+ });
454
+ }
455
+ /**
456
+ * Sorts the array in place, re-ordering it in memory.
457
+ *
458
+ * @param by - Optional projection function to determine sort order (defaults to sorting by the values themselves)
459
+ * @returns Null (mutates the array in place)
460
+ *
461
+ * @remarks
462
+ * The optional `by` function can project values to determine sort order (e.g., sort by a specific field).
463
+ *
464
+ * @example
465
+ * ```ts
466
+ * const sortNumbers = East.function([ArrayType(IntegerType)], ArrayType(IntegerType), ($, arr) => {
467
+ * arr.sortInPlace();
468
+ * $.return(arr);
469
+ * });
470
+ * const compiled = East.compile(sortNumbers.toIR(), []);
471
+ * compiled([3n, 1n, 4n, 1n, 5n]); // [1n, 1n, 3n, 4n, 5n]
472
+ * ```
473
+ *
474
+ * @example
475
+ * ```ts
476
+ * // Sorting structs by a field
477
+ * const PersonType = StructType({ name: StringType, age: IntegerType });
478
+ * const sortByAge = East.function([ArrayType(PersonType)], ArrayType(PersonType), ($, people) => {
479
+ * people.sortInPlace(($, p) => p.age);
480
+ * $.return(people);
481
+ * });
482
+ * const compiled = East.compile(sortByAge.toIR(), []);
483
+ * compiled([{ name: "Alice", age: 30n }, { name: "Bob", age: 25n }]);
484
+ * // [{ name: "Bob", age: 25n }, { name: "Alice", age: 30n }]
485
+ * ```
486
+ *
487
+ * @see {@link sort} to produce a new sorted array instead of sorting in place.
488
+ */
489
+ sortInPlace(by) {
490
+ let byExpr;
491
+ if (by === undefined) {
492
+ byExpr = Expr.function([this.value_type], this.value_type, ($, x) => x);
493
+ }
494
+ else {
495
+ byExpr = by instanceof Expr ? by : Expr.function([this.value_type], undefined, by);
496
+ }
497
+ const byType = byExpr[TypeSymbol];
498
+ const projectedType = byType.output;
499
+ if (!(isDataType(projectedType))) {
500
+ throw new Error(`Can only sort by data types, got ${printType(projectedType)}`);
501
+ }
502
+ return this[FactorySymbol]({
503
+ ast_type: "Builtin",
504
+ type: NullType,
505
+ location: get_location(2),
506
+ builtin: "ArraySortInPlace",
507
+ type_parameters: [this.value_type, projectedType],
508
+ arguments: [this[AstSymbol], byExpr[AstSymbol]],
509
+ });
510
+ }
511
+ /**
512
+ * Produces a new array with the values sorted.
513
+ *
514
+ * @param by - Optional projection function to determine sort order (defaults to sorting by the values themselves)
515
+ * @returns A new ArrayExpr containing the sorted elements
516
+ *
517
+ * @remarks
518
+ * The optional `by` function can project values to determine sort order (e.g., sort by a specific field).
519
+ * This method does not mutate the original array.
520
+ *
521
+ * @example
522
+ * ```ts
523
+ * const sortNumbers = East.function([ArrayType(IntegerType)], ArrayType(IntegerType), ($, arr) => {
524
+ * $.return(arr.sort());
525
+ * });
526
+ * const compiled = East.compile(sortNumbers.toIR(), []);
527
+ * compiled([3n, 1n, 4n, 1n, 5n]); // [1n, 1n, 3n, 4n, 5n]
528
+ * ```
529
+ *
530
+ * @example
531
+ * ```ts
532
+ * // Sorting structs by a field (descending)
533
+ * const PersonType = StructType({ name: StringType, age: IntegerType });
534
+ * const sortByAgeDesc = East.function([ArrayType(PersonType)], ArrayType(PersonType), ($, people) => {
535
+ * $.return(people.sort(($, p) => p.age.negate()));
536
+ * });
537
+ * const compiled = East.compile(sortByAgeDesc.toIR(), []);
538
+ * compiled([{ name: "Alice", age: 30n }, { name: "Bob", age: 25n }]);
539
+ * // [{ name: "Alice", age: 30n }, { name: "Bob", age: 25n }]
540
+ * ```
541
+ *
542
+ * @see {@link sortInPlace} to sort the array in place.
543
+ */
544
+ sort(by) {
545
+ let byExpr;
546
+ if (by === undefined) {
547
+ byExpr = Expr.function([this.value_type], this.value_type, ($, x) => x);
548
+ }
549
+ else {
550
+ byExpr = by instanceof Expr ? by : Expr.function([this.value_type], undefined, by);
551
+ }
552
+ const byType = byExpr[TypeSymbol];
553
+ const projectedType = byType.output;
554
+ if (!(isDataType(projectedType))) {
555
+ throw new Error(`Can only sort by data types, got ${printType(projectedType)}`);
556
+ }
557
+ return this[FactorySymbol]({
558
+ ast_type: "Builtin",
559
+ type: this[TypeSymbol],
560
+ location: get_location(2),
561
+ builtin: "ArraySort",
562
+ type_parameters: [this.value_type, projectedType],
563
+ arguments: [this[AstSymbol], byExpr[AstSymbol]],
564
+ });
565
+ }
566
+ /**
567
+ * Reverses the array in place.
568
+ *
569
+ * @returns Null (mutates the array in place)
570
+ *
571
+ * @example
572
+ * ```ts
573
+ * const reverseArray = East.function([ArrayType(IntegerType)], ArrayType(IntegerType), ($, arr) => {
574
+ * arr.reverseInPlace();
575
+ * $.return(arr);
576
+ * });
577
+ * const compiled = East.compile(reverseArray.toIR(), []);
578
+ * compiled([1n, 2n, 3n, 4n, 5n]); // [5n, 4n, 3n, 2n, 1n]
579
+ * ```
580
+ *
581
+ * @see {@link reverse} to produce a new reversed array instead of reversing in place.
582
+ */
583
+ reverseInPlace() {
584
+ return this[FactorySymbol]({
585
+ ast_type: "Builtin",
586
+ type: NullType,
587
+ location: get_location(2),
588
+ builtin: "ArrayReverseInPlace",
589
+ type_parameters: [this.value_type],
590
+ arguments: [this[AstSymbol]],
591
+ });
592
+ }
593
+ /**
594
+ * Produces a new array with the values in reverse order.
595
+ *
596
+ * @returns A new ArrayExpr with elements in reverse order
597
+ *
598
+ * @remarks
599
+ * This method does not mutate the original array.
600
+ *
601
+ * @example
602
+ * ```ts
603
+ * const reverseArray = East.function([ArrayType(StringType)], ArrayType(StringType), ($, arr) => {
604
+ * $.return(arr.reverse());
605
+ * });
606
+ * const compiled = East.compile(reverseArray.toIR(), []);
607
+ * compiled(["a", "b", "c"]); // ["c", "b", "a"]
608
+ * ```
609
+ *
610
+ * @see {@link reverseInPlace} to reverse the array in place.
611
+ */
612
+ reverse() {
613
+ return this[FactorySymbol]({
614
+ ast_type: "Builtin",
615
+ type: this[TypeSymbol],
616
+ location: get_location(2),
617
+ builtin: "ArrayReverse",
618
+ type_parameters: [this.value_type],
619
+ arguments: [this[AstSymbol]],
620
+ });
621
+ }
622
+ /**
623
+ * Checks if the array is sorted in ascending order.
624
+ *
625
+ * @param by - Optional projection function to determine sort order (defaults to sorting by the values themselves)
626
+ * @returns A BooleanExpr indicating whether the array is sorted
627
+ *
628
+ * @example
629
+ * ```ts
630
+ * const checkSorted = East.function([ArrayType(IntegerType)], BooleanType, ($, arr) => {
631
+ * $.return(arr.isSorted());
632
+ * });
633
+ * const compiled = East.compile(checkSorted.toIR(), []);
634
+ * compiled([1n, 2n, 3n, 4n]); // true
635
+ * compiled([1n, 3n, 2n, 4n]); // false
636
+ * ```
637
+ *
638
+ * @example
639
+ * ```ts
640
+ * // Checking if sorted by a specific field
641
+ * const PersonType = StructType({ name: StringType, age: IntegerType });
642
+ * const checkSortedByAge = East.function([ArrayType(PersonType)], BooleanType, ($, people) => {
643
+ * $.return(people.isSorted(($, p) => p.age));
644
+ * });
645
+ * const compiled = East.compile(checkSortedByAge.toIR(), []);
646
+ * compiled([{ name: "Bob", age: 25n }, { name: "Alice", age: 30n }]); // true
647
+ * ```
648
+ */
649
+ isSorted(by) {
650
+ let byExpr;
651
+ if (by === undefined) {
652
+ byExpr = Expr.function([this.value_type], this.value_type, ($, x) => x);
653
+ }
654
+ else {
655
+ byExpr = by instanceof Expr ? by : Expr.function([this.value_type], undefined, by);
656
+ }
657
+ const byType = byExpr[TypeSymbol];
658
+ const projectedType = byType.output;
659
+ if (!(isDataType(projectedType))) {
660
+ throw new Error(`Can only sort by data types, got ${printType(projectedType)}`);
661
+ }
662
+ return this[FactorySymbol]({
663
+ ast_type: "Builtin",
664
+ type: BooleanType,
665
+ location: get_location(2),
666
+ builtin: "ArrayIsSorted",
667
+ type_parameters: [this.value_type, projectedType],
668
+ arguments: [this[AstSymbol], byExpr[AstSymbol]],
669
+ });
670
+ }
671
+ /**
672
+ * Returns the index of the first element in a sorted array that is >= the given value (binary search).
673
+ *
674
+ * @param value - The value to search for
675
+ * @param by - Optional projection function used for sorting (must match the array's sort order)
676
+ * @returns An IntegerExpr representing the index (may be out of bounds if all elements are less)
677
+ *
678
+ * @remarks
679
+ * This method assumes the array is already sorted. If all elements are less than the given value,
680
+ * the returned index will equal the array size (out-of-bounds).
681
+ *
682
+ * @example
683
+ * ```ts
684
+ * const findFirst = East.function([ArrayType(IntegerType), IntegerType], IntegerType, ($, arr, value) => {
685
+ * $.return(arr.findSortedFirst(value));
686
+ * });
687
+ * const compiled = East.compile(findFirst.toIR(), []);
688
+ * compiled([1n, 3n, 5n, 7n, 9n], 5n); // 2n (index of 5)
689
+ * compiled([1n, 3n, 5n, 7n, 9n], 4n); // 2n (index where 4 would be inserted)
690
+ * compiled([1n, 3n, 5n, 7n, 9n], 10n); // 5n (out of bounds)
691
+ * ```
692
+ *
693
+ * @example
694
+ * ```ts
695
+ * // Using with projection function
696
+ * const PersonType = StructType({ name: StringType, age: IntegerType });
697
+ * const findByAge = East.function([ArrayType(PersonType), IntegerType], IntegerType, ($, people, age) => {
698
+ * $.return(people.findSortedFirst(age, ($, p) => p.age));
699
+ * });
700
+ * const compiled = East.compile(findByAge.toIR(), []);
701
+ * // Assumes people array is sorted by age
702
+ * compiled([{ name: "Bob", age: 25n }, { name: "Alice", age: 30n }], 28n); // 1n
703
+ * ```
704
+ */
705
+ findSortedFirst(value, by) {
706
+ const valueAst = valueOrExprToAst(value);
707
+ const projectedType = valueAst.type;
708
+ if (!isDataType(projectedType)) {
709
+ throw new Error(`Can only search for data types, got ${printType(projectedType)}`);
710
+ }
711
+ let byExpr;
712
+ if (by === undefined) {
713
+ // It would be cool to infer an "obvious" projection here, e.g. prefix of a struct
714
+ byExpr = Expr.function([this.value_type], projectedType, ($, x) => x);
715
+ }
716
+ else {
717
+ byExpr = by instanceof Expr ? by : Expr.function([this.value_type], projectedType, by);
718
+ }
719
+ return this[FactorySymbol]({
720
+ ast_type: "Builtin",
721
+ type: IntegerType,
722
+ location: get_location(2),
723
+ builtin: "ArrayFindSortedFirst",
724
+ type_parameters: [this.value_type, projectedType],
725
+ arguments: [this[AstSymbol], valueAst, byExpr[AstSymbol]],
726
+ });
727
+ }
728
+ /**
729
+ * Returns the index of the last element in a sorted array that is <= the given value (binary search).
730
+ *
731
+ * @param value - The value to search for
732
+ * @param by - Optional projection function used for sorting (must match the array's sort order)
733
+ * @returns An IntegerExpr representing the index (may be -1 if all elements are greater)
734
+ *
735
+ * @remarks
736
+ * This method assumes the array is already sorted. If all elements are greater than the given value,
737
+ * the returned index will be -1 (out-of-bounds).
738
+ *
739
+ * @example
740
+ * ```ts
741
+ * const findLast = East.function([ArrayType(IntegerType), IntegerType], IntegerType, ($, arr, value) => {
742
+ * $.return(arr.findSortedLast(value));
743
+ * });
744
+ * const compiled = East.compile(findLast.toIR(), []);
745
+ * compiled([1n, 3n, 5n, 7n, 9n], 5n); // 2n (index of 5)
746
+ * compiled([1n, 3n, 5n, 7n, 9n], 6n); // 2n (index of last element <= 6)
747
+ * compiled([1n, 3n, 5n, 7n, 9n], 0n); // -1n (all elements are greater)
748
+ * ```
749
+ */
750
+ findSortedLast(value, by) {
751
+ const valueAst = valueOrExprToAst(value);
752
+ const projectedType = valueAst.type;
753
+ if (!isDataType(projectedType)) {
754
+ throw new Error(`Can only search for data types, got ${printType(projectedType)}`);
755
+ }
756
+ let byExpr;
757
+ if (by === undefined) {
758
+ // It would be cool to infer an "obvious" projection here, e.g. prefix of a struct
759
+ byExpr = Expr.function([this.value_type], projectedType, ($, x) => x);
760
+ }
761
+ else {
762
+ byExpr = by instanceof Expr ? by : Expr.function([this.value_type], projectedType, by);
763
+ }
764
+ return this[FactorySymbol]({
765
+ ast_type: "Builtin",
766
+ type: IntegerType,
767
+ location: get_location(2),
768
+ builtin: "ArrayFindSortedLast",
769
+ type_parameters: [this.value_type, projectedType],
770
+ arguments: [this[AstSymbol], valueAst, byExpr[AstSymbol]],
771
+ });
772
+ }
773
+ /**
774
+ * Returns the start and end indices of elements in a sorted array that equal the given value (binary search).
775
+ *
776
+ * @param value - The value to search for
777
+ * @param by - Optional projection function used for sorting (must match the array's sort order)
778
+ * @returns A struct with `start` and `end` indices (exclusive end, may be out of bounds if not found)
779
+ *
780
+ * @remarks
781
+ * This method assumes the array is already sorted. If no elements match, the range will have zero size.
782
+ * The indices may be out-of-bounds if no elements are found.
783
+ *
784
+ * @example
785
+ * ```ts
786
+ * const findRange = East.function([ArrayType(IntegerType), IntegerType], StructType({ start: IntegerType, end: IntegerType }), ($, arr, value) => {
787
+ * $.return(arr.findSortedRange(value));
788
+ * });
789
+ * const compiled = East.compile(findRange.toIR(), []);
790
+ * compiled([1n, 3n, 5n, 5n, 5n, 7n, 9n], 5n); // { start: 2n, end: 5n }
791
+ * compiled([1n, 3n, 5n, 7n, 9n], 4n); // { start: 2n, end: 2n } (not found)
792
+ * ```
793
+ */
794
+ findSortedRange(value, by) {
795
+ const valueAst = valueOrExprToAst(value);
796
+ const projectedType = valueAst.type;
797
+ if (!isDataType(projectedType)) {
798
+ throw new Error(`Can only search for data types, got ${printType(projectedType)}`);
799
+ }
800
+ let byExpr;
801
+ if (by === undefined) {
802
+ // It would be cool to infer an "obvious" projection here, e.g. prefix of a struct
803
+ byExpr = Expr.function([this.value_type], projectedType, ($, x) => x);
804
+ }
805
+ else {
806
+ byExpr = by instanceof Expr ? by : Expr.function([this.value_type], projectedType, by);
807
+ }
808
+ return this[FactorySymbol]({
809
+ ast_type: "Builtin",
810
+ type: StructType({ start: IntegerType, end: IntegerType }),
811
+ location: get_location(2),
812
+ builtin: "ArrayFindSortedRange",
813
+ type_parameters: [this.value_type, projectedType],
814
+ arguments: [this[AstSymbol], valueAst, byExpr[AstSymbol]],
815
+ });
816
+ }
817
+ /**
818
+ * Extracts a slice of the array from start to end index (exclusive).
819
+ *
820
+ * @param start - The starting index (inclusive)
821
+ * @param end - The ending index (exclusive)
822
+ * @returns A new ArrayExpr containing the sliced elements
823
+ *
824
+ * @remarks
825
+ * This method does not mutate the original array.
826
+ *
827
+ * @example
828
+ * ```ts
829
+ * const sliceArray = East.function([ArrayType(IntegerType), IntegerType, IntegerType], ArrayType(IntegerType), ($, arr, start, end) => {
830
+ * $.return(arr.slice(start, end));
831
+ * });
832
+ * const compiled = East.compile(sliceArray.toIR(), []);
833
+ * compiled([10n, 20n, 30n, 40n, 50n], 1n, 4n); // [20n, 30n, 40n]
834
+ * compiled([10n, 20n, 30n, 40n, 50n], 0n, 2n); // [10n, 20n]
835
+ * ```
836
+ */
837
+ slice(start, end) {
838
+ const startExpr = Expr.from(start, IntegerType);
839
+ const endExpr = Expr.from(end, IntegerType);
840
+ return this[FactorySymbol]({
841
+ ast_type: "Builtin",
842
+ type: this[TypeSymbol],
843
+ location: get_location(2),
844
+ builtin: "ArraySlice",
845
+ type_parameters: [this.value_type],
846
+ arguments: [this[AstSymbol], startExpr[AstSymbol], endExpr[AstSymbol]],
847
+ });
848
+ }
849
+ /**
850
+ * Creates a new array containing values from this array followed by values of the other array.
851
+ *
852
+ * @param other - The array to concatenate
853
+ * @returns A new ArrayExpr with combined elements
854
+ *
855
+ * @remarks
856
+ * This method does not mutate the original arrays.
857
+ *
858
+ * @example
859
+ * ```ts
860
+ * const concatArrays = East.function([ArrayType(IntegerType), ArrayType(IntegerType)], ArrayType(IntegerType), ($, arr1, arr2) => {
861
+ * $.return(arr1.concat(arr2));
862
+ * });
863
+ * const compiled = East.compile(concatArrays.toIR(), []);
864
+ * compiled([1n, 2n, 3n], [4n, 5n]); // [1n, 2n, 3n, 4n, 5n]
865
+ * ```
866
+ */
867
+ concat(other) {
868
+ const otherExpr = Expr.from(other, this[TypeSymbol]);
869
+ return this[FactorySymbol]({
870
+ ast_type: "Builtin",
871
+ type: this[TypeSymbol],
872
+ location: get_location(2),
873
+ builtin: "ArrayConcat",
874
+ type_parameters: [this.value_type],
875
+ arguments: [this[AstSymbol], otherExpr[AstSymbol]],
876
+ });
877
+ }
878
+ /**
879
+ * Returns a new array containing values at the specified indices.
880
+ *
881
+ * @param keys - An array of indices to retrieve
882
+ * @param onMissing - Optional function to provide default values for out-of-bounds indices
883
+ * @returns A new ArrayExpr with elements at the specified indices
884
+ *
885
+ * @throws East runtime error if an index is out of bounds and no onMissing function is provided
886
+ *
887
+ * @example
888
+ * ```ts
889
+ * const getMultiple = East.function([ArrayType(StringType), ArrayType(IntegerType)], ArrayType(StringType), ($, arr, indices) => {
890
+ * $.return(arr.getKeys(indices));
891
+ * });
892
+ * const compiled = East.compile(getMultiple.toIR(), []);
893
+ * compiled(["a", "b", "c", "d"], [0n, 2n, 3n]); // ["a", "c", "d"]
894
+ * ```
895
+ *
896
+ * @example
897
+ * ```ts
898
+ * // With default for missing keys
899
+ * const getWithDefault = East.function([ArrayType(IntegerType), ArrayType(IntegerType)], ArrayType(IntegerType), ($, arr, indices) => {
900
+ * $.return(arr.getKeys(indices, ($, key) => -1n));
901
+ * });
902
+ * const compiled = East.compile(getWithDefault.toIR(), []);
903
+ * compiled([10n, 20n, 30n], [1n, 5n, 2n]); // [20n, -1n, 30n]
904
+ * ```
905
+ */
906
+ getKeys(keys, onMissing) {
907
+ const keysExpr = Expr.from(keys, ArrayType(IntegerType));
908
+ let default_function_ast;
909
+ if (onMissing === undefined) {
910
+ const location = get_location(2);
911
+ const default_function = Expr.function([IntegerType], this.value_type, ($, key) => $.error(Expr.str `Cannot get key ${key} from array`, location));
912
+ default_function_ast = Expr.ast(default_function);
913
+ }
914
+ else {
915
+ const default_function_expr = Expr.from(onMissing, FunctionType([IntegerType], this.value_type, null));
916
+ default_function_ast = Expr.ast(default_function_expr);
917
+ }
918
+ return this[FactorySymbol]({
919
+ ast_type: "Builtin",
920
+ type: this[TypeSymbol],
921
+ location: get_location(2),
922
+ builtin: "ArrayGetKeys",
923
+ type_parameters: [this.value_type],
924
+ arguments: [this[AstSymbol], keysExpr[AstSymbol], default_function_ast],
925
+ });
926
+ }
927
+ /**
928
+ * Executes a function for each element in the array.
929
+ *
930
+ * @param fn - Function taking (element, index) to execute for each entry
931
+ * @returns Null
932
+ *
933
+ * @example
934
+ * ```ts
935
+ * const log = East.platform(FunctionType([StringType], NullType));
936
+ * const forEachExample = East.function([ArrayType(IntegerType)], NullType, ($, arr) => {
937
+ * arr.forEach(($, elem, index) => {
938
+ * log(Expr.str`Index ${index}: ${elem}`);
939
+ * });
940
+ * $.return(null);
941
+ * });
942
+ * const compiled = East.compile(forEachExample.toIR(), [log.implement(console.log)]);
943
+ * compiled([10n, 20n, 30n]);
944
+ * // Logs: "Index 0: 10", "Index 1: 20", "Index 2: 30"
945
+ * ```
946
+ */
947
+ forEach(fn) {
948
+ if (!(fn instanceof Expr)) {
949
+ fn = Expr.function([this.value_type, IntegerType], undefined, fn);
950
+ }
951
+ const fnType = fn[TypeSymbol];
952
+ const returnType = fnType.output;
953
+ return this[FactorySymbol]({
954
+ ast_type: "Builtin",
955
+ type: NullType,
956
+ location: get_location(2),
957
+ builtin: "ArrayForEach",
958
+ type_parameters: [this.value_type, returnType],
959
+ arguments: [this[AstSymbol], fn[AstSymbol]],
960
+ });
961
+ }
962
+ /**
963
+ * Creates a shallow copy of the array.
964
+ *
965
+ * @returns A new ArrayExpr with the same elements
966
+ *
967
+ * @remarks
968
+ * Only the top-level array is cloned. Deeply nested structures (e.g., arrays of arrays) share references.
969
+ *
970
+ * @example
971
+ * ```ts
972
+ * const copyArray = East.function([ArrayType(IntegerType)], ArrayType(IntegerType), ($, arr) => {
973
+ * const copy = arr.copy();
974
+ * copy.pushLast(99n);
975
+ * $.return(copy);
976
+ * });
977
+ * const compiled = East.compile(copyArray.toIR(), []);
978
+ * const original = [1n, 2n, 3n];
979
+ * compiled(original); // [1n, 2n, 3n, 99n]
980
+ * // original is unchanged: [1n, 2n, 3n]
981
+ * ```
982
+ */
983
+ copy() {
984
+ return this[FactorySymbol]({
985
+ ast_type: "Builtin",
986
+ type: this[TypeSymbol],
987
+ location: get_location(2),
988
+ builtin: "ArrayCopy",
989
+ type_parameters: [this.value_type],
990
+ arguments: [this[AstSymbol]],
991
+ });
992
+ }
993
+ map(fn) {
994
+ // We need to infer the output type
995
+ if (fn instanceof Expr) {
996
+ // The function was provided as an expression
997
+ if (!(fn[TypeSymbol] && fn[TypeSymbol].type === "Function")) {
998
+ throw new Error("Expected a Function expression");
999
+ }
1000
+ const output_type = fn[TypeSymbol].output;
1001
+ const n_inputs = fn[TypeSymbol].inputs.length;
1002
+ if (n_inputs === 2) {
1003
+ // Two input function (value, index)
1004
+ if (!isSubtype(this.value_type, fn[TypeSymbol].inputs[0])) {
1005
+ throw new Error(`Expected Function input to be ${printType(this.value_type)}, got ${printType(fn[TypeSymbol].inputs[0])}`);
1006
+ }
1007
+ if (!isTypeEqual(IntegerType, fn[TypeSymbol].inputs[1])) {
1008
+ throw new Error(`Expected Function second input to be ${printType(IntegerType)}, got ${printType(fn[TypeSymbol].inputs[1])}`);
1009
+ }
1010
+ return this[FactorySymbol]({
1011
+ ast_type: "Builtin",
1012
+ type: ArrayType(output_type),
1013
+ location: get_location(2),
1014
+ builtin: "ArrayMap",
1015
+ type_parameters: [this.value_type, output_type],
1016
+ arguments: [this[AstSymbol], fn[AstSymbol]],
1017
+ });
1018
+ }
1019
+ else {
1020
+ throw new Error(`Expected Function to have 2 inputs, got ${n_inputs} inputs`);
1021
+ }
1022
+ }
1023
+ else {
1024
+ // The function was provided as a lambda
1025
+ // We need to convert it to a FunctionExpr first, but we also need to infer the output type (using undefined)
1026
+ const functionExpr = Expr.function([this.value_type, IntegerType], undefined, fn);
1027
+ return this.map(functionExpr);
1028
+ }
1029
+ }
1030
+ /**
1031
+ * Filters array elements using a predicate function.
1032
+ *
1033
+ * @param predicate - Function taking (element, index) and returning true to keep the element
1034
+ * @returns A new ArrayExpr containing only elements for which the predicate returned true
1035
+ *
1036
+ * @example
1037
+ * ```ts
1038
+ * const filterEven = East.function([ArrayType(IntegerType)], ArrayType(IntegerType), ($, arr) => {
1039
+ * $.return(arr.filter(($, x, i) => x.modulo(2n).equal(0n)));
1040
+ * });
1041
+ * const compiled = East.compile(filterEven.toIR(), []);
1042
+ * compiled([1n, 2n, 3n, 4n, 5n, 6n]); // [2n, 4n, 6n]
1043
+ * ```
1044
+ *
1045
+ * @example
1046
+ * ```ts
1047
+ * // Filter by index
1048
+ * const filterFirstThree = East.function([ArrayType(StringType)], ArrayType(StringType), ($, arr) => {
1049
+ * $.return(arr.filter(($, elem, index) => index.lessThan(3n)));
1050
+ * });
1051
+ * const compiled = East.compile(filterFirstThree.toIR(), []);
1052
+ * compiled(["a", "b", "c", "d", "e"]); // ["a", "b", "c"]
1053
+ * ```
1054
+ *
1055
+ * @example
1056
+ * ```ts
1057
+ * // Filter structs by field
1058
+ * const PersonType = StructType({ name: StringType, age: IntegerType });
1059
+ * const filterAdults = East.function([ArrayType(PersonType)], ArrayType(PersonType), ($, people) => {
1060
+ * $.return(people.filter(($, p, i) => p.age.greaterOrEqual(18n)));
1061
+ * });
1062
+ * const compiled = East.compile(filterAdults.toIR(), []);
1063
+ * compiled([{ name: "Alice", age: 30n }, { name: "Bob", age: 15n }]); // [{ name: "Alice", age: 30n }]
1064
+ * ```
1065
+ */
1066
+ filter(predicate) {
1067
+ if (!(predicate instanceof Expr)) {
1068
+ predicate = Expr.function([this.value_type, IntegerType], BooleanType, predicate);
1069
+ }
1070
+ // Check if it's actually a FunctionExpr by looking at its type
1071
+ if (!predicate[TypeSymbol] || predicate[TypeSymbol].type !== "Function") {
1072
+ throw new Error("forEach expects a Function expression");
1073
+ }
1074
+ const functionType = predicate[TypeSymbol];
1075
+ if (functionType.inputs.length === 2) {
1076
+ // Two input function (value, index)
1077
+ if (!isSubtype(this.value_type, functionType.inputs[0])) {
1078
+ throw new Error(`Expected Function input to be ${printType(this.value_type)}, got ${printType(functionType.inputs[0])}`);
1079
+ }
1080
+ if (!isTypeEqual(IntegerType, functionType.inputs[1])) {
1081
+ throw new Error(`Expected Function second input to be ${printType(IntegerType)}, got ${printType(functionType.inputs[1])}`);
1082
+ }
1083
+ return this[FactorySymbol]({
1084
+ ast_type: "Builtin",
1085
+ location: get_location(2),
1086
+ type: this[TypeSymbol],
1087
+ builtin: "ArrayFilter",
1088
+ type_parameters: [this.value_type],
1089
+ arguments: [this[AstSymbol], predicate[AstSymbol]],
1090
+ });
1091
+ }
1092
+ else {
1093
+ throw new Error(`Expected Function to have 2 inputs, got ${functionType.inputs.length} inputs`);
1094
+ }
1095
+ }
1096
+ filterMap(fn) {
1097
+ const fnAst = valueOrExprToAstTyped(fn, FunctionType([this.value_type, IntegerType], undefined, null));
1098
+ const returnType = fnAst.type.output;
1099
+ if (returnType.type !== "Variant") {
1100
+ throw new Error(`Expected Function to return an Option type, got ${printType(returnType)}`);
1101
+ }
1102
+ if (!Object.keys(returnType.cases).every(k => k === "none" || k === "some")) {
1103
+ throw new Error(`Expected Function to return an Option type, got ${printType(returnType)}`);
1104
+ }
1105
+ const someType = returnType.cases["some"] ?? NeverType;
1106
+ return this[FactorySymbol]({
1107
+ ast_type: "Builtin",
1108
+ type: ArrayType(someType),
1109
+ location: get_location(2),
1110
+ builtin: "ArrayFilterMap",
1111
+ type_parameters: [this.value_type, someType],
1112
+ arguments: [this[AstSymbol], fnAst],
1113
+ });
1114
+ }
1115
+ firstMap(fn) {
1116
+ const fnAst = valueOrExprToAstTyped(fn, FunctionType([this.value_type, IntegerType], undefined, null));
1117
+ const returnType = fnAst.type.output;
1118
+ if (returnType.type !== "Variant") {
1119
+ throw new Error(`Expected Function to return an Option type, got ${printType(returnType)}`);
1120
+ }
1121
+ if (!Object.keys(returnType.cases).every(k => k === "none" || k === "some")) {
1122
+ throw new Error(`Expected Function to return an Option type, got ${printType(returnType)}`);
1123
+ }
1124
+ const someType = returnType.cases["some"] ?? NeverType;
1125
+ return this[FactorySymbol]({
1126
+ ast_type: "Builtin",
1127
+ type: returnType,
1128
+ location: get_location(2),
1129
+ builtin: "ArrayFirstMap",
1130
+ type_parameters: [this.value_type, someType],
1131
+ arguments: [this[AstSymbol], fnAst],
1132
+ });
1133
+ }
1134
+ findFirst(value, by) {
1135
+ if (by === undefined) {
1136
+ return this.firstMap(($, v, k) => Expr.equal(v, value).ifElse(() => some(k), () => none));
1137
+ }
1138
+ else {
1139
+ const byExpr = (by instanceof Expr ? by : Expr.function([this.value_type, IntegerType], undefined, by));
1140
+ if (byExpr[TypeSymbol].type !== "Function") {
1141
+ throw new Error("Expected a Function expression for 'by' parameter");
1142
+ }
1143
+ const byType = byExpr[TypeSymbol];
1144
+ if (byType.inputs.length !== 2) {
1145
+ throw new Error(`Expected 'by' function to have 2 inputs, got ${byType.inputs.length}`);
1146
+ }
1147
+ if (!isSubtype(this.value_type, byType.inputs[0])) {
1148
+ throw new Error(`Expected 'by' function input to be ${printType(this.value_type)}, got ${printType(byType.inputs[0])}`);
1149
+ }
1150
+ return this.firstMap(($, v, k) => Expr.equal(byExpr(v, k), value).ifElse(() => some(k), () => none));
1151
+ }
1152
+ }
1153
+ findAll(value, by) {
1154
+ if (by === undefined) {
1155
+ return this.filterMap(($, v, k) => Expr.equal(v, value).ifElse(() => some(k), () => none));
1156
+ }
1157
+ else {
1158
+ const byExpr = (by instanceof Expr ? by : Expr.function([this.value_type, IntegerType], undefined, by));
1159
+ if (byExpr[TypeSymbol].type !== "Function") {
1160
+ throw new Error("Expected a Function expression for 'by' parameter");
1161
+ }
1162
+ const byType = byExpr[TypeSymbol];
1163
+ if (byType.inputs.length !== 2) {
1164
+ throw new Error(`Expected 'by' function to have 2 inputs, got ${byType.inputs.length}`);
1165
+ }
1166
+ if (!isSubtype(this.value_type, byType.inputs[0])) {
1167
+ throw new Error(`Expected 'by' function input to be ${printType(this.value_type)}, got ${printType(byType.inputs[0])}`);
1168
+ }
1169
+ return this.filterMap(($, v, k) => Expr.equal(byExpr(v, k), value).ifElse(() => some(k), () => none));
1170
+ }
1171
+ }
1172
+ /**
1173
+ * Reduces the array to a single value using an accumulator function and initial value.
1174
+ *
1175
+ * @param combineFn - Function taking (accumulator, element, index) and returning the new accumulator value
1176
+ * @param init - Initial value for the reduction (determines output type)
1177
+ * @returns The final accumulated value
1178
+ *
1179
+ * @remarks
1180
+ * The accumulator function is called for each element with the previous result (or initial value for first element),
1181
+ * the current value, and the current index. This operation is safe on empty arrays (returns the initial value).
1182
+ *
1183
+ * @example
1184
+ * ```ts
1185
+ * const sumArray = East.function([ArrayType(IntegerType)], IntegerType, ($, arr) => {
1186
+ * $.return(arr.reduce(($, acc, val, index) => acc.add(val), 0n));
1187
+ * });
1188
+ * const compiled = East.compile(sumArray.toIR(), []);
1189
+ * compiled([1n, 2n, 3n, 4n]); // 10n
1190
+ * compiled([]); // 0n (initial value)
1191
+ * ```
1192
+ *
1193
+ * @example
1194
+ * ```ts
1195
+ * // Building a string from array
1196
+ * const joinArray = East.function([ArrayType(StringType)], StringType, ($, arr) => {
1197
+ * $.return(arr.reduce(($, acc, val, index) =>
1198
+ * $.if(index.equal(0n), () => val, () => acc.concat(", ").concat(val))
1199
+ * , ""));
1200
+ * });
1201
+ * const compiled = East.compile(joinArray.toIR(), []);
1202
+ * compiled(["a", "b", "c"]); // "a, b, c"
1203
+ * ```
1204
+ *
1205
+ * @see {@link mapReduce} for a version that projects each value before reducing (and errors on empty arrays).
1206
+ * @see {@link sum}, {@link mean}, {@link every}, and {@link some} for common reduction operations.
1207
+ */
1208
+ reduce(combineFn, init) {
1209
+ const initAst = valueOrExprToAst(init);
1210
+ const returnType = initAst.type;
1211
+ const combineAst = valueOrExprToAstTyped(combineFn, FunctionType([returnType, this.value_type, IntegerType], returnType, null));
1212
+ return this[FactorySymbol]({
1213
+ ast_type: "Builtin",
1214
+ type: returnType,
1215
+ location: get_location(2),
1216
+ builtin: "ArrayFold",
1217
+ type_parameters: [this.value_type, returnType],
1218
+ arguments: [this[AstSymbol], initAst, combineAst],
1219
+ });
1220
+ }
1221
+ mapReduce(mapFn, combineFn) {
1222
+ const mapAst = valueOrExprToAstTyped(mapFn, FunctionType([this.value_type, IntegerType], undefined, null));
1223
+ const mapType = mapAst.type.output;
1224
+ const combineAst = valueOrExprToAstTyped(combineFn, FunctionType([mapType, mapType], mapType, null));
1225
+ return this[FactorySymbol]({
1226
+ ast_type: "Builtin",
1227
+ type: mapType,
1228
+ location: get_location(2),
1229
+ builtin: "ArrayMapReduce",
1230
+ type_parameters: [this.value_type, mapType],
1231
+ arguments: [this[AstSymbol], mapAst, combineAst],
1232
+ });
1233
+ }
1234
+ // Common reducers are provided, based on reduce
1235
+ /**
1236
+ * Returns true if every element satisfies the predicate, false otherwise.
1237
+ *
1238
+ * @param fn - Optional predicate function (required for non-Boolean arrays)
1239
+ * @returns A BooleanExpr (true for empty arrays)
1240
+ *
1241
+ * @remarks
1242
+ * This method short-circuits on the first false element for efficiency.
1243
+ *
1244
+ * @example
1245
+ * ```ts
1246
+ * const allPositive = East.function([ArrayType(IntegerType)], BooleanType, ($, arr) => {
1247
+ * $.return(arr.every(($, x, i) => x.greater(0n)));
1248
+ * });
1249
+ * const compiled = East.compile(allPositive.toIR(), []);
1250
+ * compiled([1n, 2n, 3n]); // true
1251
+ * compiled([1n, -2n, 3n]); // false
1252
+ * compiled([]); // true (empty array)
1253
+ * ```
1254
+ *
1255
+ * @example
1256
+ * ```ts
1257
+ * // Checking structs
1258
+ * const PersonType = StructType({ name: StringType, age: IntegerType });
1259
+ * const allAdults = East.function([ArrayType(PersonType)], BooleanType, ($, people) => {
1260
+ * $.return(people.every(($, p, i) => p.age.greaterOrEqual(18n)));
1261
+ * });
1262
+ * const compiled = East.compile(allAdults.toIR(), []);
1263
+ * compiled([{ name: "Alice", age: 30n }, { name: "Bob", age: 25n }]); // true
1264
+ * ```
1265
+ *
1266
+ * @see {@link some} to check if at least one element is true.
1267
+ */
1268
+ every(fn) {
1269
+ if (fn === undefined) {
1270
+ if (!isTypeEqual(this.value_type, BooleanType)) {
1271
+ throw new Error(`Can only perform every on array of booleans, got ${printType(this.value_type)}`);
1272
+ }
1273
+ // Short-circuit on first false value
1274
+ const result = this.firstMap(($, v, _k) => v.not().ifElse(() => some(null), () => none));
1275
+ return Expr.match(result, { some: () => false, none: () => true });
1276
+ }
1277
+ const fnAst = valueOrExprToAstTyped(fn, FunctionType([this.value_type, IntegerType], BooleanType, null));
1278
+ // Short-circuit on first false value
1279
+ const result = this.firstMap(($, v, k) => {
1280
+ const result = Expr.fromAst(fnAst)(v, k);
1281
+ return result.not().ifElse(() => some(null), () => none);
1282
+ });
1283
+ return Expr.match(result, { some: () => false, none: () => true });
1284
+ }
1285
+ /**
1286
+ * Returns true if at least one element satisfies the predicate, false otherwise.
1287
+ *
1288
+ * @param fn - Optional predicate function (required for non-Boolean arrays)
1289
+ * @returns A BooleanExpr (false for empty arrays)
1290
+ *
1291
+ * @remarks
1292
+ * This method short-circuits on the first true element for efficiency.
1293
+ *
1294
+ * @example
1295
+ * ```ts
1296
+ * const hasNegative = East.function([ArrayType(IntegerType)], BooleanType, ($, arr) => {
1297
+ * $.return(arr.some(($, x, i) => x.less(0n)));
1298
+ * });
1299
+ * const compiled = East.compile(hasNegative.toIR(), []);
1300
+ * compiled([1n, 2n, 3n]); // false
1301
+ * compiled([1n, -2n, 3n]); // true
1302
+ * compiled([]); // false (empty array)
1303
+ * ```
1304
+ *
1305
+ * @example
1306
+ * ```ts
1307
+ * // Checking structs
1308
+ * const PersonType = StructType({ name: StringType, age: IntegerType });
1309
+ * const hasMinor = East.function([ArrayType(PersonType)], BooleanType, ($, people) => {
1310
+ * $.return(people.some(($, p, i) => p.age.less(18n)));
1311
+ * });
1312
+ * const compiled = East.compile(hasMinor.toIR(), []);
1313
+ * compiled([{ name: "Alice", age: 30n }, { name: "Bob", age: 15n }]); // true
1314
+ * ```
1315
+ *
1316
+ * @see {@link every} to check if all elements are true.
1317
+ */
1318
+ some(fn) {
1319
+ if (fn === undefined) {
1320
+ if (!isTypeEqual(this.value_type, BooleanType)) {
1321
+ throw new Error(`Can only perform some on array of booleans, got ${printType(this.value_type)}`);
1322
+ }
1323
+ // Short-circuit on first true value
1324
+ const result = this.firstMap(($, v, _k) => v.ifElse(() => some(null), () => none));
1325
+ return Expr.match(result, { some: () => true, none: () => false });
1326
+ }
1327
+ const fnAst = valueOrExprToAstTyped(fn, FunctionType([this.value_type, IntegerType], BooleanType, null));
1328
+ // Short-circuit on first true value
1329
+ const result = this.firstMap(($, v, k) => {
1330
+ const result = Expr.fromAst(fnAst)(v, k);
1331
+ return result.ifElse(() => some(null), () => none);
1332
+ });
1333
+ return Expr.match(result, { some: () => true, none: () => false });
1334
+ }
1335
+ sum(fn) {
1336
+ if (fn === undefined) {
1337
+ if (!(isTypeEqual(this.value_type, IntegerType) || isTypeEqual(this.value_type, FloatType))) {
1338
+ throw new Error(`Can only perform sum on array of numbers (Integer or Float), got ${printType(this.value_type)}`);
1339
+ }
1340
+ const zero = isTypeEqual(this.value_type, IntegerType) ? 0n : 0.0;
1341
+ return this.reduce(($, previous, value) => previous.add(value), zero);
1342
+ }
1343
+ else {
1344
+ const fnAst = valueOrExprToAstTyped(fn, FunctionType([this.value_type, IntegerType], undefined, null));
1345
+ const returnType = fnAst.type.output;
1346
+ if (!(isTypeEqual(returnType, IntegerType) || isTypeEqual(returnType, FloatType))) {
1347
+ throw new Error(`Can only perform sum on array of numbers (Integer or Float), got ${printType(returnType)}`);
1348
+ }
1349
+ const zero = isTypeEqual(returnType, IntegerType) ? 0n : 0.0;
1350
+ return this.reduce(($, previous, value, key) => previous.add(Expr.fromAst(fnAst)(value, key)), zero);
1351
+ }
1352
+ }
1353
+ mean(fn) {
1354
+ if (fn === undefined) {
1355
+ if (isTypeEqual(this.value_type, IntegerType)) {
1356
+ return this.reduce(($, previous, value) => previous.add(value.toFloat()), 0.0).divide(this.size().toFloat());
1357
+ }
1358
+ else if (isTypeEqual(this.value_type, FloatType)) {
1359
+ return this.reduce(($, previous, value) => previous.add(value), 0.0).divide(this.size().toFloat());
1360
+ }
1361
+ else {
1362
+ throw new Error(`Can only perform sum on array of numbers (Integer or Float), got ${printType(this.value_type)}`);
1363
+ }
1364
+ }
1365
+ else {
1366
+ const fnAst = valueOrExprToAstTyped(fn, FunctionType([this.value_type, IntegerType], undefined, null));
1367
+ const returnType = fnAst.type.output;
1368
+ if (isTypeEqual(returnType, IntegerType)) {
1369
+ return this.reduce(($, previous, value, key) => previous.add(Expr.fromAst(fnAst)(value, key).toFloat()), 0.0).divide(this.size().toFloat());
1370
+ }
1371
+ else if (isTypeEqual(returnType, FloatType)) {
1372
+ return this.reduce(($, previous, value, key) => previous.add(Expr.fromAst(fnAst)(value, key)), 0.0).divide(this.size().toFloat());
1373
+ }
1374
+ else {
1375
+ throw new Error(`Can only perform sum on array of numbers (Integer or Float), got ${printType(returnType)}`);
1376
+ }
1377
+ }
1378
+ }
1379
+ findMaximum(by) {
1380
+ if (by === undefined) {
1381
+ // No projection - compare elements directly
1382
+ if (!(isDataType(this.value_type))) {
1383
+ throw new Error(`Can only perform findMaximum on arrays of data types, got ${printType(this.value_type)}`);
1384
+ }
1385
+ // Check if array is empty first
1386
+ return Expr.equal(this.size(), 0n).ifElse(() => none, () => {
1387
+ const result = this.mapReduce(($, x, k) => ({ index: k, value: x }), ($, a, b) => Expr.greaterEqual(a.value, b.value).ifElse(() => a, () => b));
1388
+ return some(result.index);
1389
+ });
1390
+ }
1391
+ else {
1392
+ // With projection - need to track element, projected value, and index
1393
+ const byExpr = (by instanceof Expr ? by : Expr.function([this.value_type, IntegerType], undefined, by));
1394
+ if (byExpr[TypeSymbol].type !== "Function") {
1395
+ throw new Error("Expected a Function expression for 'by' parameter");
1396
+ }
1397
+ const byType = byExpr[TypeSymbol];
1398
+ const projectedType = byType.output;
1399
+ if (!isDataType(projectedType)) {
1400
+ throw new Error(`Can only compare data types, got ${printType(projectedType)}`);
1401
+ }
1402
+ // Check if array is empty first
1403
+ return Expr.equal(this.size(), 0n).ifElse(() => none, () => {
1404
+ // Map to { index, key }, reduce by comparing keys, extract index
1405
+ const result = this.mapReduce(($, x, k) => ({ index: k, key: byExpr(x, k) }), ($, a, b) => Expr.greaterEqual(a.key, b.key).ifElse(() => a, () => b));
1406
+ return some(result.index);
1407
+ });
1408
+ }
1409
+ }
1410
+ findMinimum(by) {
1411
+ if (by === undefined) {
1412
+ // No projection - compare elements directly
1413
+ if (!(isDataType(this.value_type))) {
1414
+ throw new Error(`Can only perform findMinimum on arrays of data types, got ${printType(this.value_type)}`);
1415
+ }
1416
+ // Check if array is empty first
1417
+ return Expr.equal(this.size(), 0n).ifElse(() => none, () => {
1418
+ const result = this.mapReduce(($, x, k) => ({ index: k, value: x }), ($, a, b) => Expr.lessEqual(a.value, b.value).ifElse(() => a, () => b));
1419
+ return some(result.index);
1420
+ });
1421
+ }
1422
+ else {
1423
+ // With projection - need to track element, projected value, and index
1424
+ const byExpr = (by instanceof Expr ? by : Expr.function([this.value_type, IntegerType], undefined, by));
1425
+ if (byExpr[TypeSymbol].type !== "Function") {
1426
+ throw new Error("Expected a Function expression for 'by' parameter");
1427
+ }
1428
+ const byType = byExpr[TypeSymbol];
1429
+ const projectedType = byType.output;
1430
+ if (!isDataType(projectedType)) {
1431
+ throw new Error(`Can only compare data types, got ${printType(projectedType)}`);
1432
+ }
1433
+ // Check if array is empty first
1434
+ return Expr.equal(this.size(), 0n).ifElse(() => none, () => {
1435
+ // Map to { index, key }, reduce by comparing keys, extract index
1436
+ const result = this.mapReduce(($, x, k) => ({ index: k, key: byExpr(x, k) }), ($, a, b) => Expr.lessEqual(a.key, b.key).ifElse(() => a, () => b));
1437
+ return some(result.index);
1438
+ });
1439
+ }
1440
+ }
1441
+ maximum(by) {
1442
+ if (by === undefined) {
1443
+ // No projection - compare elements directly
1444
+ // Use mapReduce with lambda functions directly
1445
+ return this.mapReduce(($, x) => x, Expr.function([this.value_type, this.value_type], this.value_type, ($, a, b) => Expr.max(a, b)));
1446
+ }
1447
+ else {
1448
+ // With projection - need to track both element and projected value
1449
+ const byExpr = (by instanceof Expr ? by : Expr.function([this.value_type, IntegerType], undefined, by));
1450
+ if (byExpr[TypeSymbol].type !== "Function") {
1451
+ throw new Error("Expected a Function expression for 'by' parameter");
1452
+ }
1453
+ const byType = byExpr[TypeSymbol];
1454
+ const projectedType = byType.output;
1455
+ if (!isDataType(projectedType)) {
1456
+ throw new Error(`Can only compare data types, got ${printType(projectedType)}`);
1457
+ }
1458
+ // Map to { element, key }, reduce by comparing keys, extract element
1459
+ // Use greaterEqual to keep the first element when keys are equal
1460
+ const result = this.mapReduce(($, x, k) => ({ element: x, key: byExpr(x, k) }), ($, a, b) => Expr.greaterEqual(a.key, b.key).ifElse(() => a, () => b));
1461
+ return result.element;
1462
+ }
1463
+ }
1464
+ minimum(by) {
1465
+ if (by === undefined) {
1466
+ // No projection - compare elements directly
1467
+ if (!(isDataType(this.value_type))) {
1468
+ throw new Error(`Can only perform minimum on arrays of data types, got ${printType(this.value_type)}`);
1469
+ }
1470
+ // Use mapReduce with lambda functions directly
1471
+ return this.mapReduce(($, x) => x, Expr.function([this.value_type, this.value_type], this.value_type, ($, a, b) => Expr.min(a, b)));
1472
+ }
1473
+ else {
1474
+ // With projection - need to track both element and projected value
1475
+ const byExpr = (by instanceof Expr ? by : Expr.function([this.value_type, IntegerType], undefined, by));
1476
+ if (byExpr[TypeSymbol].type !== "Function") {
1477
+ throw new Error("Expected a Function expression for 'by' parameter");
1478
+ }
1479
+ const byType = byExpr[TypeSymbol];
1480
+ const projectedType = byType.output;
1481
+ if (!isDataType(projectedType)) {
1482
+ throw new Error(`Can only compare data types, got ${printType(projectedType)}`);
1483
+ }
1484
+ // Map to { element, key }, reduce by comparing keys, extract element
1485
+ // Use lessEqual to keep the first element when keys are equal
1486
+ const result = this.mapReduce(($, x, k) => ({ element: x, key: byExpr(x, k) }), ($, a, b) => Expr.lessEqual(a.key, b.key).ifElse(() => a, () => b));
1487
+ return result.element;
1488
+ }
1489
+ }
1490
+ /**
1491
+ * Joins string array elements with a delimiter.
1492
+ *
1493
+ * @param string - The delimiter string to insert between elements
1494
+ * @returns A StringExpr with all elements joined
1495
+ *
1496
+ * @remarks
1497
+ * This method only works on arrays of strings.
1498
+ *
1499
+ * @example
1500
+ * ```ts
1501
+ * const joinStrings = East.function([ArrayType(StringType), StringType], StringType, ($, arr, delimiter) => {
1502
+ * $.return(arr.stringJoin(delimiter));
1503
+ * });
1504
+ * const compiled = East.compile(joinStrings.toIR(), []);
1505
+ * compiled(["a", "b", "c"], ", "); // "a, b, c"
1506
+ * compiled(["hello", "world"], " "); // "hello world"
1507
+ * compiled([], ", "); // ""
1508
+ * ```
1509
+ */
1510
+ stringJoin(string) {
1511
+ if (!isTypeEqual(this.value_type, StringType)) {
1512
+ throw new Error(`Can only perform stringJoin on array of strings - try using map to convert the values to strings first`);
1513
+ }
1514
+ const stringAst = valueOrExprToAstTyped(string, StringType);
1515
+ return this[FactorySymbol]({
1516
+ ast_type: "Builtin",
1517
+ type: StringType,
1518
+ location: get_location(2),
1519
+ builtin: "ArrayStringJoin",
1520
+ type_parameters: [],
1521
+ arguments: [this[AstSymbol], stringAst],
1522
+ });
1523
+ }
1524
+ toSet(keyFn) {
1525
+ const keyFnAst = valueOrExprToAstTyped(keyFn ?? ((_$, x) => x), FunctionType([this.value_type, IntegerType], undefined, null));
1526
+ const keyType = keyFnAst.type.output;
1527
+ return this[FactorySymbol]({
1528
+ ast_type: "Builtin",
1529
+ type: SetType(keyType),
1530
+ location: get_location(2),
1531
+ builtin: "ArrayToSet",
1532
+ type_parameters: [this.value_type, keyType],
1533
+ arguments: [this[AstSymbol], keyFnAst],
1534
+ });
1535
+ }
1536
+ toDict(keyFn, valueFn, onConflictFn) {
1537
+ const keyFnAst = valueOrExprToAstTyped(keyFn ?? ((_$, x, i) => i), FunctionType([this.value_type, IntegerType], undefined, null));
1538
+ const keyType = keyFnAst.type.output;
1539
+ const valueFnAst = valueOrExprToAstTyped(valueFn ?? ((_$, x) => x), FunctionType([this.value_type, IntegerType], undefined, null));
1540
+ const valueType = valueFnAst.type.output;
1541
+ let onConflictAst;
1542
+ if (onConflictFn === undefined) {
1543
+ const location = get_location(2);
1544
+ const onConflictFunction = Expr.function([valueType, valueType, keyType], valueType, ($, existing, value, key) => $.error(Expr.str `Cannot insert duplicate key ${key} into dict`, location));
1545
+ onConflictAst = Expr.ast(onConflictFunction);
1546
+ }
1547
+ else {
1548
+ onConflictAst = valueOrExprToAstTyped(onConflictFn, FunctionType([valueType, valueType, keyType], valueType, null));
1549
+ }
1550
+ return this[FactorySymbol]({
1551
+ ast_type: "Builtin",
1552
+ type: DictType(keyType, valueType),
1553
+ location: get_location(2),
1554
+ builtin: "ArrayToDict",
1555
+ type_parameters: [this.value_type, keyType, valueType],
1556
+ arguments: [this[AstSymbol], keyFnAst, valueFnAst, onConflictAst],
1557
+ });
1558
+ }
1559
+ flatMap(fn) {
1560
+ const fnAst = valueOrExprToAstTyped(fn ?? ((_$, x) => x), FunctionType([this.value_type, IntegerType], undefined, null));
1561
+ const returnType = fnAst.type.output;
1562
+ if (returnType.type !== "Array") {
1563
+ throw new Error(`Expected Function to return an Array type, got ${printType(returnType)}`);
1564
+ }
1565
+ const elementType = returnType.value;
1566
+ return this[FactorySymbol]({
1567
+ ast_type: "Builtin",
1568
+ type: ArrayType(elementType),
1569
+ location: get_location(2),
1570
+ builtin: "ArrayFlattenToArray",
1571
+ type_parameters: [this.value_type, elementType],
1572
+ arguments: [this[AstSymbol], fnAst],
1573
+ });
1574
+ }
1575
+ flattenToSet(fn) {
1576
+ const fnAst = valueOrExprToAstTyped(fn ?? ((_$, x) => x), FunctionType([this.value_type, IntegerType], undefined, null));
1577
+ const returnType = fnAst.type.output;
1578
+ if (returnType.type !== "Set") {
1579
+ throw new Error(`Expected Function to return a Set type, got ${printType(returnType)}`);
1580
+ }
1581
+ const elementType = returnType.key;
1582
+ return this[FactorySymbol]({
1583
+ ast_type: "Builtin",
1584
+ type: SetType(elementType),
1585
+ location: get_location(2),
1586
+ builtin: "ArrayFlattenToSet",
1587
+ type_parameters: [this.value_type, elementType],
1588
+ arguments: [this[AstSymbol], fnAst],
1589
+ });
1590
+ }
1591
+ flattenToDict(fn, onConflictFn) {
1592
+ const fnAst = valueOrExprToAstTyped(fn ?? ((_$, x) => x), FunctionType([this.value_type, IntegerType], undefined, null));
1593
+ const returnType = fnAst.type.output;
1594
+ if (returnType.type !== "Dict") {
1595
+ throw new Error(`Expected Function to return a Dict type, got ${printType(returnType)}`);
1596
+ }
1597
+ const keyType = returnType.key;
1598
+ const valueType = returnType.value;
1599
+ let onConflictAst;
1600
+ if (onConflictFn === undefined) {
1601
+ const location = get_location(2);
1602
+ const onConflictFunction = Expr.function([valueType, valueType, keyType], valueType, ($, existing, value, key) => $.error(Expr.str `Cannot insert duplicate key ${key} into dict`, location));
1603
+ onConflictAst = Expr.ast(onConflictFunction);
1604
+ }
1605
+ else {
1606
+ onConflictAst = valueOrExprToAstTyped(onConflictFn, FunctionType([valueType, valueType, keyType], valueType, null));
1607
+ }
1608
+ return this[FactorySymbol]({
1609
+ ast_type: "Builtin",
1610
+ type: DictType(keyType, valueType),
1611
+ location: get_location(2),
1612
+ builtin: "ArrayFlattenToDict",
1613
+ type_parameters: [this.value_type, keyType, valueType],
1614
+ arguments: [this[AstSymbol], fnAst, onConflictAst],
1615
+ });
1616
+ }
1617
+ groupReduce(keyFn, initFn, reduceFn) {
1618
+ // Note - initFn has to be before reduceFn, otherwise the TypeScript type inference doesn't work properly
1619
+ const keyFnAst = valueOrExprToAstTyped(keyFn, FunctionType([this.value_type, IntegerType], undefined, null));
1620
+ const keyType = keyFnAst.type.output;
1621
+ const initFnAst = valueOrExprToAstTyped(initFn, FunctionType([keyType], undefined, null));
1622
+ const initType = initFnAst.type.output;
1623
+ const reduceFnAst = valueOrExprToAstTyped(reduceFn, FunctionType([initType, this.value_type, IntegerType], initType, null));
1624
+ return this[FactorySymbol]({
1625
+ ast_type: "Builtin",
1626
+ type: DictType(keyType, initType),
1627
+ location: get_location(2),
1628
+ builtin: "ArrayGroupFold",
1629
+ type_parameters: [this.value_type, keyType, initType],
1630
+ arguments: [this[AstSymbol], keyFnAst, initFnAst, reduceFnAst],
1631
+ });
1632
+ }
1633
+ groupSize(keyFn) {
1634
+ const keyFnAst = valueOrExprToAstTyped(keyFn ?? ((_$, x) => x), FunctionType([this.value_type, IntegerType], undefined, null));
1635
+ return this.toDict(((_$, elem, idx) => Expr.fromAst(keyFnAst)(elem, idx)), ((_$) => 1n), ((_$, a, b) => a.add(b)));
1636
+ }
1637
+ groupEvery(keyFn, predFn) {
1638
+ const keyFnAst = valueOrExprToAstTyped(keyFn, FunctionType([this.value_type, IntegerType], undefined, null));
1639
+ const predFnAst = valueOrExprToAstTyped(predFn, FunctionType([this.value_type, IntegerType], BooleanType, null));
1640
+ return this.groupReduce(((_$, elem, idx) => Expr.fromAst(keyFnAst)(elem, idx)), (() => true), ((_$, acc, elem, idx) => {
1641
+ const pred = Expr.fromAst(predFnAst)(elem, idx);
1642
+ return acc.and(() => pred);
1643
+ }));
1644
+ }
1645
+ groupSome(keyFn, predFn) {
1646
+ const keyFnAst = valueOrExprToAstTyped(keyFn, FunctionType([this.value_type, IntegerType], undefined, null));
1647
+ const predFnAst = valueOrExprToAstTyped(predFn, FunctionType([this.value_type, IntegerType], BooleanType, null));
1648
+ return this.groupReduce(((_$, elem, idx) => Expr.fromAst(keyFnAst)(elem, idx)), (() => false), ((_$, acc, elem, idx) => {
1649
+ const pred = Expr.fromAst(predFnAst)(elem, idx);
1650
+ return acc.or(() => pred);
1651
+ }));
1652
+ }
1653
+ groupFindAll(keyFn, value, projFn) {
1654
+ const keyFnAst = valueOrExprToAstTyped(keyFn, FunctionType([this.value_type, IntegerType], undefined, null));
1655
+ const keyFnExpr = Expr.fromAst(keyFnAst);
1656
+ if (projFn === undefined) {
1657
+ // Without projection: find where element == value
1658
+ return this.groupReduce(((_$, elem, idx) => keyFnExpr(elem, idx)), ((_$, _key) => Expr.from([], ArrayType(IntegerType))), (($, acc, elem, idx) => {
1659
+ $.if(Expr.equal(elem, value), ($) => $(acc.pushLast(idx)));
1660
+ return acc;
1661
+ }));
1662
+ }
1663
+ else {
1664
+ // With projection: find where projFn(element) == value
1665
+ const projFnExpr = (projFn instanceof Expr ? projFn : Expr.function([this.value_type, IntegerType], undefined, projFn));
1666
+ if (projFnExpr[TypeSymbol].type !== "Function") {
1667
+ throw new Error("Expected a Function expression for 'projFn' parameter");
1668
+ }
1669
+ return this.groupReduce(((_$, elem, idx) => keyFnExpr(elem, idx)), ((_$, _key) => Expr.from([], ArrayType(IntegerType))), (($, acc, elem, idx) => {
1670
+ $.if(Expr.equal(projFnExpr(elem, idx), value), ($) => $(acc.pushLast(idx)));
1671
+ return acc;
1672
+ }));
1673
+ }
1674
+ }
1675
+ groupFindFirst(keyFn, value, projFn) {
1676
+ const keyFnAst = valueOrExprToAstTyped(keyFn, FunctionType([this.value_type, IntegerType], undefined, null));
1677
+ const keyFnExpr = Expr.fromAst(keyFnAst);
1678
+ if (projFn === undefined) {
1679
+ // Without projection: find where element == value
1680
+ return this.groupReduce(((_$, elem, idx) => keyFnExpr(elem, idx)), ((_$, _key) => Expr.from(none, OptionType(IntegerType))), ((_$, acc, elem, idx) => {
1681
+ const isSome = acc.hasTag("some");
1682
+ return isSome.ifElse(() => acc, () => Expr.equal(elem, value).ifElse(() => some(idx), () => Expr.from(none, OptionType(IntegerType))));
1683
+ }));
1684
+ }
1685
+ else {
1686
+ // With projection: find where projFn(element) == value
1687
+ const projFnExpr = (projFn instanceof Expr ? projFn : Expr.function([this.value_type, IntegerType], undefined, projFn));
1688
+ if (projFnExpr[TypeSymbol].type !== "Function") {
1689
+ throw new Error("Expected a Function expression for 'projFn' parameter");
1690
+ }
1691
+ return this.groupReduce(((_$, elem, idx) => keyFnExpr(elem, idx)), ((_$, _key) => Expr.from(none, OptionType(IntegerType))), ((_$, acc, elem, idx) => {
1692
+ const isSome = acc.hasTag("some");
1693
+ return isSome.ifElse(() => acc, () => Expr.equal(projFnExpr(elem, idx), value).ifElse(() => some(idx), () => Expr.from(none, OptionType(IntegerType))));
1694
+ }));
1695
+ }
1696
+ }
1697
+ groupFindMinimum(keyFn, byFn) {
1698
+ const keyFnAst = valueOrExprToAstTyped(keyFn, FunctionType([this.value_type, IntegerType], undefined, null));
1699
+ const byFnAst = valueOrExprToAstTyped(byFn ?? ((_$, x) => x), FunctionType([this.value_type, IntegerType], undefined, null));
1700
+ return this.toDict(((_$, elem, idx) => Expr.fromAst(keyFnAst)(elem, idx)), ((_$, elem, idx) => ({ by: Expr.fromAst(byFnAst)(elem, idx), index: idx })), ((_$, a, b) => Expr.lessEqual(a.by, b.by).ifElse(() => a, () => b))).map(((_$, v) => v.index));
1701
+ }
1702
+ groupFindMaximum(keyFn, byFn) {
1703
+ const keyFnAst = valueOrExprToAstTyped(keyFn, FunctionType([this.value_type, IntegerType], undefined, null));
1704
+ const byFnAst = valueOrExprToAstTyped(byFn ?? ((_$, x) => x), FunctionType([this.value_type, IntegerType], undefined, null));
1705
+ return this.toDict(((_$, elem, idx) => Expr.fromAst(keyFnAst)(elem, idx)), ((_$, elem, idx) => ({ by: Expr.fromAst(byFnAst)(elem, idx), index: idx })), ((_$, a, b) => Expr.greaterEqual(a.by, b.by).ifElse(() => a, () => b))).map(((_$, v) => v.index));
1706
+ }
1707
+ groupSum(keyFn, valueFn) {
1708
+ const keyFnAst = valueOrExprToAstTyped(keyFn, FunctionType([this.value_type, IntegerType], undefined, null));
1709
+ const valueFnAst = valueOrExprToAstTyped(valueFn ?? ((_$, x) => x), FunctionType([this.value_type, IntegerType], undefined, null));
1710
+ const valueType = valueFnAst.type.output;
1711
+ const isInteger = isTypeEqual(valueType, IntegerType);
1712
+ const isFloat = isTypeEqual(valueType, FloatType);
1713
+ if (!isInteger && !isFloat) {
1714
+ throw new Error(`Can only perform groupSum on Integer or Float values, got ${printType(valueType)}`);
1715
+ }
1716
+ return this.toDict(((_$, elem, idx) => Expr.fromAst(keyFnAst)(elem, idx)), ((_$, elem, idx) => Expr.fromAst(valueFnAst)(elem, idx)), ((_$, a, b) => a.add(b)));
1717
+ }
1718
+ groupMean(keyFn, valueFn) {
1719
+ const keyFnAst = valueOrExprToAstTyped(keyFn, FunctionType([this.value_type, IntegerType], undefined, null));
1720
+ const valueFnAst = valueOrExprToAstTyped(valueFn ?? ((_$, x) => x), FunctionType([this.value_type, IntegerType], undefined, null));
1721
+ const valueType = valueFnAst.type.output;
1722
+ const isInteger = isTypeEqual(valueType, IntegerType);
1723
+ const isFloat = isTypeEqual(valueType, FloatType);
1724
+ if (!isInteger && !isFloat) {
1725
+ throw new Error(`Can only perform groupMean on Integer or Float values, got ${printType(valueType)}`);
1726
+ }
1727
+ return this.toDict(((_$, elem, idx) => Expr.fromAst(keyFnAst)(elem, idx)), ((_$, elem, idx) => {
1728
+ const val = Expr.fromAst(valueFnAst)(elem, idx);
1729
+ return { sum: isInteger ? val.toFloat() : val, count: 1n };
1730
+ }), ((_$, a, b) => ({ sum: a.sum.add(b.sum), count: a.count.add(b.count) }))).map(((_$, v) => v.sum.divide(v.count.toFloat())));
1731
+ }
1732
+ groupToArrays(keyFn, valueFn) {
1733
+ const keyFnAst = valueOrExprToAstTyped(keyFn, FunctionType([this.value_type, IntegerType], undefined, null));
1734
+ const valueFnAst = valueOrExprToAstTyped(valueFn ?? ((_$, x) => x), FunctionType([this.value_type, IntegerType], undefined, null));
1735
+ const keyFnExpr = Expr.fromAst(keyFnAst);
1736
+ const valueFnExpr = Expr.fromAst(valueFnAst);
1737
+ const valueType = valueFnAst.type.output;
1738
+ return this.groupReduce(((_$, elem, idx) => keyFnExpr(elem, idx)), ((_$, _key) => Expr.from([], ArrayType(valueType))), (($, acc, elem, idx) => {
1739
+ const val = valueFnExpr(elem, idx);
1740
+ $(acc.pushLast(val));
1741
+ return acc;
1742
+ }));
1743
+ }
1744
+ groupToSets(keyFn, valueFn) {
1745
+ const keyFnAst = valueOrExprToAstTyped(keyFn, FunctionType([this.value_type, IntegerType], undefined, null));
1746
+ const valueFnAst = valueOrExprToAstTyped(valueFn ?? ((_$, x) => x), FunctionType([this.value_type, IntegerType], undefined, null));
1747
+ const keyFnExpr = Expr.fromAst(keyFnAst);
1748
+ const valueFnExpr = Expr.fromAst(valueFnAst);
1749
+ const valueType = valueFnAst.type.output;
1750
+ return this.groupReduce(((_$, elem, idx) => keyFnExpr(elem, idx)), ((_$, _key) => Expr.from(new Set(), SetType(valueType))), (($, acc, elem, idx) => {
1751
+ const val = valueFnExpr(elem, idx);
1752
+ $(acc.tryInsert(val));
1753
+ return acc;
1754
+ }));
1755
+ }
1756
+ groupMinimum(keyFn, byFn) {
1757
+ const keyFnAst = valueOrExprToAstTyped(keyFn, FunctionType([this.value_type, IntegerType], undefined, null));
1758
+ const byFnAst = valueOrExprToAstTyped(byFn ?? ((_$, x) => x), FunctionType([this.value_type, IntegerType], undefined, null));
1759
+ return this.toDict(((_$, elem, idx) => Expr.fromAst(keyFnAst)(elem, idx)), ((_$, elem, idx) => ({ by: Expr.fromAst(byFnAst)(elem, idx), elem })), ((_$, a, b) => Expr.lessEqual(a.by, b.by).ifElse(() => a, () => b))).map(((_$, v) => v.elem));
1760
+ }
1761
+ groupMaximum(keyFn, byFn) {
1762
+ const keyFnAst = valueOrExprToAstTyped(keyFn, FunctionType([this.value_type, IntegerType], undefined, null));
1763
+ const byFnAst = valueOrExprToAstTyped(byFn ?? ((_$, x) => x), FunctionType([this.value_type, IntegerType], undefined, null));
1764
+ return this.toDict(((_$, elem, idx) => Expr.fromAst(keyFnAst)(elem, idx)), ((_$, elem, idx) => ({ by: Expr.fromAst(byFnAst)(elem, idx), elem })), ((_$, a, b) => Expr.greaterEqual(a.by, b.by).ifElse(() => a, () => b))).map(((_$, v) => v.elem));
1765
+ }
1766
+ groupToDicts(keyFn, keyFn2, valueFn, combineFn) {
1767
+ const keyFnAst = valueOrExprToAstTyped(keyFn, FunctionType([this.value_type, IntegerType], undefined, null));
1768
+ const keyFn2Ast = valueOrExprToAstTyped(keyFn2, FunctionType([this.value_type, IntegerType], undefined, null));
1769
+ const valueFnAst = valueOrExprToAstTyped(valueFn ?? ((_$, x) => x), FunctionType([this.value_type, IntegerType], undefined, null));
1770
+ const keyFnExpr = Expr.fromAst(keyFnAst);
1771
+ const keyFn2Expr = Expr.fromAst(keyFn2Ast);
1772
+ const valueFnExpr = Expr.fromAst(valueFnAst);
1773
+ const key2Type = keyFn2Ast.type.output;
1774
+ const valueType = valueFnAst.type.output;
1775
+ if (combineFn !== undefined) {
1776
+ // With conflict resolution - use tryGet + match to check existence, then insert or combine
1777
+ const combineFnAst = valueOrExprToAstTyped(combineFn, FunctionType([valueType, valueType], valueType, null));
1778
+ const combineFnExpr = Expr.fromAst(combineFnAst);
1779
+ return this.groupReduce(((_$, elem, idx) => keyFnExpr(elem, idx)), ((_$, _key) => Expr.from(new Map(), DictType(key2Type, valueType))), (($, dict, elem, idx) => {
1780
+ const innerKey = keyFn2Expr(elem, idx);
1781
+ const val = valueFnExpr(elem, idx);
1782
+ $.match(dict.tryGet(innerKey), {
1783
+ some: ($, existing) => {
1784
+ const combined = combineFnExpr(existing, val);
1785
+ $(dict.update(innerKey, combined));
1786
+ },
1787
+ none: ($) => {
1788
+ $(dict.insert(innerKey, val));
1789
+ }
1790
+ });
1791
+ return dict;
1792
+ }));
1793
+ }
1794
+ else {
1795
+ // Without conflict resolution - use insert (errors on duplicate)
1796
+ return this.groupReduce(((_$, elem, idx) => keyFnExpr(elem, idx)), ((_$, _key) => Expr.from(new Map(), DictType(key2Type, valueType))), (($, dict, elem, idx) => {
1797
+ const innerKey = keyFn2Expr(elem, idx);
1798
+ const val = valueFnExpr(elem, idx);
1799
+ $(dict.insert(innerKey, val));
1800
+ return dict;
1801
+ }));
1802
+ }
1803
+ }
1804
+ }
1805
+ //# sourceMappingURL=array.js.map