@procedures/collections 0.0.0-stage → 0.1.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 (205) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/LICENSE +21 -0
  3. package/README.md +394 -2
  4. package/dist/.tsbuildinfo +1 -0
  5. package/dist/async/async-queue.d.ts +155 -0
  6. package/dist/async/async-queue.d.ts.map +1 -0
  7. package/dist/async/async-queue.js +342 -0
  8. package/dist/async/async-queue.js.map +1 -0
  9. package/dist/async/channels.d.ts +188 -0
  10. package/dist/async/channels.d.ts.map +1 -0
  11. package/dist/async/channels.js +314 -0
  12. package/dist/async/channels.js.map +1 -0
  13. package/dist/behaviors/bounded.d.ts +79 -0
  14. package/dist/behaviors/bounded.d.ts.map +1 -0
  15. package/dist/behaviors/bounded.js +384 -0
  16. package/dist/behaviors/bounded.js.map +1 -0
  17. package/dist/behaviors/composite.d.ts +17 -0
  18. package/dist/behaviors/composite.d.ts.map +1 -0
  19. package/dist/behaviors/composite.js +93 -0
  20. package/dist/behaviors/composite.js.map +1 -0
  21. package/dist/behaviors/evented.d.ts +154 -0
  22. package/dist/behaviors/evented.d.ts.map +1 -0
  23. package/dist/behaviors/evented.js +299 -0
  24. package/dist/behaviors/evented.js.map +1 -0
  25. package/dist/behaviors/lru.d.ts +84 -0
  26. package/dist/behaviors/lru.d.ts.map +1 -0
  27. package/dist/behaviors/lru.js +376 -0
  28. package/dist/behaviors/lru.js.map +1 -0
  29. package/dist/behaviors/readonly.d.ts +73 -0
  30. package/dist/behaviors/readonly.d.ts.map +1 -0
  31. package/dist/behaviors/readonly.js +177 -0
  32. package/dist/behaviors/readonly.js.map +1 -0
  33. package/dist/behaviors/safe.d.ts +163 -0
  34. package/dist/behaviors/safe.d.ts.map +1 -0
  35. package/dist/behaviors/safe.js +176 -0
  36. package/dist/behaviors/safe.js.map +1 -0
  37. package/dist/behaviors/synchronized.d.ts +88 -0
  38. package/dist/behaviors/synchronized.d.ts.map +1 -0
  39. package/dist/behaviors/synchronized.js +285 -0
  40. package/dist/behaviors/synchronized.js.map +1 -0
  41. package/dist/behaviors/ttl.d.ts +118 -0
  42. package/dist/behaviors/ttl.d.ts.map +1 -0
  43. package/dist/behaviors/ttl.js +354 -0
  44. package/dist/behaviors/ttl.js.map +1 -0
  45. package/dist/contracts/model.d.ts +29 -0
  46. package/dist/contracts/model.d.ts.map +1 -0
  47. package/dist/contracts/model.js +26 -0
  48. package/dist/contracts/model.js.map +1 -0
  49. package/dist/core/effects.d.ts +176 -0
  50. package/dist/core/effects.d.ts.map +1 -0
  51. package/dist/core/effects.js +180 -0
  52. package/dist/core/effects.js.map +1 -0
  53. package/dist/core/events.d.ts +192 -0
  54. package/dist/core/events.d.ts.map +1 -0
  55. package/dist/core/events.js +291 -0
  56. package/dist/core/events.js.map +1 -0
  57. package/dist/core/middleware.d.ts +231 -0
  58. package/dist/core/middleware.d.ts.map +1 -0
  59. package/dist/core/middleware.js +161 -0
  60. package/dist/core/middleware.js.map +1 -0
  61. package/dist/core/policies.d.ts +220 -0
  62. package/dist/core/policies.d.ts.map +1 -0
  63. package/dist/core/policies.js +48 -0
  64. package/dist/core/policies.js.map +1 -0
  65. package/dist/core/traits.d.ts +142 -0
  66. package/dist/core/traits.d.ts.map +1 -0
  67. package/dist/core/traits.js +8 -0
  68. package/dist/core/traits.js.map +1 -0
  69. package/dist/fx/collectors.d.ts +193 -0
  70. package/dist/fx/collectors.d.ts.map +1 -0
  71. package/dist/fx/collectors.js +375 -0
  72. package/dist/fx/collectors.js.map +1 -0
  73. package/dist/fx/iter.d.ts +166 -0
  74. package/dist/fx/iter.d.ts.map +1 -0
  75. package/dist/fx/iter.js +421 -0
  76. package/dist/fx/iter.js.map +1 -0
  77. package/dist/impl/array-deque.d.ts +112 -0
  78. package/dist/impl/array-deque.d.ts.map +1 -0
  79. package/dist/impl/array-deque.js +380 -0
  80. package/dist/impl/array-deque.js.map +1 -0
  81. package/dist/impl/array-list.d.ts +101 -0
  82. package/dist/impl/array-list.d.ts.map +1 -0
  83. package/dist/impl/array-list.js +387 -0
  84. package/dist/impl/array-list.js.map +1 -0
  85. package/dist/impl/hash-map.d.ts +149 -0
  86. package/dist/impl/hash-map.d.ts.map +1 -0
  87. package/dist/impl/hash-map.js +473 -0
  88. package/dist/impl/hash-map.js.map +1 -0
  89. package/dist/impl/hash-set.d.ts +125 -0
  90. package/dist/impl/hash-set.d.ts.map +1 -0
  91. package/dist/impl/hash-set.js +264 -0
  92. package/dist/impl/hash-set.js.map +1 -0
  93. package/dist/impl/linked-hash-map.d.ts +141 -0
  94. package/dist/impl/linked-hash-map.d.ts.map +1 -0
  95. package/dist/impl/linked-hash-map.js +442 -0
  96. package/dist/impl/linked-hash-map.js.map +1 -0
  97. package/dist/impl/linked-list.d.ts +109 -0
  98. package/dist/impl/linked-list.d.ts.map +1 -0
  99. package/dist/impl/linked-list.js +494 -0
  100. package/dist/impl/linked-list.js.map +1 -0
  101. package/dist/impl/priority-queue.d.ts +139 -0
  102. package/dist/impl/priority-queue.d.ts.map +1 -0
  103. package/dist/impl/priority-queue.js +343 -0
  104. package/dist/impl/priority-queue.js.map +1 -0
  105. package/dist/impl/tree-map.d.ts +153 -0
  106. package/dist/impl/tree-map.d.ts.map +1 -0
  107. package/dist/impl/tree-map.js +788 -0
  108. package/dist/impl/tree-map.js.map +1 -0
  109. package/dist/impl/tree-set.d.ts +112 -0
  110. package/dist/impl/tree-set.d.ts.map +1 -0
  111. package/dist/impl/tree-set.js +374 -0
  112. package/dist/impl/tree-set.js.map +1 -0
  113. package/dist/index.d.ts +106 -0
  114. package/dist/index.d.ts.map +1 -0
  115. package/dist/index.js +139 -0
  116. package/dist/index.js.map +1 -0
  117. package/dist/interfaces/collection.d.ts +119 -0
  118. package/dist/interfaces/collection.d.ts.map +1 -0
  119. package/dist/interfaces/collection.js +8 -0
  120. package/dist/interfaces/collection.js.map +1 -0
  121. package/dist/interfaces/list.d.ts +247 -0
  122. package/dist/interfaces/list.d.ts.map +1 -0
  123. package/dist/interfaces/list.js +8 -0
  124. package/dist/interfaces/list.js.map +1 -0
  125. package/dist/interfaces/map.d.ts +344 -0
  126. package/dist/interfaces/map.d.ts.map +1 -0
  127. package/dist/interfaces/map.js +8 -0
  128. package/dist/interfaces/map.js.map +1 -0
  129. package/dist/interfaces/queue.d.ts +304 -0
  130. package/dist/interfaces/queue.d.ts.map +1 -0
  131. package/dist/interfaces/queue.js +8 -0
  132. package/dist/interfaces/queue.js.map +1 -0
  133. package/dist/interfaces/set.d.ts +137 -0
  134. package/dist/interfaces/set.d.ts.map +1 -0
  135. package/dist/interfaces/set.js +8 -0
  136. package/dist/interfaces/set.js.map +1 -0
  137. package/dist/register.d.ts +8 -0
  138. package/dist/register.d.ts.map +1 -0
  139. package/dist/register.js +8 -0
  140. package/dist/register.js.map +1 -0
  141. package/dist/storage/index.d.ts +15 -0
  142. package/dist/storage/index.d.ts.map +1 -0
  143. package/dist/storage/index.js +14 -0
  144. package/dist/storage/index.js.map +1 -0
  145. package/dist/storage/interface.d.ts +141 -0
  146. package/dist/storage/interface.d.ts.map +1 -0
  147. package/dist/storage/interface.js +23 -0
  148. package/dist/storage/interface.js.map +1 -0
  149. package/dist/storage/memory.d.ts +68 -0
  150. package/dist/storage/memory.d.ts.map +1 -0
  151. package/dist/storage/memory.js +154 -0
  152. package/dist/storage/memory.js.map +1 -0
  153. package/dist/utils/defaults.d.ts +135 -0
  154. package/dist/utils/defaults.d.ts.map +1 -0
  155. package/dist/utils/defaults.js +322 -0
  156. package/dist/utils/defaults.js.map +1 -0
  157. package/dist/utils/factories.d.ts +144 -0
  158. package/dist/utils/factories.d.ts.map +1 -0
  159. package/dist/utils/factories.js +293 -0
  160. package/dist/utils/factories.js.map +1 -0
  161. package/dist/utils/helpers.d.ts +137 -0
  162. package/dist/utils/helpers.d.ts.map +1 -0
  163. package/dist/utils/helpers.js +221 -0
  164. package/dist/utils/helpers.js.map +1 -0
  165. package/package.json +68 -3
  166. package/src/async/async-queue.ts +417 -0
  167. package/src/async/channels.ts +374 -0
  168. package/src/behaviors/bounded.ts +477 -0
  169. package/src/behaviors/composite.ts +99 -0
  170. package/src/behaviors/evented.ts +464 -0
  171. package/src/behaviors/lru.ts +447 -0
  172. package/src/behaviors/readonly.ts +224 -0
  173. package/src/behaviors/safe.ts +372 -0
  174. package/src/behaviors/synchronized.ts +316 -0
  175. package/src/behaviors/ttl.ts +462 -0
  176. package/src/contracts/model.ts +44 -0
  177. package/src/core/effects.ts +323 -0
  178. package/src/core/events.ts +356 -0
  179. package/src/core/middleware.ts +415 -0
  180. package/src/core/policies.ts +316 -0
  181. package/src/core/traits.ts +164 -0
  182. package/src/fx/collectors.ts +500 -0
  183. package/src/fx/iter.ts +536 -0
  184. package/src/impl/array-deque.ts +463 -0
  185. package/src/impl/array-list.ts +490 -0
  186. package/src/impl/hash-map.ts +623 -0
  187. package/src/impl/hash-set.ts +342 -0
  188. package/src/impl/linked-hash-map.ts +572 -0
  189. package/src/impl/linked-list.ts +606 -0
  190. package/src/impl/priority-queue.ts +448 -0
  191. package/src/impl/tree-map.ts +912 -0
  192. package/src/impl/tree-set.ts +469 -0
  193. package/src/index.ts +277 -0
  194. package/src/interfaces/collection.ts +141 -0
  195. package/src/interfaces/list.ts +281 -0
  196. package/src/interfaces/map.ts +405 -0
  197. package/src/interfaces/queue.ts +353 -0
  198. package/src/interfaces/set.ts +158 -0
  199. package/src/register.ts +8 -0
  200. package/src/storage/index.ts +16 -0
  201. package/src/storage/interface.ts +179 -0
  202. package/src/storage/memory.ts +180 -0
  203. package/src/utils/defaults.ts +345 -0
  204. package/src/utils/factories.ts +357 -0
  205. package/src/utils/helpers.ts +248 -0
@@ -0,0 +1,357 @@
1
+ /**
2
+ * Factory utilities for creating collections.
3
+ *
4
+ * Provides convenient factory methods similar to Java's Collections class.
5
+ * These mirror methods like Collections.emptyList(), singletonList(), etc.
6
+ */
7
+
8
+ import { arrayList, ArrayList } from "../impl/array-list.js";
9
+ import { arrayDeque, ArrayDeque } from "../impl/array-deque.js";
10
+ import { hashMap, HashMap } from "../impl/hash-map.js";
11
+ import type { List } from "../interfaces/list.js";
12
+ import type { Queue } from "../interfaces/queue.js";
13
+ import type { MapLike } from "../interfaces/map.js";
14
+ import { readonly } from "../behaviors/readonly.js";
15
+ import { compose } from "../core/middleware.js";
16
+
17
+ // ============================================================================
18
+ // Empty collections
19
+ // ============================================================================
20
+
21
+ /**
22
+ * Returns an empty, immutable list.
23
+ * Multiple calls return the same instance (singleton pattern).
24
+ *
25
+ * @example
26
+ * const list = emptyList<number>()
27
+ * list.size // 0
28
+ * list.add(1) // Error: Cannot modify readonly collection
29
+ */
30
+ const EMPTY_LIST_CACHE = new Map<any, any>();
31
+
32
+ export function emptyList<T>(): List<T> {
33
+ const key = "list";
34
+ if (!EMPTY_LIST_CACHE.has(key)) {
35
+ const list = compose(readonly<List<T>>())(arrayList<T>());
36
+ EMPTY_LIST_CACHE.set(key, list);
37
+ }
38
+ return EMPTY_LIST_CACHE.get(key);
39
+ }
40
+
41
+ /**
42
+ * Returns an empty, immutable queue.
43
+ */
44
+ const EMPTY_QUEUE_CACHE = new Map<any, any>();
45
+
46
+ export function emptyQueue<T>(): Queue<T> {
47
+ const key = "queue";
48
+ if (!EMPTY_QUEUE_CACHE.has(key)) {
49
+ const queue = compose(readonly<Queue<T>>())(arrayDeque<T>() as Queue<T>);
50
+ EMPTY_QUEUE_CACHE.set(key, queue);
51
+ }
52
+ return EMPTY_QUEUE_CACHE.get(key);
53
+ }
54
+
55
+ /**
56
+ * Returns an empty, immutable map.
57
+ */
58
+ const EMPTY_MAP_CACHE = new Map<any, any>();
59
+
60
+ export function emptyMap<K, V>(): MapLike<K, V> {
61
+ const key = "map";
62
+ if (!EMPTY_MAP_CACHE.has(key)) {
63
+ const map = compose(readonly<MapLike<K, V>>())(hashMap<K, V>());
64
+ EMPTY_MAP_CACHE.set(key, map);
65
+ }
66
+ return EMPTY_MAP_CACHE.get(key);
67
+ }
68
+
69
+ // ============================================================================
70
+ // Singleton collections (single element)
71
+ // ============================================================================
72
+
73
+ /**
74
+ * Returns an immutable list containing only the specified element.
75
+ *
76
+ * @example
77
+ * const list = singletonList(42)
78
+ * list.size // 1
79
+ * list.get(0) // 42
80
+ * list.add(2) // Error: Cannot modify readonly collection
81
+ */
82
+ export function singletonList<T>(element: T): List<T> {
83
+ const list = arrayList<T>();
84
+ list.add(element);
85
+ return compose(readonly<List<T>>())(list);
86
+ }
87
+
88
+ /**
89
+ * Returns an immutable map containing only the specified key-value mapping.
90
+ */
91
+ export function singletonMap<K, V>(key: K, value: V): MapLike<K, V> {
92
+ const map = hashMap<K, V>();
93
+ map.set(key, value);
94
+ return compose(readonly<MapLike<K, V>>())(map);
95
+ }
96
+
97
+ // ============================================================================
98
+ // N-copies collections
99
+ // ============================================================================
100
+
101
+ /**
102
+ * Returns an immutable list consisting of n copies of the specified element.
103
+ *
104
+ * @example
105
+ * const list = nCopies(5, 'hello')
106
+ * list.size // 5
107
+ * list.get(2) // 'hello'
108
+ */
109
+ export function nCopies<T>(n: number, element: T): List<T> {
110
+ if (n < 0) {
111
+ throw new Error(`n must be non-negative, got ${n}`);
112
+ }
113
+
114
+ const list = arrayList<T>();
115
+ for (let i = 0; i < n; i++) {
116
+ list.add(element);
117
+ }
118
+ return compose(readonly<List<T>>())(list);
119
+ }
120
+
121
+ // ============================================================================
122
+ // Collection creation from iterables
123
+ // ============================================================================
124
+
125
+ /**
126
+ * Creates a new ArrayList from the given elements.
127
+ *
128
+ * @example
129
+ * const list = listOf(1, 2, 3, 4, 5)
130
+ */
131
+ export function listOf<T>(...elements: T[]): ArrayList<T> {
132
+ return arrayList(elements);
133
+ }
134
+
135
+ /**
136
+ * Creates a new ArrayDeque from the given elements.
137
+ */
138
+ export function queueOf<T>(...elements: T[]): ArrayDeque<T> {
139
+ return arrayDeque(elements);
140
+ }
141
+
142
+ /**
143
+ * Creates a new HashMap from the given key-value pairs.
144
+ *
145
+ * @example
146
+ * const map = mapOf(['a', 1], ['b', 2], ['c', 3])
147
+ */
148
+ export function mapOf<K, V>(...entries: [K, V][]): HashMap<K, V> {
149
+ return hashMap(entries);
150
+ }
151
+
152
+ // ============================================================================
153
+ // Mutable collection factories
154
+ // ============================================================================
155
+
156
+ /**
157
+ * Creates a new mutable ArrayList with optional initial elements.
158
+ *
159
+ * @example
160
+ * const list = mutableListOf(1, 2, 3)
161
+ * list.add(4)
162
+ */
163
+ export function mutableListOf<T>(...elements: T[]): ArrayList<T> {
164
+ return arrayList(elements);
165
+ }
166
+
167
+ /**
168
+ * Creates a new mutable HashMap with optional initial entries.
169
+ */
170
+ export function mutableMapOf<K, V>(...entries: [K, V][]): HashMap<K, V> {
171
+ return hashMap(entries);
172
+ }
173
+
174
+ // ============================================================================
175
+ // Immutable collection factories
176
+ // ============================================================================
177
+
178
+ /**
179
+ * Creates an immutable List from the given elements.
180
+ *
181
+ * @example
182
+ * const list = immutableListOf(1, 2, 3)
183
+ * list.get(0) // 1
184
+ * list.add(4) // Error: Cannot modify readonly collection
185
+ */
186
+ export function immutableListOf<T>(...elements: T[]): List<T> {
187
+ if (elements.length === 0) {
188
+ return emptyList<T>();
189
+ }
190
+ if (elements.length === 1) {
191
+ return singletonList(elements[0]! as T);
192
+ }
193
+ const list = arrayList(elements);
194
+ return compose(readonly<List<T>>())(list);
195
+ }
196
+
197
+ /**
198
+ * Creates an immutable Map from the given entries.
199
+ */
200
+ export function immutableMapOf<K, V>(...entries: [K, V][]): MapLike<K, V> {
201
+ if (entries.length === 0) {
202
+ return emptyMap<K, V>();
203
+ }
204
+ if (entries.length === 1) {
205
+ return singletonMap(entries[0]![0], entries[0]![1]);
206
+ }
207
+ const map = hashMap(entries);
208
+ return compose(readonly<MapLike<K, V>>())(map);
209
+ }
210
+
211
+ // ============================================================================
212
+ // Range factories
213
+ // ============================================================================
214
+
215
+ /**
216
+ * Creates a list containing integers from start (inclusive) to end (exclusive).
217
+ *
218
+ * @example
219
+ * const list = range(0, 5) // [0, 1, 2, 3, 4]
220
+ * const list2 = range(10, 15) // [10, 11, 12, 13, 14]
221
+ */
222
+ export function range(start: number, end: number, step: number = 1): ArrayList<number> {
223
+ if (step === 0) {
224
+ throw new Error("step cannot be zero");
225
+ }
226
+
227
+ const list = arrayList<number>();
228
+
229
+ if (step > 0) {
230
+ for (let i = start; i < end; i += step) {
231
+ list.add(i);
232
+ }
233
+ } else {
234
+ for (let i = start; i > end; i += step) {
235
+ list.add(i);
236
+ }
237
+ }
238
+
239
+ return list;
240
+ }
241
+
242
+ /**
243
+ * Creates a list containing integers from 0 to n (exclusive).
244
+ *
245
+ * @example
246
+ * const list = rangeTo(5) // [0, 1, 2, 3, 4]
247
+ */
248
+ export function rangeTo(n: number): ArrayList<number> {
249
+ return range(0, n);
250
+ }
251
+
252
+ // ============================================================================
253
+ // Collection builders
254
+ // ============================================================================
255
+
256
+ /**
257
+ * Builder for constructing lists with a fluent API.
258
+ *
259
+ * @example
260
+ * const list = buildList<number>((builder) => {
261
+ * builder.add(1)
262
+ * builder.add(2)
263
+ * if (someCondition) {
264
+ * builder.add(3)
265
+ * }
266
+ * })
267
+ */
268
+ export function buildList<T>(builder: (list: ArrayList<T>) => void): ArrayList<T> {
269
+ const list = arrayList<T>();
270
+ builder(list);
271
+ return list;
272
+ }
273
+
274
+ /**
275
+ * Builder for constructing maps with a fluent API.
276
+ */
277
+ export function buildMap<K, V>(builder: (map: HashMap<K, V>) => void): HashMap<K, V> {
278
+ const map = hashMap<K, V>();
279
+ builder(map);
280
+ return map;
281
+ }
282
+
283
+ // ============================================================================
284
+ // Conversion utilities
285
+ // ============================================================================
286
+
287
+ /**
288
+ * Converts an iterable to an ArrayList.
289
+ *
290
+ * @example
291
+ * const set = new Set([1, 2, 3])
292
+ * const list = toList(set)
293
+ */
294
+ export function toList<T>(iterable: Iterable<T>): ArrayList<T> {
295
+ return arrayList(iterable);
296
+ }
297
+
298
+ /**
299
+ * Converts an iterable to a HashMap using a key extractor function.
300
+ *
301
+ * @example
302
+ * const users = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]
303
+ * const userMap = toMap(users, u => u.id)
304
+ */
305
+ export function toMap<T, K>(
306
+ iterable: Iterable<T>,
307
+ keyExtractor: (value: T) => K
308
+ ): HashMap<K, T>;
309
+ export function toMap<T, K, V>(
310
+ iterable: Iterable<T>,
311
+ keyExtractor: (value: T) => K,
312
+ valueExtractor: (value: T) => V
313
+ ): HashMap<K, V>;
314
+ export function toMap<T, K, V>(
315
+ iterable: Iterable<T>,
316
+ keyExtractor: (value: T) => K,
317
+ valueExtractor?: (value: T) => V
318
+ ): HashMap<K, any> {
319
+ const map = hashMap<K, any>();
320
+ for (const item of iterable) {
321
+ const key = keyExtractor(item);
322
+ const value = valueExtractor ? valueExtractor(item) : item;
323
+ map.set(key, value);
324
+ }
325
+ return map;
326
+ }
327
+
328
+ // ============================================================================
329
+ // Grouping utilities
330
+ // ============================================================================
331
+
332
+ /**
333
+ * Groups elements by a key extractor function.
334
+ *
335
+ * @example
336
+ * const users = [
337
+ * { age: 25, name: 'Alice' },
338
+ * { age: 30, name: 'Bob' },
339
+ * { age: 25, name: 'Charlie' }
340
+ * ]
341
+ * const byAge = groupBy(users, u => u.age)
342
+ * // Map { 25 => [Alice, Charlie], 30 => [Bob] }
343
+ */
344
+ export function groupBy<T, K>(
345
+ iterable: Iterable<T>,
346
+ keyExtractor: (value: T) => K
347
+ ): HashMap<K, ArrayList<T>> {
348
+ const map = hashMap<K, ArrayList<T>>();
349
+
350
+ for (const item of iterable) {
351
+ const key = keyExtractor(item);
352
+ const list = map.computeIfAbsent(key, () => arrayList<T>());
353
+ list.add(item);
354
+ }
355
+
356
+ return map;
357
+ }
@@ -0,0 +1,248 @@
1
+ /**
2
+ * Common helper utilities for collections.
3
+ *
4
+ * Provides shared functionality to reduce code duplication across implementations.
5
+ */
6
+
7
+ import type { Eq } from "../core/traits.js";
8
+
9
+ // ============================================================================
10
+ // Bounds checking utilities
11
+ // ============================================================================
12
+
13
+ /**
14
+ * Checks if an index is within valid bounds [0, size).
15
+ * Throws RangeError if out of bounds.
16
+ *
17
+ * @param index The index to check
18
+ * @param size The size of the collection
19
+ * @param collectionName Optional name for error message
20
+ * @throws RangeError if index is out of bounds
21
+ */
22
+ export function checkBounds(index: number, size: number, collectionName: string = "Collection"): void {
23
+ if (index < 0 || index >= size) {
24
+ throw new RangeError(`${collectionName}: Index ${index} out of bounds [0, ${size})`);
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Checks if an index is valid for insertion [0, size].
30
+ * Allows index === size for append operations.
31
+ *
32
+ * @param index The index to check
33
+ * @param size The size of the collection
34
+ * @param collectionName Optional name for error message
35
+ * @throws RangeError if index is out of bounds
36
+ */
37
+ export function checkInsertBounds(index: number, size: number, collectionName: string = "Collection"): void {
38
+ if (index < 0 || index > size) {
39
+ throw new RangeError(`${collectionName}: Index ${index} out of bounds [0, ${size}]`);
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Checks if a range [start, end) is valid.
45
+ *
46
+ * @param start Start index (inclusive)
47
+ * @param end End index (exclusive)
48
+ * @param size The size of the collection
49
+ * @param collectionName Optional name for error message
50
+ * @throws RangeError if range is invalid
51
+ */
52
+ export function checkRangeBounds(
53
+ start: number,
54
+ end: number,
55
+ size: number,
56
+ collectionName: string = "Collection"
57
+ ): void {
58
+ if (start < 0 || end > size || start > end) {
59
+ throw new RangeError(
60
+ `${collectionName}: Invalid range [${start}, ${end}) for size ${size}`
61
+ );
62
+ }
63
+ }
64
+
65
+ // ============================================================================
66
+ // Equality and search utilities
67
+ // ============================================================================
68
+
69
+ /**
70
+ * Finds the index of an element in an array using custom equality.
71
+ *
72
+ * @param array The array to search
73
+ * @param element The element to find
74
+ * @param eq Equality function
75
+ * @returns Index of element, or -1 if not found
76
+ */
77
+ export function findIndex<T>(array: T[], element: T, eq: Eq<T>): number {
78
+ for (let i = 0; i < array.length; i++) {
79
+ if (eq(array[i]!, element)) {
80
+ return i;
81
+ }
82
+ }
83
+ return -1;
84
+ }
85
+
86
+ /**
87
+ * Finds the last index of an element in an array using custom equality.
88
+ *
89
+ * @param array The array to search
90
+ * @param element The element to find
91
+ * @param eq Equality function
92
+ * @returns Last index of element, or -1 if not found
93
+ */
94
+ export function findLastIndex<T>(array: T[], element: T, eq: Eq<T>): number {
95
+ for (let i = array.length - 1; i >= 0; i--) {
96
+ if (eq(array[i]!, element)) {
97
+ return i;
98
+ }
99
+ }
100
+ return -1;
101
+ }
102
+
103
+ /**
104
+ * Checks if an array contains an element using custom equality.
105
+ *
106
+ * @param array The array to search
107
+ * @param element The element to find
108
+ * @param eq Equality function
109
+ * @returns true if element is found
110
+ */
111
+ export function arrayContains<T>(array: T[], element: T, eq: Eq<T>): boolean {
112
+ return findIndex(array, element, eq) !== -1;
113
+ }
114
+
115
+ /**
116
+ * Removes the first occurrence of an element from an array.
117
+ *
118
+ * @param array The array to modify
119
+ * @param element The element to remove
120
+ * @param eq Equality function
121
+ * @returns true if element was found and removed
122
+ */
123
+ export function arrayRemove<T>(array: T[], element: T, eq: Eq<T>): boolean {
124
+ const index = findIndex(array, element, eq);
125
+ if (index === -1) return false;
126
+ array.splice(index, 1);
127
+ return true;
128
+ }
129
+
130
+ /**
131
+ * Removes all occurrences of an element from an array.
132
+ *
133
+ * @param array The array to modify
134
+ * @param element The element to remove
135
+ * @param eq Equality function
136
+ * @returns Number of elements removed
137
+ */
138
+ export function arrayRemoveAll<T>(array: T[], element: T, eq: Eq<T>): number {
139
+ let count = 0;
140
+ for (let i = array.length - 1; i >= 0; i--) {
141
+ if (eq(array[i]!, element)) {
142
+ array.splice(i, 1);
143
+ count++;
144
+ }
145
+ }
146
+ return count;
147
+ }
148
+
149
+ // ============================================================================
150
+ // Iterator utilities
151
+ // ============================================================================
152
+
153
+ /**
154
+ * Creates a simple iterator for an array.
155
+ *
156
+ * @param array The array to iterate
157
+ * @returns Iterator over the array
158
+ */
159
+ export function* arrayIterator<T>(array: T[]): Iterator<T> {
160
+ yield* array;
161
+ }
162
+
163
+ /**
164
+ * Creates a reversed iterator for an array.
165
+ *
166
+ * @param array The array to iterate
167
+ * @returns Reversed iterator over the array
168
+ */
169
+ export function* reverseArrayIterator<T>(array: T[]): Iterator<T> {
170
+ for (let i = array.length - 1; i >= 0; i--) {
171
+ yield array[i]!;
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Converts an iterable to an array.
177
+ *
178
+ * @param iterable The iterable to convert
179
+ * @returns Array containing all elements
180
+ */
181
+ export function toArray<T>(iterable: Iterable<T>): T[] {
182
+ return Array.from(iterable);
183
+ }
184
+
185
+ // ============================================================================
186
+ // Validation utilities
187
+ // ============================================================================
188
+
189
+ /**
190
+ * Ensures a value is not null or undefined.
191
+ *
192
+ * @param value The value to check
193
+ * @param message Error message
194
+ * @returns The non-null value
195
+ * @throws Error if value is null or undefined
196
+ */
197
+ export function requireNonNull<T>(value: T | null | undefined, message: string = "Value cannot be null or undefined"): T {
198
+ if (value === null || value === undefined) {
199
+ throw new Error(message);
200
+ }
201
+ return value;
202
+ }
203
+
204
+ /**
205
+ * Ensures a collection is not empty.
206
+ *
207
+ * @param size Current size
208
+ * @param collectionName Optional name for error message
209
+ * @throws Error if collection is empty
210
+ */
211
+ export function requireNonEmpty(size: number, collectionName: string = "Collection"): void {
212
+ if (size === 0) {
213
+ throw new Error(`${collectionName} is empty`);
214
+ }
215
+ }
216
+
217
+ // ============================================================================
218
+ // Math utilities
219
+ // ============================================================================
220
+
221
+ /**
222
+ * Clamps a value between min and max.
223
+ *
224
+ * @param value The value to clamp
225
+ * @param min Minimum value
226
+ * @param max Maximum value
227
+ * @returns Clamped value
228
+ */
229
+ export function clamp(value: number, min: number, max: number): number {
230
+ return Math.max(min, Math.min(max, value));
231
+ }
232
+
233
+ /**
234
+ * Calculates hash code for a string.
235
+ * Uses Java's String.hashCode() algorithm.
236
+ *
237
+ * @param str The string to hash
238
+ * @returns Hash code
239
+ */
240
+ export function stringHashCode(str: string): number {
241
+ let hash = 0;
242
+ for (let i = 0; i < str.length; i++) {
243
+ const char = str.charCodeAt(i);
244
+ hash = ((hash << 5) - hash) + char;
245
+ hash = hash & hash; // Convert to 32-bit integer
246
+ }
247
+ return hash;
248
+ }