@grain/stdlib 0.5.13 → 0.6.1

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 (155) hide show
  1. package/CHANGELOG.md +201 -0
  2. package/LICENSE +1 -1
  3. package/README.md +25 -2
  4. package/array.gr +1512 -199
  5. package/array.md +2032 -94
  6. package/bigint.gr +239 -140
  7. package/bigint.md +450 -106
  8. package/buffer.gr +595 -102
  9. package/buffer.md +903 -145
  10. package/bytes.gr +401 -110
  11. package/bytes.md +551 -63
  12. package/char.gr +228 -49
  13. package/char.md +373 -7
  14. package/exception.gr +26 -12
  15. package/exception.md +29 -5
  16. package/float32.gr +130 -109
  17. package/float32.md +185 -57
  18. package/float64.gr +112 -99
  19. package/float64.md +185 -57
  20. package/hash.gr +62 -40
  21. package/hash.md +27 -3
  22. package/int16.gr +430 -0
  23. package/int16.md +618 -0
  24. package/int32.gr +200 -269
  25. package/int32.md +254 -289
  26. package/int64.gr +142 -225
  27. package/int64.md +254 -289
  28. package/int8.gr +511 -0
  29. package/int8.md +786 -0
  30. package/json.gr +2071 -0
  31. package/json.md +646 -0
  32. package/list.gr +120 -68
  33. package/list.md +125 -80
  34. package/map.gr +560 -57
  35. package/map.md +672 -56
  36. package/marshal.gr +239 -227
  37. package/marshal.md +36 -4
  38. package/number.gr +626 -676
  39. package/number.md +738 -153
  40. package/option.gr +33 -35
  41. package/option.md +58 -42
  42. package/package.json +2 -2
  43. package/path.gr +148 -187
  44. package/path.md +47 -96
  45. package/pervasives.gr +75 -416
  46. package/pervasives.md +85 -180
  47. package/priorityqueue.gr +433 -74
  48. package/priorityqueue.md +422 -54
  49. package/queue.gr +362 -80
  50. package/queue.md +433 -38
  51. package/random.gr +67 -75
  52. package/random.md +68 -40
  53. package/range.gr +135 -63
  54. package/range.md +198 -43
  55. package/rational.gr +284 -0
  56. package/rational.md +545 -0
  57. package/regex.gr +933 -1066
  58. package/regex.md +59 -60
  59. package/result.gr +23 -25
  60. package/result.md +54 -39
  61. package/runtime/atof/common.gr +78 -82
  62. package/runtime/atof/common.md +22 -10
  63. package/runtime/atof/decimal.gr +102 -127
  64. package/runtime/atof/decimal.md +28 -7
  65. package/runtime/atof/lemire.gr +56 -71
  66. package/runtime/atof/lemire.md +9 -1
  67. package/runtime/atof/parse.gr +83 -110
  68. package/runtime/atof/parse.md +12 -2
  69. package/runtime/atof/slow.gr +28 -35
  70. package/runtime/atof/slow.md +9 -1
  71. package/runtime/atof/table.gr +19 -18
  72. package/runtime/atof/table.md +10 -2
  73. package/runtime/atoi/parse.gr +153 -136
  74. package/runtime/atoi/parse.md +50 -1
  75. package/runtime/bigint.gr +410 -517
  76. package/runtime/bigint.md +71 -57
  77. package/runtime/compare.gr +176 -85
  78. package/runtime/compare.md +31 -1
  79. package/runtime/dataStructures.gr +144 -32
  80. package/runtime/dataStructures.md +267 -31
  81. package/runtime/debugPrint.gr +34 -15
  82. package/runtime/debugPrint.md +37 -5
  83. package/runtime/equal.gr +53 -52
  84. package/runtime/equal.md +30 -1
  85. package/runtime/exception.gr +38 -47
  86. package/runtime/exception.md +10 -8
  87. package/runtime/gc.gr +23 -152
  88. package/runtime/gc.md +13 -17
  89. package/runtime/malloc.gr +31 -31
  90. package/runtime/malloc.md +11 -3
  91. package/runtime/numberUtils.gr +193 -174
  92. package/runtime/numberUtils.md +29 -9
  93. package/runtime/numbers.gr +1695 -1021
  94. package/runtime/numbers.md +1098 -134
  95. package/runtime/string.gr +543 -245
  96. package/runtime/string.md +76 -6
  97. package/runtime/unsafe/constants.gr +30 -13
  98. package/runtime/unsafe/constants.md +80 -0
  99. package/runtime/unsafe/conv.gr +55 -28
  100. package/runtime/unsafe/conv.md +41 -9
  101. package/runtime/unsafe/memory.gr +10 -30
  102. package/runtime/unsafe/memory.md +15 -19
  103. package/runtime/unsafe/tags.gr +37 -21
  104. package/runtime/unsafe/tags.md +88 -8
  105. package/runtime/unsafe/wasmf32.gr +30 -36
  106. package/runtime/unsafe/wasmf32.md +64 -56
  107. package/runtime/unsafe/wasmf64.gr +30 -36
  108. package/runtime/unsafe/wasmf64.md +64 -56
  109. package/runtime/unsafe/wasmi32.gr +49 -66
  110. package/runtime/unsafe/wasmi32.md +102 -94
  111. package/runtime/unsafe/wasmi64.gr +52 -79
  112. package/runtime/unsafe/wasmi64.md +108 -100
  113. package/runtime/utils/printing.gr +13 -15
  114. package/runtime/utils/printing.md +11 -3
  115. package/runtime/wasi.gr +294 -295
  116. package/runtime/wasi.md +62 -42
  117. package/set.gr +574 -64
  118. package/set.md +634 -54
  119. package/stack.gr +181 -64
  120. package/stack.md +271 -42
  121. package/string.gr +453 -533
  122. package/string.md +241 -151
  123. package/uint16.gr +369 -0
  124. package/uint16.md +585 -0
  125. package/uint32.gr +470 -0
  126. package/uint32.md +737 -0
  127. package/uint64.gr +471 -0
  128. package/uint64.md +737 -0
  129. package/uint8.gr +369 -0
  130. package/uint8.md +585 -0
  131. package/uri.gr +1093 -0
  132. package/uri.md +477 -0
  133. package/{sys → wasi}/file.gr +914 -500
  134. package/{sys → wasi}/file.md +454 -50
  135. package/wasi/process.gr +292 -0
  136. package/{sys → wasi}/process.md +164 -6
  137. package/wasi/random.gr +77 -0
  138. package/wasi/random.md +80 -0
  139. package/{sys → wasi}/time.gr +15 -22
  140. package/{sys → wasi}/time.md +5 -5
  141. package/immutablearray.gr +0 -929
  142. package/immutablearray.md +0 -1038
  143. package/immutablemap.gr +0 -493
  144. package/immutablemap.md +0 -479
  145. package/immutablepriorityqueue.gr +0 -360
  146. package/immutablepriorityqueue.md +0 -291
  147. package/immutableset.gr +0 -498
  148. package/immutableset.md +0 -449
  149. package/runtime/debug.gr +0 -2
  150. package/runtime/debug.md +0 -6
  151. package/runtime/unsafe/errors.gr +0 -36
  152. package/runtime/unsafe/errors.md +0 -204
  153. package/sys/process.gr +0 -254
  154. package/sys/random.gr +0 -79
  155. package/sys/random.md +0 -66
package/array.gr CHANGED
@@ -1,40 +1,55 @@
1
1
  /**
2
- * @module Array: Utilities for working with arrays.
2
+ * Utilities for working with arrays.
3
3
  *
4
- * @example import Array from "array"
4
+ * An immutable array implementation is available in the `Immutable` submodule.
5
+ *
6
+ * @example from "array" include Array
7
+ * @example [> 1, 2, 3]
5
8
  *
6
9
  * @since v0.2.0
7
10
  * @history v0.1.0: Originally named `arrays`
8
11
  * @history v0.2.0: Renamed to `array`
9
12
  */
13
+ module Array
10
14
 
11
- import WasmI32 from "runtime/unsafe/wasmi32"
12
- import Memory from "runtime/unsafe/memory"
13
- import { allocateArray, tagSimpleNumber } from "runtime/dataStructures"
14
- import Exception from "runtime/exception"
15
- import { coerceNumberToWasmI32 } from "runtime/numbers"
15
+ from "number" include Number
16
+ from "list" include List
17
+ from "runtime/unsafe/wasmi32" include WasmI32
18
+ from "runtime/unsafe/memory" include Memory
19
+ from "runtime/dataStructures" include DataStructures
20
+ use DataStructures.{ allocateArray, tagSimpleNumber }
21
+ from "runtime/exception" include Exception
22
+ from "runtime/numbers" include Numbers
23
+ use Numbers.{ coerceNumberToWasmI32 }
16
24
 
17
- /**
18
- * @section Values: Functions for working with the Array data type.
19
- */
20
-
21
- @unsafe
22
- let mut _ARRAY_LENGTH_OFFSET = 4n
23
25
  @unsafe
24
- let mut _ARRAY_START_OFFSET = 8n
26
+ let _ARRAY_START_OFFSET = 8n
25
27
 
26
28
  @unsafe
27
29
  let checkLength = length => {
30
+ use WasmI32.{ (&), (>>), (<) }
28
31
  let ptr = WasmI32.fromGrain(length)
29
- if (WasmI32.eqz(WasmI32.and(ptr, 1n))) {
32
+ if (WasmI32.eqz(ptr & 1n)) {
30
33
  throw Exception.InvalidArgument("Length argument must be an integer")
31
34
  }
32
- let len = WasmI32.shrS(ptr, 1n)
33
- if (WasmI32.ltS(len, 0n)) {
35
+ let len = ptr >> 1n
36
+ if (len < 0n) {
34
37
  throw Exception.InvalidArgument("Length argument must be non-negative")
35
38
  }
36
39
  }
37
40
 
41
+ /**
42
+ * A simple helper function to convert a negative array index
43
+ * number to its positive positional equivalent.
44
+ */
45
+ let wrapNegativeIndex = (arrLen, idx) => {
46
+ if (idx >= 0) {
47
+ idx
48
+ } else {
49
+ arrLen + idx
50
+ }
51
+ }
52
+
38
53
  /**
39
54
  * Provides the length of the input array.
40
55
  *
@@ -45,10 +60,7 @@ let checkLength = length => {
45
60
  * @since v0.1.0
46
61
  */
47
62
  @unsafe
48
- export let length = array => {
49
- let ptr = WasmI32.fromGrain(array: Array<a>)
50
- tagSimpleNumber(WasmI32.load(ptr, _ARRAY_LENGTH_OFFSET))
51
- }
63
+ provide primitive length = "@array.length"
52
64
 
53
65
  /**
54
66
  * Creates a new array of the specified length with each element being
@@ -61,20 +73,20 @@ export let length = array => {
61
73
  * @throws InvalidArgument(String): When `length` is not an integer
62
74
  * @throws InvalidArgument(String): When `length` is negative
63
75
  *
64
- * @example Array.make(5, "foo") // [> "foo", "foo", "foo", "foo", "foo"]
76
+ * @example Array.make(5, "foo") == [> "foo", "foo", "foo", "foo", "foo"]
65
77
  *
66
78
  * @since v0.1.0
67
79
  */
68
80
  @unsafe
69
- export let make: (Number, a) -> Array<a> = (length: Number, item: a) => {
70
- let lengthArg = length
81
+ provide let make = (length: Number, item: a) => {
82
+ use WasmI32.{ (+), (*), (<) }
71
83
  checkLength(length)
72
84
  let length = coerceNumberToWasmI32(length)
73
- let byteLength = WasmI32.mul(length, 4n)
85
+ let byteLength = length * 4n
74
86
  let array = allocateArray(length)
75
- for (let mut i = 0n; WasmI32.ltS(i, byteLength); i = WasmI32.add(i, 4n)) {
87
+ for (let mut i = 0n; i < byteLength; i += 4n) {
76
88
  WasmI32.store(
77
- WasmI32.add(array, i),
89
+ array + i,
78
90
  Memory.incRef(WasmI32.fromGrain(item)),
79
91
  _ARRAY_START_OFFSET
80
92
  )
@@ -94,28 +106,25 @@ export let make: (Number, a) -> Array<a> = (length: Number, item: a) => {
94
106
  * @throws InvalidArgument(String): When `length` is not an integer
95
107
  * @throws InvalidArgument(String): When `length` is negative
96
108
  *
97
- * @example Array.init(5, n => n + 3) // [> 3, 4, 5, 6, 7]
109
+ * @example Array.init(5, n => n + 3) == [> 3, 4, 5, 6, 7]
98
110
  *
99
111
  * @since v0.1.0
100
112
  */
101
113
  @unsafe
102
- export let init: (Number, Number -> a) -> Array<a> =
103
- (
104
- length: Number,
105
- fn: Number -> a,
106
- ) => {
114
+ provide let init = (length: Number, fn: Number => a) => {
115
+ use WasmI32.{ (+), (*), (<) }
107
116
  checkLength(length)
108
117
  let length = coerceNumberToWasmI32(length)
109
- let byteLength = WasmI32.mul(length, 4n)
118
+ let byteLength = length * 4n
110
119
  let array = allocateArray(length)
111
120
  let mut index = 0n
112
- for (let mut i = 0n; WasmI32.ltS(i, byteLength); i = WasmI32.add(i, 4n)) {
121
+ for (let mut i = 0n; i < byteLength; i += 4n) {
113
122
  WasmI32.store(
114
- WasmI32.add(array, i),
123
+ array + i,
115
124
  Memory.incRef(WasmI32.fromGrain(fn(tagSimpleNumber(index)))),
116
125
  _ARRAY_START_OFFSET
117
126
  )
118
- index = WasmI32.add(index, 1n)
127
+ index += 1n
119
128
  }
120
129
  WasmI32.toGrain(array): Array<a>
121
130
  }
@@ -129,12 +138,16 @@ export let init: (Number, Number -> a) -> Array<a> =
129
138
  * @param index: The index to access
130
139
  * @param array: The array to access
131
140
  * @returns The element from the array
132
- * @example Array.get(1,[> 1, 2, 3, 4, 5]) == 2
141
+ *
142
+ * @throws IndexOutOfBounds: When `index` is not an integer
143
+ * @throws IndexOutOfBounds: When `index` is out of bounds
144
+ *
145
+ * @example Array.get(1, [> 1, 2, 3, 4, 5]) == 2
133
146
  *
134
147
  * @since v0.1.0
135
148
  * @history v0.2.0: Argument order changed to data-last
136
149
  */
137
- export let get = (index, array) => {
150
+ provide let get = (index, array) => {
138
151
  array[index]
139
152
  }
140
153
 
@@ -147,12 +160,19 @@ export let get = (index, array) => {
147
160
  * @param index: The index to update
148
161
  * @param value: The value to store
149
162
  * @param array: The array to update
150
- * @example Array.set(1, 9, [> 1, 2, 3, 4, 5]) == [> 1, 9, 3, 4, 5]
163
+ *
164
+ * @throws IndexOutOfBounds: When `index` is not an integer
165
+ * @throws IndexOutOfBounds: When `index` is out of bounds
166
+ *
167
+ * @example
168
+ * let array = [> 1, 2, 3, 4, 5]
169
+ * Array.set(1, 9, array)
170
+ * assert array == [> 1, 9, 3, 4, 5]
151
171
  *
152
172
  * @since v0.1.0
153
173
  * @history v0.2.0: Argument order changed to data-last
154
174
  */
155
- export let set = (index, value, array) => {
175
+ provide let set = (index, value, array) => {
156
176
  array[index] = value
157
177
  }
158
178
 
@@ -163,11 +183,14 @@ export let set = (index, value, array) => {
163
183
  * @param array1: The array containing elements to appear first
164
184
  * @param array2: The array containing elements to appear second
165
185
  * @returns The new array containing elements from `array1` followed by elements from `array2`
186
+ *
187
+ * @throws InvalidArgument(String): When the combined length of the two arrays is not an integer
188
+ *
166
189
  * @example Array.append([> 1, 2], [> 3, 4, 5]) == [> 1, 2, 3, 4, 5]
167
190
  *
168
191
  * @since v0.1.0
169
192
  */
170
- export let append = (array1, array2) => {
193
+ provide let append = (array1, array2) => {
171
194
  let len1 = length(array1)
172
195
  let len2 = length(array2)
173
196
 
@@ -186,11 +209,14 @@ export let append = (array1, array2) => {
186
209
  *
187
210
  * @param arrays: A list containing all arrays to combine
188
211
  * @returns The new array
212
+ *
213
+ * @throws InvalidArgument(String): When the combined length of all arrays is not an integer
214
+ *
189
215
  * @example Array.concat([[> 1, 2], [> 3, 4], [> 5, 6]]) == [> 1, 2, 3, 4, 5, 6]
190
216
  *
191
217
  * @since v0.1.0
192
218
  */
193
- export let concat = arrays => {
219
+ provide let concat = arrays => {
194
220
  // This function is slightly verbose to avoid depending on the List stdlib.
195
221
 
196
222
  let rec findLength = (arrays, acc) => {
@@ -230,9 +256,11 @@ export let concat = arrays => {
230
256
  * @param array: The array to copy
231
257
  * @returns The new array containing the elements from the input
232
258
  *
259
+ * @example Array.copy([> 1, 2, 3]) == [> 1, 2, 3]
260
+ *
233
261
  * @since v0.1.0
234
262
  */
235
- export let copy = array => {
263
+ provide let copy = array => {
236
264
  init(length(array), n => array[n])
237
265
  }
238
266
 
@@ -243,9 +271,14 @@ export let copy = array => {
243
271
  * @param n: The number of times to iterate the given array
244
272
  * @param array: The array to iterate
245
273
  *
274
+ * @example
275
+ * let mut str = ""
276
+ * Array.cycle(s => str = str ++ s, 2, [> "a", "b", "c"])
277
+ * assert str == "abcabc"
278
+ *
246
279
  * @since v0.4.4
247
280
  */
248
- export let cycle = (fn, n, array) => {
281
+ provide let cycle = (fn, n, array) => {
249
282
  let length = length(array)
250
283
  for (let mut iteration = 0; iteration < n; iteration += 1) {
251
284
  for (let mut count = 0; count < length; count += 1) {
@@ -260,10 +293,15 @@ export let cycle = (fn, n, array) => {
260
293
  * @param fn: The iterator function to call with each element
261
294
  * @param array: The array to iterate
262
295
  *
296
+ * @example
297
+ * let mut str = ""
298
+ * Array.forEach(s => str = str ++ s, [> "a", "b", "c"])
299
+ * assert str == "abc"
300
+ *
263
301
  * @since v0.1.0
264
302
  * @history v0.2.0: Argument order changed to data-last
265
303
  */
266
- export let forEach = (fn, array) => {
304
+ provide let forEach = (fn, array) => {
267
305
  let length = length(array)
268
306
  for (let mut count = 0; count < length; count += 1) {
269
307
  fn(array[count]): Void
@@ -277,10 +315,15 @@ export let forEach = (fn, array) => {
277
315
  * @param fn: The iterator function to call with each element
278
316
  * @param array: The array to iterate
279
317
  *
318
+ * @example
319
+ * let mut str = ""
320
+ * Array.forEachi((s, i) => str = str ++ s ++ toString(i), [> "a", "b", "c"])
321
+ * assert str == "a0b1c2"
322
+ *
280
323
  * @since v0.1.0
281
324
  * @history v0.2.0: Argument order changed to data-last
282
325
  */
283
- export let forEachi = (fn, array) => {
326
+ provide let forEachi = (fn, array) => {
284
327
  let length = length(array)
285
328
  for (let mut count = 0; count < length; count += 1) {
286
329
  fn(array[count], count): Void
@@ -295,10 +338,12 @@ export let forEachi = (fn, array) => {
295
338
  * @param array: The array to iterate
296
339
  * @returns The new array with mapped values
297
340
  *
341
+ * @example Array.map(x => x * 2, [> 1, 2, 3]) == [> 2, 4, 6]
342
+ *
298
343
  * @since v0.1.0
299
344
  * @history v0.2.0: Argument order changed to data-last
300
345
  */
301
- export let map = (fn, array) => {
346
+ provide let map = (fn, array) => {
302
347
  let length = length(array)
303
348
  init(length, i => {
304
349
  fn(array[i])
@@ -313,9 +358,11 @@ export let map = (fn, array) => {
313
358
  * @param array: The array to iterate
314
359
  * @returns The new array with mapped values
315
360
  *
361
+ * @example Array.mapi((x, i) => (x * 2, i), [> 1, 2, 3]) == [> (2, 0), (4, 1), (6, 2)]
362
+ *
316
363
  * @since v0.1.0
317
364
  */
318
- export let mapi = (fn, array) => {
365
+ provide let mapi = (fn, array) => {
319
366
  let length = length(array)
320
367
  init(length, index => {
321
368
  fn(array[index], index)
@@ -336,11 +383,12 @@ export let mapi = (fn, array) => {
336
383
  * @param array: The array to iterate
337
384
  * @returns The final accumulator returned from `fn`
338
385
  *
339
- * @example Array.reduce((a, b) => a + b, 0, [> 1, 2, 3]) // 6
386
+ * @example Array.reduce((acc, el) => acc + el, 0, [> 1, 2, 3]) == 6
387
+ * @example Array.reduce((acc, el) => acc ++ el, "", [> "baz", "bar", "foo"]) == "bazbarfoo"
340
388
  *
341
389
  * @since v0.3.0
342
390
  */
343
- export let reduce = (fn, initial, array) => {
391
+ provide let reduce = (fn, initial, array) => {
344
392
  let mut acc = initial
345
393
  forEach(el => acc = fn(acc, el), array)
346
394
  acc
@@ -360,11 +408,11 @@ export let reduce = (fn, initial, array) => {
360
408
  * @param array: The array to iterate
361
409
  * @returns The final accumulator returned from `fn`
362
410
  *
363
- * @example Array.reduceRight((a, b) => b ++ a, "", [> "baz", "bar", "foo"]) // "foobarbaz"
411
+ * @example Array.reduceRight((el, acc) => acc ++ el, "", [> "baz", "bar", "foo"]) == "foobarbaz"
364
412
  *
365
413
  * @since v0.5.3
366
414
  */
367
- export let reduceRight = (fn, initial, array) => {
415
+ provide let reduceRight = (fn, initial, array) => {
368
416
  let mut acc = initial
369
417
  for (let mut i = length(array) - 1; i >= 0; i -= 1) {
370
418
  acc = fn(array[i], acc)
@@ -387,9 +435,16 @@ export let reduceRight = (fn, initial, array) => {
387
435
  * @param array: The array to iterate
388
436
  * @returns The final accumulator returned from `fn`
389
437
  *
438
+ * @example Array.reducei((acc, el, index) => acc + el + index, 0, [> 1, 2, 3]) == 9
439
+ * @example
440
+ * let output = Array.reducei((acc, el, index) => {
441
+ * acc ++ el ++ toString(index)
442
+ * }, "", [> "baz", "bar", "foo"])
443
+ * assert output == "baz0bar1foo2"
444
+ *
390
445
  * @since v0.3.0
391
446
  */
392
- export let reducei = (fn, initial, array) => {
447
+ provide let reducei = (fn, initial, array) => {
393
448
  let mut acc = initial
394
449
  forEachi((el, index) => acc = fn(acc, el, index), array)
395
450
  acc
@@ -405,9 +460,13 @@ export let reducei = (fn, initial, array) => {
405
460
  * @param array: The array to iterate
406
461
  * @returns The new array
407
462
  *
463
+ * @throws InvalidArgument(String): When the combined length of all arrays is not an integer
464
+ *
465
+ * @example Array.flatMap(e => [> 1, e], [> 1, 2, 3]) == [> 1, 1, 1, 2, 1, 3]
466
+ *
408
467
  * @since v0.3.0
409
468
  */
410
- export let flatMap = (fn, array) => {
469
+ provide let flatMap = (fn, array) => {
411
470
  let nested = map(fn, array)
412
471
  let arrLen = reduce((acc, arr) => acc + length(arr), 0, nested)
413
472
  let mut outerI = 0
@@ -429,11 +488,14 @@ export let flatMap = (fn, array) => {
429
488
  *
430
489
  * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
431
490
  * @param array: The array to check
432
- * @returns `true` if all elements satify the condition or `false` otherwise
491
+ * @returns `true` if all elements satisfy the condition or `false` otherwise
492
+ *
493
+ * @example Array.every(e => e % 2 == 0, [> 2, 4, 6]) == true
494
+ * @example Array.every(e => e % 2 == 0, [> 2, 4, 7]) == false
433
495
  *
434
496
  * @since v0.3.0
435
497
  */
436
- export let every = (fn, array) => {
498
+ provide let every = (fn, array) => {
437
499
  let len = length(array)
438
500
  let mut all = true
439
501
  for (let mut index = 0; all && index < len; index += 1) {
@@ -448,11 +510,15 @@ export let every = (fn, array) => {
448
510
  *
449
511
  * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
450
512
  * @param array: The array to iterate
451
- * @returns `true` if one or more elements satify the condition or `false` otherwise
513
+ * @returns `true` if one or more elements satisfy the condition or `false` otherwise
514
+ *
515
+ * @example Array.some(e => e % 2 == 0, [> 2, 4, 6]) == true
516
+ * @example Array.some(e => e % 2 == 0, [> 2, 4, 7]) == true
517
+ * @example Array.some(e => e % 2 == 0, [> 3, 5, 7]) == false
452
518
  *
453
519
  * @since v0.3.0
454
520
  */
455
- export let some = (fn, array) => {
521
+ provide let some = (fn, array) => {
456
522
  let len = length(array)
457
523
  let mut found = false
458
524
  for (let mut index = 0; !found && index < len; index += 1) {
@@ -467,9 +533,14 @@ export let some = (fn, array) => {
467
533
  * @param value: The value replacing each element
468
534
  * @param array: The array to update
469
535
  *
536
+ * @example
537
+ * let arr = [> 2, 4, 6]
538
+ * Array.fill(0, arr)
539
+ * assert arr == [> 0, 0, 0]
540
+ *
470
541
  * @since v0.2.0
471
542
  */
472
- export let fill = (value, array) => {
543
+ provide let fill = (value, array) => {
473
544
  let length = length(array)
474
545
  forEachi((_, index) => {
475
546
  array[index] = value
@@ -486,26 +557,32 @@ export let fill = (value, array) => {
486
557
  * @param stop: The (exclusive) index to end replacement
487
558
  * @param array: The array to update
488
559
  *
560
+ * @throws IndexOutOfBounds: When the start index is out of bounds
561
+ * @throws IndexOutOfBounds: When the start index is greater then the stop index
562
+ *
563
+ * @example
564
+ * let arr = [> 2, 4, 6, 8]
565
+ * Array.fillRange(0, 1, 3, arr)
566
+ * assert arr == [> 2, 0, 0, 8]
567
+ *
489
568
  * @since v0.2.0
490
569
  */
491
- export let fillRange = (value, start, stop, array) => {
570
+ provide let fillRange = (value, start, stop, array) => {
492
571
  let length = length(array)
493
- let startIndex = if (start < 0) length + start else start
494
- let stopIndex = if (stop < 0) length + stop else stop
495
-
496
- if (startIndex > length) {
497
- fail "The start index is outside the array"
572
+ let startIndex = wrapNegativeIndex(length, start)
573
+ let stopIndex = wrapNegativeIndex(length, stop)
574
+ // Ensure we aren't working with a `stop` value that is too big
575
+ let stopIndex = if (stopIndex < 0 || stopIndex > length) {
576
+ length
577
+ } else {
578
+ stopIndex
498
579
  }
499
- if (startIndex > stopIndex) {
500
- fail "The start index cannot be higher than the stop index"
580
+
581
+ if (startIndex > length || startIndex > stopIndex) {
582
+ throw IndexOutOfBounds
501
583
  }
502
584
 
503
- let mut index = startIndex
504
- for (
505
- let mut index = startIndex;
506
- index < stopIndex && index < length;
507
- index += 1
508
- ) {
585
+ for (let mut index = startIndex; index < stopIndex; index += 1) {
509
586
  array[index] = value
510
587
  }
511
588
  void
@@ -517,9 +594,11 @@ export let fillRange = (value, start, stop, array) => {
517
594
  * @param array: The array to reverse
518
595
  * @returns The new array
519
596
  *
597
+ * @example Array.reverse([> 1, 2, 3]) == [> 3, 2, 1]
598
+ *
520
599
  * @since v0.4.0
521
600
  */
522
- export let reverse = array => {
601
+ provide let reverse = array => {
523
602
  let len = length(array)
524
603
  init(len, index => {
525
604
  let last = len - index - 1
@@ -533,9 +612,11 @@ export let reverse = array => {
533
612
  * @param array: The array to convert
534
613
  * @returns The list containing all elements from the array
535
614
  *
615
+ * @example Array.toList([> 1, 2, 3]) == [1, 2, 3]
616
+ *
536
617
  * @since v0.1.0
537
618
  */
538
- export let toList = array => {
619
+ provide let toList = array => {
539
620
  let rec buildList = (acc, index) => {
540
621
  let index = index - 1
541
622
  if (index < 0) {
@@ -553,9 +634,11 @@ export let toList = array => {
553
634
  * @param list: The list to convert
554
635
  * @returns The array containing all elements from the list
555
636
  *
637
+ * @example Array.fromList([1, 2, 3]) == [> 1, 2, 3]
638
+ *
556
639
  * @since v0.1.0
557
640
  */
558
- export let fromList = list => {
641
+ provide let fromList = list => {
559
642
  let rec listLength = (list, acc) => {
560
643
  match (list) {
561
644
  [_, ...rest] => listLength(rest, acc + 1),
@@ -584,84 +667,62 @@ export let fromList = list => {
584
667
  * @param array: The array to inspect
585
668
  * @returns `true` if the value exists in the array or `false` otherwise
586
669
  *
670
+ * @example Array.contains(1, [> 1, 2, 3]) == true
671
+ * @example Array.contains(0, [> 1, 2, 3]) == false
672
+ *
587
673
  * @since v0.2.0
588
674
  */
589
- export let contains = (search, array) => {
675
+ provide let contains = (search, array) => {
590
676
  // TODO(#189): This should be rewritten to use recursion and pattern matching
591
677
  let len = length(array)
592
- let mut found = false
593
- for (let mut index = 0; !found && index < len; index += 1) {
594
- found = array[index] == search
678
+ for (let mut i = 0; i < len; i += 1) {
679
+ if (array[i] == search) return true
595
680
  }
596
- found
681
+ return false
597
682
  }
598
683
 
599
684
  /**
600
- * Finds the first element in an array that satifies the given condition.
685
+ * Finds the first element in an array that satisfies the given condition.
601
686
  *
602
687
  * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
603
688
  * @param array: The array to search
604
689
  * @returns `Some(element)` containing the first value found or `None` otherwise
605
690
  *
691
+ * @example Array.find(e => e % 2 == 0, [> 1, 4, 3]) == Some(4)
692
+ * @example Array.find(e => e % 2 == 0, [> 1, 2, 3, 4]) == Some(2)
693
+ * @example Array.find(e => e % 2 == 0, [> 1, 3, 5]) == None
694
+ *
606
695
  * @since v0.2.0
607
696
  */
608
- export let find = (fn, array) => {
609
- let length = length(array)
610
- if (length == 0) {
611
- None
612
- } else {
613
- let mut count = 0
614
- let mut matching = false
615
- let mut matchedItem = array[0]
616
- while (count < length) {
617
- if (fn(array[count])) {
618
- matching = true
619
- matchedItem = array[count]
620
- count = length
621
- } else {
622
- count += 1
623
- }
624
- }
625
- if (!matching) {
626
- None
627
- } else {
628
- Some(matchedItem)
697
+ provide let find = (fn, array) => {
698
+ let len = length(array)
699
+ for (let mut i = 0; i < len; i += 1) {
700
+ if (fn(array[i])) {
701
+ return Some(array[i])
629
702
  }
630
703
  }
704
+ return None
631
705
  }
632
706
 
633
707
  /**
634
- * Finds the first index in an array where the element satifies the given condition.
708
+ * Finds the first index in an array where the element satisfies the given condition.
635
709
  *
636
710
  * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
637
711
  * @param array: The array to search
638
712
  * @returns `Some(index)` containing the index of the first element found or `None` otherwise
639
713
  *
714
+ * @example Array.findIndex(e => e % 2 == 0, [> 1, 4, 3]) == Some(1)
715
+ * @example Array.findIndex(e => e % 2 == 0, [> 1, 2, 3, 4]) == Some(1)
716
+ * @example Array.findIndex(e => e % 2 == 0, [> 1, 3, 5]) == None
717
+ *
640
718
  * @since v0.2.0
641
719
  */
642
- export let findIndex = (fn, array) => {
643
- let length = length(array)
644
- if (length == 0) {
645
- None
646
- } else {
647
- let mut count = 0
648
- let mut matching = false
649
- let mut matchedIndex = 0
650
- while (count < length) {
651
- if (fn(array[count])) {
652
- matching = true
653
- matchedIndex = count
654
- count = length
655
- } else {
656
- count += 1
657
- }
658
- }
659
- if (!matching) {
660
- None
661
- } else {
662
- Some(matchedIndex)
663
- }
720
+ provide let findIndex = (fn, array) => {
721
+ let len = length(array)
722
+ for (let mut i = 0; i < len; i += 1) {
723
+ if (fn(array[i])) return Some(i)
664
724
  }
725
+ return None
665
726
  }
666
727
 
667
728
  /**
@@ -672,9 +733,13 @@ export let findIndex = (fn, array) => {
672
733
  * @param array2: The array to provide values for the second tuple element
673
734
  * @returns The new array containing all pairs of `(a, b)`
674
735
  *
736
+ * @throws InvalidArgument(String): When the multiplied array lengths are not an integer
737
+ *
738
+ * @example Array.product([> 1, 2], [> 3, 4]) == [> (1, 3), (1, 4), (2, 3), (2, 4)]
739
+ *
675
740
  * @since v0.2.0
676
741
  */
677
- export let product = (array1: Array<a>, array2: Array<b>) => {
742
+ provide let product = (array1: Array<a>, array2: Array<b>) => {
678
743
  let lenA = length(array1)
679
744
  let lenB = length(array2)
680
745
  let mut indexA = -1
@@ -682,8 +747,6 @@ export let product = (array1: Array<a>, array2: Array<b>) => {
682
747
  init(lenA * lenB, n => {
683
748
  if (n % lenB == 0) {
684
749
  indexA += 1
685
- } else {
686
- indexA = indexA
687
750
  }
688
751
  (array1[indexA], array2[n % lenB])
689
752
  })
@@ -696,18 +759,19 @@ export let product = (array1: Array<a>, array2: Array<b>) => {
696
759
  * @param array: The array to iterate
697
760
  * @returns The total number of elements that satisfy the condition
698
761
  *
762
+ * @example Array.count(e => e % 2 == 0, [> 1, 2, 3, 4]) == 2
763
+ *
699
764
  * @since v0.2.0
700
765
  */
701
- export let count = (fn, array) => {
702
- let length = length(array)
703
- let mut position = 0
704
- let mut count = 0
705
- for (let mut position = 0; position < length; position += 1) {
706
- if (fn(array[position])) {
707
- count += 1
766
+ provide let count = (fn, array) => {
767
+ let len = length(array)
768
+ let mut n = 0
769
+ for (let mut i = 0; i < len; i += 1) {
770
+ if (fn(array[i])) {
771
+ n += 1
708
772
  }
709
773
  }
710
- count
774
+ n
711
775
  }
712
776
 
713
777
  /**
@@ -718,17 +782,20 @@ export let count = (fn, array) => {
718
782
  * @param array: The array to iterate
719
783
  * @returns The total number of elements that satisfy the condition
720
784
  *
785
+ * @example Array.counti((e, i) => e % 2 == 0 && i % 2 == 0, [> 1, 2, 3, 4, 5]) == 0
786
+ * @example Array.counti((e, i) => e % 2 == 0 && i % 2 == 0, [> 0, 1, 2, 3, 5]) == 2
787
+ *
721
788
  * @since v0.3.0
722
789
  */
723
- export let counti = (fn, array) => {
724
- let length = length(array)
725
- let mut count = 0
726
- for (let mut position = 0; position < length; position += 1) {
727
- if (fn(array[position], position)) {
728
- count += 1
790
+ provide let counti = (fn, array) => {
791
+ let len = length(array)
792
+ let mut n = 0
793
+ for (let mut i = 0; i < len; i += 1) {
794
+ if (fn(array[i], i)) {
795
+ n += 1
729
796
  }
730
797
  }
731
- count
798
+ n
732
799
  }
733
800
 
734
801
  /**
@@ -740,9 +807,11 @@ export let counti = (fn, array) => {
740
807
  * @param array: The array to iterate
741
808
  * @returns The new array containing elements where `fn` returned `true`
742
809
  *
810
+ * @example Array.filter(e => e % 2 == 0, [> 1, 2, 3, 4]) == [> 2, 4]
811
+ *
743
812
  * @since v0.3.0
744
813
  */
745
- export let filter = (fn, array) => {
814
+ provide let filter = (fn, array) => {
746
815
  let filtered = copy(array)
747
816
  let mut position = 0
748
817
  forEach(el => {
@@ -765,9 +834,12 @@ export let filter = (fn, array) => {
765
834
  * @param array: The array to iterate
766
835
  * @returns The new array containing elements where `fn` returned `true`
767
836
  *
837
+ * @example Array.filteri((e, i) => e % 2 == 0, [> 1, 2, 3, 4]) == [> 2, 4]
838
+ * @example Array.filteri((e, i) => e % 2 == 1 && i % 2 == 0, [> 1, 2, 3, 4, 5]) == [> 1, 3, 5]
839
+ *
768
840
  * @since v0.3.0
769
841
  */
770
- export let filteri = (fn, array) => {
842
+ provide let filteri = (fn, array) => {
771
843
  let filtered = copy(array)
772
844
  let mut position = 0
773
845
  forEachi((el, index) => {
@@ -788,11 +860,16 @@ export let filteri = (fn, array) => {
788
860
  * @param array: The array to filter
789
861
  * @returns The new array with only unique values
790
862
  *
863
+ * @example Array.unique([> 1, 2, 1, 2, 3, 1]) == [> 1, 2, 3]
864
+ *
791
865
  * @since v0.3.0
792
866
  */
793
- export let unique = array => {
794
- filteri((el, index) =>
795
- findIndex(value => value == el, array) == Some(index), array)
867
+ provide let unique = array => {
868
+ // TODO(#1651): improve performance
869
+ filteri(
870
+ (el, index) => findIndex(value => value == el, array) == Some(index),
871
+ array
872
+ )
796
873
  }
797
874
 
798
875
  /**
@@ -804,19 +881,20 @@ export let unique = array => {
804
881
  * @param array2: The array to provide values for the second tuple element
805
882
  * @returns The new array containing indexed pairs of `(a, b)`
806
883
  *
807
- * @throws Failure(String): When the arrays have different sizes
884
+ * @throws IndexOutOfBounds: When the arrays have different sizes
885
+ *
886
+ * @example Array.zip([> 1, 2, 3], [> 4, 5, 6]) == [> (1, 4), (2, 5), (3, 6)]
808
887
  *
809
888
  * @since v0.4.0
889
+ * @history v0.6.0: Support zipping arrays of different sizes
810
890
  */
811
- export let zip = (array1: Array<a>, array2: Array<b>) => {
891
+ provide let zip = (array1: Array<a>, array2: Array<b>) => {
812
892
  let len = length(array1)
813
- if (len != length(array2)) {
814
- fail "arguments to zip must be same length"
815
- } else {
816
- init(len, n => {
817
- (array1[n], array2[n])
818
- })
819
- }
893
+ let len2 = length(array2)
894
+ let len = if (len > len2) len2 else len
895
+ init(len, n => {
896
+ (array1[n], array2[n])
897
+ })
820
898
  }
821
899
 
822
900
  /**
@@ -834,12 +912,14 @@ export let zip = (array1: Array<a>, array2: Array<b>) => {
834
912
  * @param array2: The array whose elements will each be passed to the function as the second argument
835
913
  * @returns The new array containing elements derived from applying the function to pairs of input array elements
836
914
  *
837
- * @example Array.zipWith((a, b) => a + b, [> 1, 2, 3], [> 4, 5, 6]) // [> 5, 7, 9]
838
- * @example Array.zipWith((a, b) => a * b, [> 1, 2, 3], [> 4, 5]) // [> 4, 10]
915
+ * @throws IndexOutOfBounds: When the arrays have different sizes
916
+ *
917
+ * @example Array.zipWith((a, b) => a + b, [> 1, 2, 3], [> 4, 5, 6]) == [> 5, 7, 9]
918
+ * @example Array.zipWith((a, b) => a * b, [> 1, 2, 3], [> 4, 5]) == [> 4, 10]
839
919
  *
840
920
  * @since v0.5.3
841
921
  */
842
- export let zipWith = (fn, array1: Array<a>, array2: Array<b>) => {
922
+ provide let zipWith = (fn, array1: Array<a>, array2: Array<b>) => {
843
923
  let len1 = length(array1)
844
924
  let len2 = length(array2)
845
925
  let minLen = if (len1 > len2) len2 else len1
@@ -854,9 +934,11 @@ export let zipWith = (fn, array1: Array<a>, array2: Array<b>) => {
854
934
  * @param array: The array of tuples to split
855
935
  * @returns An array containing all elements from the first tuple element, and an array containing all elements from the second tuple element
856
936
  *
937
+ * @example Array.unzip([> (1, 4), (2, 5), (3, 6)]) == ([> 1, 2, 3], [> 4, 5, 6])
938
+ *
857
939
  * @since v0.4.0
858
940
  */
859
- export let unzip = array => {
941
+ provide let unzip = array => {
860
942
  let lenArr = length(array)
861
943
 
862
944
  let a = init(lenArr, n => {
@@ -879,31 +961,34 @@ export let unzip = array => {
879
961
  * @param items: The input strings
880
962
  * @returns The concatenated string
881
963
  *
964
+ * @example Array.join(", ", [> "a", "b", "c"]) == "a, b, c"
965
+ *
882
966
  * @since v0.4.0
883
967
  */
884
- export let join = (separator: String, items: Array<String>) => {
885
- let iter = (acc, str) => {
886
- match (acc) {
887
- None => Some(str),
888
- Some(prev) => Some(prev ++ separator ++ str),
889
- }
890
- }
891
- match (reduce(iter, None, items)) {
892
- None => "",
893
- Some(s) => s,
968
+ @unsafe
969
+ provide let join = (separator: String, items: Array<String>) => {
970
+ use WasmI32.{ (+), (==) }
971
+ use DataStructures.{ allocateString, stringSize }
972
+ let arrLen = length(items)
973
+ let sepPtr = WasmI32.fromGrain(separator)
974
+ let sepSize = stringSize(sepPtr)
975
+ let sepPtr = sepPtr + 8n
976
+ let mut strSize = 0n
977
+ for (let mut i = 0; i < arrLen; i = incr(i)) {
978
+ strSize += stringSize(WasmI32.fromGrain(items[i]))
979
+ if (i != 0) strSize += sepSize
894
980
  }
895
- }
896
-
897
- /**
898
- * A simple helper function to convert a negative array index
899
- * number to its positive positional equivalent.
900
- */
901
- let wrapNegativeIndex = (arrLen, idx) => {
902
- if (idx >= 0) {
903
- idx
904
- } else {
905
- arrLen + idx
981
+ let newString = allocateString(strSize)
982
+ let mut offset = newString + 8n
983
+ for (let mut i = 0; i < arrLen; i = incr(i)) {
984
+ let ptr = WasmI32.fromGrain(items[i])
985
+ let size = stringSize(ptr)
986
+ Memory.copy(offset, ptr + 8n, size)
987
+ offset += size
988
+ if (i != arrLen) Memory.copy(offset, sepPtr, sepSize)
989
+ offset += sepSize
906
990
  }
991
+ WasmI32.toGrain(newString): String
907
992
  }
908
993
 
909
994
  /**
@@ -913,17 +998,21 @@ let wrapNegativeIndex = (arrLen, idx) => {
913
998
  * If either index is a negative number, it will be treated as a reverse index from
914
999
  * the end of the array. e.g. `slice(1, -1, [> 'a', 'b', 'c']) == [> 'b']`.
915
1000
  *
916
- * @param startIndex: The index of the array where the slice will begin (inclusive)
917
- * @param endIndex: The index of the array where the slice will end (exclusive)
1001
+ * @param start: The index of the array where the slice will begin (inclusive)
1002
+ * @param end: The index of the array where the slice will end (exclusive)
918
1003
  * @param array: The array to be sliced
919
1004
  * @returns The subset of the array that was sliced
920
1005
  *
1006
+ * @example Array.slice(1, end=3, [> 1, 2, 3, 4]) == [> 2, 3]
1007
+ * @example Array.slice(1, [> 1, 2, 3, 4]) == [> 2, 3, 4]
1008
+ *
921
1009
  * @since v0.4.0
1010
+ * @history v0.6.0: Default `end` to the Array length
922
1011
  */
923
- export let slice = (startIndex, endIndex, array) => {
1012
+ provide let slice = (start, end=length(array), array) => {
924
1013
  let arrayLength = length(array)
925
- let startIndex = wrapNegativeIndex(arrayLength, startIndex)
926
- let endIndex = wrapNegativeIndex(arrayLength, endIndex)
1014
+ let startIndex = wrapNegativeIndex(arrayLength, start)
1015
+ let endIndex = wrapNegativeIndex(arrayLength, end)
927
1016
  // Ensure we aren't working with an `end` value that is too big
928
1017
  let endIndex = if (endIndex > arrayLength) {
929
1018
  arrayLength
@@ -945,16 +1034,23 @@ export let slice = (startIndex, endIndex, array) => {
945
1034
  * Sorts an array in-place.
946
1035
  *
947
1036
  * Ordering is calculated using a comparator function which takes two array elements and must return 0 if both are equal, a positive number if the first is greater, and a negative number if the first is smaller.
948
- * @param comp: The comparator function used to indicate sort order
1037
+ * @param compare: The comparator function used to indicate sort order
949
1038
  * @param array: The array to be sorted
1039
+ *
1040
+ * @example
1041
+ * let arr = [> 3, 2, 4, 1]
1042
+ * Array.sort(compare=(a, b) => a - b, arr)
1043
+ * assert arr == [> 1, 2, 3, 4]
1044
+ *
950
1045
  * @since v0.4.5
1046
+ * @history v0.6.0: Made `compare` a default argument
951
1047
  */
952
- export let sort = (comp, array) => {
1048
+ provide let sort = (compare=compare, array) => {
953
1049
  let partition = (low, high) => {
954
1050
  let pivot = array[high]
955
1051
  let mut i = low - 1
956
1052
  for (let mut j = low; j < high; j += 1) {
957
- if (comp(array[j], pivot) < 0) {
1053
+ if (compare(array[j], pivot) < 0) {
958
1054
  i += 1
959
1055
  let temp = array[i]
960
1056
  array[i] = array[j]
@@ -978,19 +1074,28 @@ export let sort = (comp, array) => {
978
1074
  }
979
1075
 
980
1076
  /**
981
- * Rotates array elements by the specified amount to the right, in place.
1077
+ * Rotates array elements in place by the specified amount to the left, such
1078
+ * that the `n`th element becomes the first in the array.
982
1079
  *
983
1080
  * If value is negative, array elements will be rotated by the
984
- * specified amount to the left. See examples.
1081
+ * specified amount to the right. See examples.
985
1082
  *
986
1083
  * @param n: The number of elements to rotate by
987
1084
  * @param arr: The array to be rotated
988
1085
  *
989
- * @example let array = [> 1, 2, 3, 4, 5]; rotate(2, arr); arr == [> 4, 5, 1, 2, 3]
990
- * @example let array = [> 1, 2, 3, 4, 5]; rotate(-1, arr); arr == [> 2, 3, 4, 5, 1]
1086
+ * @example
1087
+ * let array = [> 1, 2, 3, 4, 5]
1088
+ * Array.rotate(2, array)
1089
+ * assert array == [> 3, 4, 5, 1, 2]
1090
+ * @example
1091
+ * let array = [> 1, 2, 3, 4, 5]
1092
+ * Array.rotate(-1, array)
1093
+ * assert array == [> 5, 1, 2, 3, 4]
1094
+ *
991
1095
  * @since v0.4.5
1096
+ * @history v0.6.0: Behavior changed from right-rotation to left-rotation
992
1097
  */
993
- export let rotate = (n, arr) => {
1098
+ provide let rotate = (n, arr) => {
994
1099
  let rec gcd = (a, b) => {
995
1100
  if (b == 0) {
996
1101
  a
@@ -1002,13 +1107,12 @@ export let rotate = (n, arr) => {
1002
1107
  let arrLen = length(arr)
1003
1108
  if (arrLen > 0) {
1004
1109
  let k = n % arrLen
1005
- let mut d = -1
1006
1110
  let mut j = 0
1007
1111
  for (let mut i = 0; i < gcd(arrLen, k); i += 1) {
1008
1112
  j = i
1009
1113
  let temp = arr[i]
1010
1114
  while (true) {
1011
- d = (j - k) % arrLen
1115
+ let d = (j + k) % arrLen
1012
1116
  if (d == i) {
1013
1117
  break
1014
1118
  }
@@ -1020,3 +1124,1212 @@ export let rotate = (n, arr) => {
1020
1124
  }
1021
1125
  }
1022
1126
  }
1127
+
1128
+ let rec chunkHelp = (chunkSize, arr, arrLen, chunkStartIndex) => {
1129
+ if (arrLen == 0) {
1130
+ []
1131
+ } else if (arrLen < chunkSize) {
1132
+ [slice(chunkStartIndex, arr)]
1133
+ } else {
1134
+ // create the first chunk of the given array
1135
+ let firstChunk = slice(
1136
+ chunkStartIndex,
1137
+ end=chunkStartIndex + chunkSize,
1138
+ arr
1139
+ )
1140
+ let restChunks = chunkHelp(
1141
+ chunkSize,
1142
+ arr,
1143
+ arrLen - chunkSize,
1144
+ chunkStartIndex + chunkSize
1145
+ )
1146
+ [firstChunk, ...restChunks]
1147
+ }
1148
+ }
1149
+
1150
+ /**
1151
+ * Splits the given array into chunks of the provided size.
1152
+ * If the array cannot be split evenly, the final chunk will contain the remaining elements.
1153
+ *
1154
+ * @param chunkSize: The maximum size of each chunk
1155
+ * @param arr: The array to chunk
1156
+ * @returns An array of chunks
1157
+ *
1158
+ * @throws InvalidArgument(String): When `chunkSize` is not an integer
1159
+ * @throws InvalidArgument(String): When `chunkSize` is less than one
1160
+ *
1161
+ * @example Array.chunk(2, [> 1, 2, 3, 4, 5]) == [> [> 1, 2], [> 3, 4], [> 5]]
1162
+ * @example Array.chunk(2, [> 1, 2, 3, 4]) == [> [> 1, 2], [> 3, 4]]
1163
+ *
1164
+ * @since v0.6.0
1165
+ */
1166
+ provide let chunk = (chunkSize, arr) => {
1167
+ if (chunkSize <= 0) {
1168
+ throw Exception.InvalidArgument("chunkSize must be greater than 0")
1169
+ } else {
1170
+ checkLength(chunkSize)
1171
+ let arrLen = length(arr)
1172
+ let chunks = chunkHelp(chunkSize, arr, arrLen, 0)
1173
+ fromList(chunks)
1174
+ }
1175
+ }
1176
+
1177
+ /**
1178
+ * An immutable array implementation.
1179
+ *
1180
+ * @example use Array.{ module Immutable }
1181
+ * @example Array.Immutable.empty
1182
+ * @example Immutable.fromList([1, 2, 3, 4, 5])
1183
+ *
1184
+ * @since v0.6.0
1185
+ * @history v0.5.4: Originally in `"immutablearray"` module
1186
+ */
1187
+ provide module Immutable {
1188
+ // Immutable arrays implemented as relaxed radix balanced trees. This data
1189
+ // structure allows access and updating in O(log(n)) time, but since the tree
1190
+ // branching factor is chosen to be a large number (32), these operations run
1191
+ // in effectively constant time in most practical situations.
1192
+
1193
+ // This implementation was adapted from Elm's Array module
1194
+ // https://github.com/elm/core/blob/master/src/Array.elm
1195
+ // license of software used:
1196
+
1197
+ // Copyright 2014-present Evan Czaplicki
1198
+ //
1199
+ // Redistribution and use in source and binary forms, with or without
1200
+ // modification, are permitted provided that the following conditions are met:
1201
+ //
1202
+ // 1. Redistributions of source code must retain the above copyright notice,
1203
+ // this list of conditions and the following disclaimer.
1204
+ //
1205
+ // 2. Redistributions in binary form must reproduce the above copyright notice,
1206
+ // this list of conditions and the following disclaimer in the documentation
1207
+ // and/or other materials provided with the distribution.
1208
+ //
1209
+ // 3. Neither the name of the copyright holder nor the names of its
1210
+ // contributors may be used to endorse or promote products derived from this
1211
+ // software without specific prior written permission.
1212
+ //
1213
+ // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
1214
+ // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1215
+ // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1216
+ // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
1217
+ // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
1218
+ // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
1219
+ // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
1220
+ // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
1221
+ // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
1222
+ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
1223
+ // POSSIBILITY OF SUCH DAMAGE.
1224
+
1225
+ // Maximum number of children each tree node can have;
1226
+ // an arbitrary multiple of 2 that gives a good performance tradeoff
1227
+ let branchingFactor = 32
1228
+ // 32 = 2^5
1229
+ let branchingBits = 5
1230
+ // To be applied to numbers to bring them within the range [0, 32)
1231
+ let bitmask = branchingFactor - 1
1232
+
1233
+ type rec Tree<a> = Array<Node<a>>
1234
+ and enum Node<a> {
1235
+ Tree(Tree<a>),
1236
+ Leaf(Array<a>),
1237
+ }
1238
+
1239
+ // a helper data structure used for building an array piece by piece
1240
+ record Builder<a> {
1241
+ btail: Array<a>,
1242
+ nodes: List<Node<a>>,
1243
+ numNodes: Number,
1244
+ }
1245
+
1246
+ // A "tail" of < 32 values at the end of the array is kept as a performance
1247
+ // optimization
1248
+ abstract record ImmutableArray<a> {
1249
+ length: Number,
1250
+ shift: Number,
1251
+ root: Tree<a>,
1252
+ tail: Array<a>,
1253
+ }
1254
+
1255
+ // Aliasing names to make references to mutable arrays within the Immutable
1256
+ // submodule clearer to distinguish
1257
+ let mutLength = length
1258
+ let mutInit = init
1259
+ let mutForEach = forEach
1260
+ let mutMap = map
1261
+ let mutReduce = reduce
1262
+ let mutReduceRight = reduceRight
1263
+ let mutFromList = fromList
1264
+ let mutCopy = copy
1265
+ let mutSlice = slice
1266
+ let mutAppend = append
1267
+
1268
+ /**
1269
+ * An empty array.
1270
+ *
1271
+ * @example Array.Immutable.empty == Array.Immutable.fromList([])
1272
+ *
1273
+ * @since v0.6.0
1274
+ * @history v0.5.4: Originally in `"immutablearray"` module
1275
+ */
1276
+ provide let empty = {
1277
+ let empty = { length: 0, shift: branchingBits, root: [>], tail: [>] }
1278
+ empty
1279
+ }
1280
+
1281
+ /**
1282
+ * Determines if the array contains no elements.
1283
+ *
1284
+ * @param array: The array to check
1285
+ * @returns `true` if the array is empty and `false` otherwise
1286
+ *
1287
+ * @example
1288
+ * use Array.{ module Immutable }
1289
+ * assert Immutable.isEmpty(Immutable.fromList([1, 2, 3])) == false
1290
+ * @example
1291
+ * use Array.{ module Immutable }
1292
+ * assert Immutable.isEmpty(Immutable.fromList([])) == true
1293
+ *
1294
+ * @since v0.6.0
1295
+ * @history v0.5.4: Originally in `"immutablearray"` module
1296
+ */
1297
+ provide let isEmpty = array => array.length == 0
1298
+
1299
+ /**
1300
+ * Provides the length of the input array.
1301
+ *
1302
+ * @param array: The array to inspect
1303
+ * @returns The number of elements in the array
1304
+ *
1305
+ * @example
1306
+ * use Array.{ module Immutable }
1307
+ * assert Immutable.length(Immutable.fromList([1, 2, 3, 4, 5])) == 5
1308
+ *
1309
+ * @since v0.6.0
1310
+ * @history v0.5.4: Originally in `"immutablearray"` module
1311
+ */
1312
+ provide let length = array => array.length
1313
+
1314
+ let tailIndex = length => {
1315
+ let shiftedRight = length >> branchingBits
1316
+ shiftedRight << branchingBits
1317
+ }
1318
+
1319
+ let wrapNegativeIndex = (len, index) => {
1320
+ if (index >= 0) index else len + index
1321
+ }
1322
+
1323
+ let arraySetCopy = (index, value, array) => {
1324
+ let copy = mutCopy(array)
1325
+ copy[index] = value
1326
+ copy
1327
+ }
1328
+
1329
+ // Appends 2 arrays together, truncating to a max size of 32 for storage in
1330
+ // a tree node. Also returns number of elements truncated.
1331
+ let arrayAppendMax32 = (array1, array2) => {
1332
+ let len1 = mutLength(array1)
1333
+ let len2 = mutLength(array2)
1334
+
1335
+ let numToAppend = Number.min(len2, branchingFactor - len1)
1336
+ let toAppend = if (numToAppend < len2) {
1337
+ mutSlice(0, end=numToAppend, array2)
1338
+ } else {
1339
+ array2
1340
+ }
1341
+ let numNotAppended = len1 + len2 - branchingFactor
1342
+
1343
+ (mutAppend(array1, toAppend), numNotAppended)
1344
+ }
1345
+
1346
+ let log32floor = num => {
1347
+ let mut val = -1
1348
+ let mut num = num
1349
+ while (num > 0) {
1350
+ val += 1
1351
+ num = num >> branchingBits
1352
+ }
1353
+ val
1354
+ }
1355
+
1356
+ /**
1357
+ * Retrieves the element from the array at the specified index.
1358
+ * A negative index is treated as an offset from the end of the array.
1359
+ *
1360
+ * @param index: The index to access
1361
+ * @param array: The array to access
1362
+ * @returns The element from the array
1363
+ *
1364
+ * @throws IndexOutOfBounds: When the index being accessed is outside the array's bounds
1365
+ *
1366
+ * @example
1367
+ * use Array.{ module Immutable }
1368
+ * assert Immutable.get(1, Immutable.fromList([1, 2, 3, 4])) == 2
1369
+ * @example
1370
+ * use Array.{ module Immutable }
1371
+ * assert Immutable.get(-1, Immutable.fromList([1, 2, 3, 4])) == 4
1372
+ *
1373
+ * @since v0.6.0
1374
+ * @history v0.5.4: Originally in `"immutablearray"` module
1375
+ */
1376
+ provide let get = (index, array) => {
1377
+ let index = wrapNegativeIndex(array.length, index)
1378
+
1379
+ let rec getInner = (shift, node) => {
1380
+ let pos = index >> shift & bitmask
1381
+ match (node[pos]) {
1382
+ Tree(subTree) => getInner(shift - branchingBits, subTree),
1383
+ Leaf(vals) => vals[index & bitmask],
1384
+ }
1385
+ }
1386
+
1387
+ let { length, shift, root, tail } = array
1388
+ if (index < 0 || index >= length) {
1389
+ throw IndexOutOfBounds
1390
+ } else if (index >= tailIndex(length)) {
1391
+ tail[index & bitmask]
1392
+ } else {
1393
+ getInner(shift, root)
1394
+ }
1395
+ }
1396
+
1397
+ /**
1398
+ * Creates a new array in which the element at the specified index is set to a
1399
+ * new value. A negative index is treated as an offset from the end of the array.
1400
+ *
1401
+ * @param index: The index to update
1402
+ * @param value: The value to store
1403
+ * @param array: The array to update
1404
+ * @returns A new array containing the new element at the given index
1405
+ *
1406
+ * @throws IndexOutOfBounds: When the index being updated is outside the array's bounds
1407
+ *
1408
+ * @example
1409
+ * use Array.{ module Immutable }
1410
+ * let arr = Immutable.fromList([1, 2, 3, 4, 5])
1411
+ * let array = Immutable.set(1, 9, arr)
1412
+ * assert arr == Immutable.fromList([1, 2, 3, 4, 5])
1413
+ * assert array == Immutable.fromList([1, 9, 3, 4, 5])
1414
+ *
1415
+ * @since v0.6.0
1416
+ * @history v0.5.4: Originally in `"immutablearray"` module
1417
+ */
1418
+ provide let set = (index, value, array) => {
1419
+ let index = wrapNegativeIndex(array.length, index)
1420
+
1421
+ let rec setInner = (shift, node) => {
1422
+ let pos = index >> shift & bitmask
1423
+ let newVal = match (node[pos]) {
1424
+ Tree(subTree) => {
1425
+ Tree(setInner(shift - branchingBits, subTree))
1426
+ },
1427
+ Leaf(vals) => {
1428
+ Leaf(arraySetCopy(index & bitmask, value, vals))
1429
+ },
1430
+ }
1431
+ arraySetCopy(pos, newVal, node)
1432
+ }
1433
+
1434
+ let { length, shift, root, tail } = array
1435
+ if (index < 0 || index >= length) {
1436
+ throw IndexOutOfBounds
1437
+ } else if (index >= tailIndex(length)) {
1438
+ { length, shift, root, tail: arraySetCopy(index & bitmask, value, tail) }
1439
+ } else {
1440
+ { length, shift, root: setInner(shift, root), tail }
1441
+ }
1442
+ }
1443
+
1444
+ // Inserts a new tail into the array. If the length of the tail equals the
1445
+ // branching factor, it is instead inserted into the main tree rather than
1446
+ // the tail
1447
+ let replaceTail = (newTail, array) => {
1448
+ let rec insertTailInTree = (shift, node) => {
1449
+ let pos = array.length >> shift & bitmask
1450
+ if (pos >= mutLength(node)) {
1451
+ let newElem = if (shift == branchingBits) {
1452
+ Leaf(newTail)
1453
+ } else {
1454
+ Tree(insertTailInTree(shift - branchingBits, [>]))
1455
+ }
1456
+ mutAppend(node, [> newElem])
1457
+ } else {
1458
+ let newSubTree = match (node[pos]) {
1459
+ Tree(subTree) => subTree,
1460
+ Leaf(_) => [> node[pos]],
1461
+ }
1462
+ let newNode = Tree(insertTailInTree(shift - branchingBits, newSubTree))
1463
+ arraySetCopy(pos, newNode, node)
1464
+ }
1465
+ }
1466
+
1467
+ let { length, shift, root, tail } = array
1468
+ let newArrayLen = length + (mutLength(newTail) - mutLength(tail))
1469
+ if (mutLength(newTail) == branchingFactor) {
1470
+ let overflow = newArrayLen >> branchingBits > 1 << shift
1471
+ if (overflow) {
1472
+ let newShift = shift + branchingBits
1473
+ let newRoot = insertTailInTree(newShift, [> Tree(root)])
1474
+ { length: newArrayLen, shift: newShift, root: newRoot, tail: [>] }
1475
+ } else {
1476
+ let newRoot = insertTailInTree(shift, root)
1477
+ { length: newArrayLen, shift, root: newRoot, tail: [>] }
1478
+ }
1479
+ } else {
1480
+ { length: newArrayLen, shift, root, tail: newTail }
1481
+ }
1482
+ }
1483
+
1484
+ let appendTree = (toAppend, array) => {
1485
+ let (appended, numNotAppended) = arrayAppendMax32(array.tail, toAppend)
1486
+ let newArray = replaceTail(appended, array)
1487
+ if (numNotAppended > 0) {
1488
+ let appendLen = mutLength(toAppend)
1489
+ let newTail = mutSlice(
1490
+ appendLen - numNotAppended,
1491
+ end=appendLen,
1492
+ toAppend
1493
+ )
1494
+ replaceTail(newTail, newArray)
1495
+ } else {
1496
+ newArray
1497
+ }
1498
+ }
1499
+
1500
+ // Flatten an array into a builder
1501
+ let arrayToBuilder = array => {
1502
+ let rec reduceFn = (acc, node) => {
1503
+ match (node) {
1504
+ Tree(subTree) => mutReduce(reduceFn, acc, subTree),
1505
+ Leaf(_) => [node, ...acc],
1506
+ }
1507
+ }
1508
+ let { tail, root, length, _ } = array
1509
+ {
1510
+ btail: tail,
1511
+ nodes: mutReduce(reduceFn, [], root),
1512
+ numNodes: length >> branchingBits,
1513
+ }
1514
+ }
1515
+
1516
+ // For use to compress a large (> 32) list of nodes into subtrees
1517
+ let rec compressNodes = (nodes, acc) => {
1518
+ let node = mutFromList(List.take(branchingFactor, nodes))
1519
+ let remaining = List.drop(branchingFactor, nodes)
1520
+ let newAcc = [Tree(node), ...acc]
1521
+ match (remaining) {
1522
+ [] => List.reverse(newAcc),
1523
+ _ => compressNodes(remaining, newAcc),
1524
+ }
1525
+ }
1526
+
1527
+ let builderToArray = builder => {
1528
+ // Builds the non-tail portion of an array
1529
+ let rec buildTree = (nodes, numNodes) => {
1530
+ let newNodeSize = Number.ceil(numNodes / branchingFactor)
1531
+ match (newNodeSize) {
1532
+ 1 => mutFromList(nodes),
1533
+ _ => buildTree(compressNodes(nodes, []), newNodeSize),
1534
+ }
1535
+ }
1536
+
1537
+ let { btail, nodes, numNodes } = builder
1538
+ match (numNodes) {
1539
+ 0 =>
1540
+ {
1541
+ length: mutLength(btail),
1542
+ shift: branchingBits,
1543
+ root: [>],
1544
+ tail: btail,
1545
+ },
1546
+ _ => {
1547
+ let treeSize = numNodes * branchingFactor
1548
+ let depth = log32floor(treeSize - 1)
1549
+ {
1550
+ length: treeSize + mutLength(btail),
1551
+ shift: Number.max(1, depth) * branchingBits,
1552
+ root: buildTree(nodes, numNodes),
1553
+ tail: btail,
1554
+ }
1555
+ },
1556
+ }
1557
+ }
1558
+
1559
+ // Append a chunk of <= 32 values to a builder
1560
+ let appendBuilder = (toAppend, builder) => {
1561
+ let { btail, nodes, numNodes } = builder
1562
+ let (appended, numNotAppended) = arrayAppendMax32(btail, toAppend)
1563
+
1564
+ if (numNotAppended >= 0) {
1565
+ let appendLen = mutLength(toAppend)
1566
+ {
1567
+ btail: mutSlice(appendLen - numNotAppended, end=appendLen, toAppend),
1568
+ nodes: [Leaf(appended), ...nodes],
1569
+ numNodes: numNodes + 1,
1570
+ }
1571
+ } else {
1572
+ { btail: appended, nodes, numNodes }
1573
+ }
1574
+ }
1575
+
1576
+ /**
1577
+ * Creates a new array with the elements of the first array followed by
1578
+ * the elements of the second array.
1579
+ *
1580
+ * @param array1: The array containing elements to appear first
1581
+ * @param array2: The array containing elements to appear second
1582
+ * @returns The new array containing elements from `array1` followed by elements from `array2`
1583
+ *
1584
+ * @example
1585
+ * use Array.{ module Immutable }
1586
+ * let arr1 = Immutable.fromList([1, 2])
1587
+ * let arr2 = Immutable.fromList([3, 4, 5])
1588
+ * assert Immutable.append(arr1, arr2) == Immutable.fromList([1, 2, 3, 4, 5])
1589
+ * assert arr1 == Immutable.fromList([1, 2])
1590
+ * assert arr2 == Immutable.fromList([3, 4, 5])
1591
+ *
1592
+ * @since v0.6.0
1593
+ * @history v0.5.4: Originally in `"immutablearray"` module
1594
+ */
1595
+ provide let append = (array1, array2) => {
1596
+ // Magic number of 4 determined best from benchmarks according to Elm's
1597
+ // Array implementation
1598
+ if (array2.length <= branchingFactor * 4) {
1599
+ let rec reduceFn = (array, node) => {
1600
+ match (node) {
1601
+ Tree(subTree) => mutReduce(reduceFn, array, subTree),
1602
+ Leaf(vals) => appendTree(vals, array),
1603
+ }
1604
+ }
1605
+ let withoutTail = mutReduce(reduceFn, array1, array2.root)
1606
+ appendTree(array2.tail, withoutTail)
1607
+ } else {
1608
+ let rec reduceFn = (builder, node) => {
1609
+ match (node) {
1610
+ Tree(subTree) => mutReduce(reduceFn, builder, subTree),
1611
+ Leaf(vals) => appendBuilder(vals, builder),
1612
+ }
1613
+ }
1614
+ let withoutTail = mutReduce(reduceFn, arrayToBuilder(array1), array2.root)
1615
+ let { btail, nodes, numNodes } = appendBuilder(array2.tail, withoutTail)
1616
+ builderToArray({ btail, nodes: List.reverse(nodes), numNodes })
1617
+ }
1618
+ }
1619
+
1620
+ /**
1621
+ * Creates a single array containing the elements of all arrays in the
1622
+ * provided list.
1623
+ *
1624
+ * @param arrays: A list containing all arrays to combine
1625
+ * @returns The new array
1626
+ *
1627
+ * @example
1628
+ * use Array.{ module Immutable }
1629
+ * let arr1 = Immutable.fromList([1, 2])
1630
+ * let arr2 = Immutable.fromList([3, 4])
1631
+ * let arr3 = Immutable.fromList([5, 6])
1632
+ * assert Immutable.concat([arr1, arr2, arr3]) == Immutable.fromList([1, 2, 3, 4, 5, 6])
1633
+ *
1634
+ * @since v0.6.0
1635
+ * @history v0.5.4: Originally in `"immutablearray"` module
1636
+ */
1637
+ provide let concat = arrays => {
1638
+ List.reduce(append, empty, arrays)
1639
+ }
1640
+
1641
+ /**
1642
+ * Creates a new array of the specified length where each element is
1643
+ * initialized with the result of an initializer function. The initializer
1644
+ * is called with the index of each array element.
1645
+ *
1646
+ * @param length: The length of the new array
1647
+ * @param fn: The initializer function to call with each index, where the value returned will be used to initialize the element
1648
+ * @returns The new array
1649
+ *
1650
+ * @example
1651
+ * use Array.{ module Immutable }
1652
+ * assert Immutable.init(5, i => i) == Immutable.fromList([0, 1, 2, 3, 4])
1653
+ * @example
1654
+ * use Array.{ module Immutable }
1655
+ * assert Immutable.init(5, i => i + 3) == Immutable.fromList([3, 4, 5, 6, 7])
1656
+ *
1657
+ * @since v0.6.0
1658
+ * @history v0.5.4: Originally in `"immutablearray"` module
1659
+ */
1660
+ provide let init = (length, fn) => {
1661
+ let tailLen = length % branchingFactor
1662
+ let btail = mutInit(tailLen, i => fn(length - tailLen + i))
1663
+
1664
+ let rec initInner = (beginI, nodes) => {
1665
+ if (beginI < 0) {
1666
+ builderToArray({ btail, nodes, numNodes: length >> branchingBits })
1667
+ } else {
1668
+ let leaf = Leaf(mutInit(branchingFactor, i => fn(beginI + i)))
1669
+ initInner(beginI - branchingFactor, [leaf, ...nodes])
1670
+ }
1671
+ }
1672
+ initInner(length - tailLen - branchingFactor, [])
1673
+ }
1674
+
1675
+ /**
1676
+ * Creates a new array of the specified length with each element being
1677
+ * initialized with the given value.
1678
+ *
1679
+ * @param length: The length of the new array
1680
+ * @param value: The value to store at each index
1681
+ * @returns The new array
1682
+ *
1683
+ * @example
1684
+ * use Array.{ module Immutable }
1685
+ * assert Immutable.make(5, "🌾") == Immutable.fromList(["🌾", "🌾", "🌾", "🌾", "🌾"])
1686
+ *
1687
+ * @since v0.6.0
1688
+ * @history v0.5.4: Originally in `"immutablearray"` module
1689
+ */
1690
+ provide let make = (length, value) => {
1691
+ init(length, (_) => value)
1692
+ }
1693
+
1694
+ /**
1695
+ * Iterates an array, calling an iterator function on each element.
1696
+ *
1697
+ * @param fn: The iterator function to call with each element
1698
+ * @param array: The array to iterate
1699
+ *
1700
+ * @example
1701
+ * use Array.{ module Immutable }
1702
+ * let arr = Immutable.fromList(["foo", "bar", "baz"])
1703
+ * let mut str = ""
1704
+ * Immutable.forEach(e => str = str ++ e, arr)
1705
+ * assert str == "foobarbaz"
1706
+ *
1707
+ * @since v0.6.0
1708
+ * @history v0.5.4: Originally in `"immutablearray"` module
1709
+ */
1710
+ provide let forEach = (fn, array) => {
1711
+ let rec forEachFn = node => {
1712
+ match (node) {
1713
+ Tree(subTree) => mutForEach(forEachFn, subTree),
1714
+ Leaf(vals) => mutForEach(fn, vals),
1715
+ }
1716
+ }
1717
+ let { tail, root, _ } = array
1718
+ mutForEach(forEachFn, root)
1719
+ mutForEach(fn, tail)
1720
+ }
1721
+
1722
+ /**
1723
+ * Iterates an array a given number of times, calling an iterator function on each element.
1724
+ *
1725
+ * @param fn: The iterator function to call with each element
1726
+ * @param n: The number of times to iterate the given array
1727
+ * @param array: The array to iterate
1728
+ *
1729
+ * @example
1730
+ * use Array.{ module Immutable }
1731
+ * let arr = Immutable.fromList(["a", "b", "c"])
1732
+ * let mut str = ""
1733
+ * Immutable.cycle(e => str = str ++ e, 2, arr)
1734
+ * assert str == "abcabc"
1735
+ *
1736
+ * @since v0.6.0
1737
+ * @history v0.5.4: Originally in `"immutablearray"` module
1738
+ */
1739
+ provide let cycle = (fn, n, array) => {
1740
+ for (let mut i = 0; i < n; i += 1) {
1741
+ forEach(fn, array)
1742
+ }
1743
+ }
1744
+
1745
+ /**
1746
+ * Produces a new array initialized with the results of a mapper function
1747
+ * called on each element of the input array.
1748
+ *
1749
+ * @param fn: The mapper function to call on each element, where the value returned will be used to initialize the element in the new array
1750
+ * @param array: The array to iterate
1751
+ * @returns The new array with mapped values
1752
+ *
1753
+ * @example
1754
+ * use Array.{ module Immutable }
1755
+ * let arr = Immutable.fromList(["foo", "bar", "baz"])
1756
+ * let arr = Immutable.map(e => e ++ "_", arr)
1757
+ * assert arr == Immutable.fromList(["foo_", "bar_", "baz_"])
1758
+ *
1759
+ * @since v0.6.0
1760
+ * @history v0.5.4: Originally in `"immutablearray"` module
1761
+ */
1762
+ provide let map = (fn, array) => {
1763
+ let rec mapFn = node => {
1764
+ match (node) {
1765
+ Tree(subTree) => Tree(mutMap(mapFn, subTree)),
1766
+ Leaf(vals) => Leaf(mutMap(fn, vals)),
1767
+ }
1768
+ }
1769
+ let { length, shift, root, tail } = array
1770
+ let newRoot = mutMap(mapFn, root)
1771
+ let newTail = mutMap(fn, tail)
1772
+ { length, shift, root: newRoot, tail: newTail }
1773
+ }
1774
+
1775
+ /**
1776
+ * Combines all elements of an array using a reducer function,
1777
+ * starting from the "head", or left side, of the array.
1778
+ *
1779
+ * In `ImmutableArray.reduce(fn, initial, array)`, `fn` is called with
1780
+ * an accumulator and each element of the array, and returns
1781
+ * a new accumulator. The final value is the last accumulator
1782
+ * returned. The accumulator starts with value `initial`.
1783
+ *
1784
+ * @param fn: The reducer function to call on each element, where the value returned will be the next accumulator value
1785
+ * @param initial: The initial value to use for the accumulator on the first iteration
1786
+ * @param array: The array to iterate
1787
+ * @returns The final accumulator returned from `fn`
1788
+ *
1789
+ * @example
1790
+ * use Array.{ module Immutable }
1791
+ * let arr = Immutable.fromList([1, 2, 3])
1792
+ * assert Immutable.reduce((acc, x) => acc + x, 0, arr) == 6
1793
+ *
1794
+ * @since v0.6.0
1795
+ * @history v0.5.4: Originally in `"immutablearray"` module
1796
+ */
1797
+ provide let reduce = (fn, initial, array) => {
1798
+ let rec reduceFn = (acc, node) => {
1799
+ match (node) {
1800
+ Tree(subTree) => mutReduce(reduceFn, acc, subTree),
1801
+ Leaf(vals) => mutReduce(fn, acc, vals),
1802
+ }
1803
+ }
1804
+ let { tail, root, _ } = array
1805
+ let withoutTail = mutReduce(reduceFn, initial, root)
1806
+ mutReduce(fn, withoutTail, tail)
1807
+ }
1808
+
1809
+ /**
1810
+ * Combines all elements of an array using a reducer function,
1811
+ * starting from the "end", or right side, of the array.
1812
+ *
1813
+ * In `ImmutableArray.reduceRight(fn, initial, array)`, `fn` is called with
1814
+ * each element of the array and an accumulator, and returns
1815
+ * a new accumulator. The final value is the last accumulator
1816
+ * returned. The accumulator starts with value `initial`.
1817
+ *
1818
+ * @param fn: The reducer function to call on each element, where the value returned will be the next accumulator value
1819
+ * @param initial: The initial value to use for the accumulator on the first iteration
1820
+ * @param array: The array to iterate
1821
+ * @returns The final accumulator returned from `fn`
1822
+ *
1823
+ * @example
1824
+ * use Array.{ module Immutable }
1825
+ * let arr = Immutable.fromList(["baz", "bar", "foo"])
1826
+ * assert Immutable.reduceRight((x, acc) => acc ++ x, "", arr) == "foobarbaz"
1827
+ *
1828
+ * @since v0.6.0
1829
+ * @history v0.5.4: Originally in `"immutablearray"` module
1830
+ */
1831
+ provide let reduceRight = (fn, initial, array) => {
1832
+ let rec reduceFn = (node, acc) => {
1833
+ match (node) {
1834
+ Tree(subTree) => mutReduceRight(reduceFn, acc, subTree),
1835
+ Leaf(vals) => mutReduceRight(fn, acc, vals),
1836
+ }
1837
+ }
1838
+ let { tail, root, _ } = array
1839
+ let tailVal = mutReduceRight(fn, initial, tail)
1840
+ mutReduceRight(reduceFn, tailVal, root)
1841
+ }
1842
+
1843
+ /**
1844
+ * Produces a new array by calling a function on each element
1845
+ * of the input array. Each iteration produces an intermediate
1846
+ * array, which are all appended to produce a "flattened" array
1847
+ * of all results.
1848
+ *
1849
+ * @param fn: The function to be called on each element, where the value returned will be an array that gets appended to the new array
1850
+ * @param array: The array to iterate
1851
+ * @returns The new array
1852
+ *
1853
+ * @example
1854
+ * use Array.{ module Immutable }
1855
+ * let arr = Immutable.fromList([1, 3, 5])
1856
+ * let arr = Immutable.flatMap(n => Immutable.fromList([n, n + 1]), arr)
1857
+ * assert arr == Immutable.fromList([1, 2, 3, 4, 5, 6])
1858
+ *
1859
+ * @since v0.6.0
1860
+ * @history v0.5.4: Originally in `"immutablearray"` module
1861
+ */
1862
+ provide let flatMap = (fn, array) => {
1863
+ reduce((acc, x) => append(acc, fn(x)), empty, array)
1864
+ }
1865
+
1866
+ /**
1867
+ * Converts the input list to an array.
1868
+ *
1869
+ * @param list: The list to convert
1870
+ * @returns The array containing all elements from the list
1871
+ *
1872
+ * @example
1873
+ * use Array.{ module Immutable }
1874
+ * let arr = Immutable.fromList([1, 2, 3])
1875
+ * assert Immutable.get(1, arr) == 2
1876
+ *
1877
+ * @since v0.6.0
1878
+ * @history v0.5.4: Originally in `"immutablearray"` module
1879
+ */
1880
+ provide let fromList = list => {
1881
+ let rec fromListInner = (list, nodes, numNodes) => {
1882
+ let node = mutFromList(List.take(branchingFactor, list))
1883
+ let remaining = List.drop(branchingFactor, list)
1884
+ if (mutLength(node) < branchingFactor) {
1885
+ builderToArray({ btail: node, nodes: List.reverse(nodes), numNodes })
1886
+ } else {
1887
+ fromListInner(remaining, [Leaf(node), ...nodes], numNodes + 1)
1888
+ }
1889
+ }
1890
+
1891
+ fromListInner(list, [], 0)
1892
+ }
1893
+
1894
+ /**
1895
+ * Converts the input array to a list.
1896
+ *
1897
+ * @param array: The array to convert
1898
+ * @returns The list containing all elements from the array
1899
+ *
1900
+ * @example
1901
+ * use Array.{ module Immutable }
1902
+ * let arr = Immutable.fromList(['a', 'b', 'c'])
1903
+ * let arr = Immutable.set(0, 'd', arr)
1904
+ * assert Immutable.toList(arr) == ['d', 'b', 'c']
1905
+ *
1906
+ * @since v0.6.0
1907
+ * @history v0.5.4: Originally in `"immutablearray"` module
1908
+ */
1909
+ provide let toList = array => {
1910
+ reduceRight((val, list) => [val, ...list], [], array)
1911
+ }
1912
+
1913
+ /**
1914
+ * Produces a new array by calling a function on each element of
1915
+ * the input array and only including it in the result array if the element satisfies
1916
+ * the condition.
1917
+ *
1918
+ * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
1919
+ * @param array: The array to iterate
1920
+ * @returns The new array containing elements where `fn` returned `true`
1921
+ *
1922
+ * @example
1923
+ * use Array.{ module Immutable }
1924
+ * let arr = Immutable.fromList(['a', 'a', 'b', 'c'])
1925
+ * let arr = Immutable.filter(e => e == 'a', arr)
1926
+ * assert Immutable.toList(arr) == ['a', 'a']
1927
+ *
1928
+ * @since v0.6.0
1929
+ * @history v0.5.4: Originally in `"immutablearray"` module
1930
+ */
1931
+ provide let filter = (fn, array) => {
1932
+ fromList(
1933
+ reduceRight((x, arr) => if (fn(x)) [x, ...arr] else arr, [], array)
1934
+ )
1935
+ }
1936
+
1937
+ /**
1938
+ * Checks that the given condition is satisfied for all
1939
+ * elements in the input array.
1940
+ *
1941
+ * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
1942
+ * @param array: The array to check
1943
+ * @returns `true` if all elements satify the condition or `false` otherwise
1944
+ *
1945
+ * @example
1946
+ * use Array.{ module Immutable }
1947
+ * let arr = Immutable.fromList(['a', 'a'])
1948
+ * assert Immutable.every(e => e == 'a', arr) == true
1949
+ * @example
1950
+ * use Array.{ module Immutable }
1951
+ * let arr = Immutable.fromList(['a', 'a', 'b', 'c'])
1952
+ * assert Immutable.every(e => e == 'a', arr) == false
1953
+ *
1954
+ * @since v0.6.0
1955
+ * @history v0.5.4: Originally in `"immutablearray"` module
1956
+ */
1957
+ provide let every = (fn, array) => {
1958
+ reduce((acc, x) => acc && fn(x), true, array)
1959
+ }
1960
+
1961
+ /**
1962
+ * Checks that the given condition is satisfied **at least
1963
+ * once** by an element in the input array.
1964
+ *
1965
+ * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
1966
+ * @param array: The array to iterate
1967
+ * @returns `true` if one or more elements satisfy the condition or `false` otherwise
1968
+ *
1969
+ * @example
1970
+ * use Array.{ module Immutable }
1971
+ * let arr = Immutable.fromList(['a', 'a', 'b', 'c'])
1972
+ * assert Immutable.every(e => e == 'a', arr) == false
1973
+ * @example
1974
+ * use Array.{ module Immutable }
1975
+ * let arr = Immutable.fromList(['b', 'c'])
1976
+ * assert Immutable.some(e => e == 'a', arr) == false
1977
+ *
1978
+ * @since v0.6.0
1979
+ * @history v0.5.4: Originally in `"immutablearray"` module
1980
+ */
1981
+ provide let some = (fn, array) => {
1982
+ reduce((acc, x) => acc || fn(x), false, array)
1983
+ }
1984
+
1985
+ /**
1986
+ * Creates a new array with all elements in reverse order.
1987
+ *
1988
+ * @param array: The array to reverse
1989
+ * @returns The new array
1990
+ *
1991
+ * @example
1992
+ * use Array.{ module Immutable }
1993
+ * let arr = Immutable.fromList(['a', 'b', 'c'])
1994
+ * let arr = Immutable.reverse(arr)
1995
+ * assert Immutable.toList(arr) == ['c', 'b', 'a']
1996
+ *
1997
+ * @since v0.6.0
1998
+ * @history v0.5.4: Originally in `"immutablearray"` module
1999
+ */
2000
+ provide let reverse = array => {
2001
+ fromList(reduce((acc, x) => [x, ...acc], [], array))
2002
+ }
2003
+
2004
+ /**
2005
+ * Checks if the value is an element of the input array.
2006
+ * Uses the generic `==` structural equality operator.
2007
+ *
2008
+ * @param search: The value to compare
2009
+ * @param array: The array to inspect
2010
+ * @returns `true` if the value exists in the array or `false` otherwise
2011
+ *
2012
+ * @example
2013
+ * use Array.{ module Immutable }
2014
+ * let arr = Immutable.fromList(['a', 'b', 'c'])
2015
+ * assert Immutable.contains('a', arr) == true
2016
+ * @example
2017
+ * use Array.{ module Immutable }
2018
+ * let arr = Immutable.fromList(['b', 'c'])
2019
+ * assert Immutable.contains('a', arr) == false
2020
+ *
2021
+ * @since v0.6.0
2022
+ * @history v0.5.4: Originally in `"immutablearray"` module
2023
+ */
2024
+ provide let contains = (search, array) => {
2025
+ reduce((acc, x) => acc || x == search, false, array)
2026
+ }
2027
+
2028
+ /**
2029
+ * Finds the first element in an array that satisfies the given condition.
2030
+ *
2031
+ * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
2032
+ * @param array: The array to search
2033
+ * @returns `Some(element)` containing the first value found or `None` otherwise
2034
+ *
2035
+ * @example
2036
+ * use Array.{ module Immutable }
2037
+ * let arr = Immutable.fromList([1, 2, 3])
2038
+ * assert Immutable.find(e => e == 2, arr) == Some(2)
2039
+ * @example
2040
+ * use Array.{ module Immutable }
2041
+ * let arr = Immutable.fromList([1, 3])
2042
+ * assert Immutable.find(e => e == 2, arr) == None
2043
+ *
2044
+ * @since v0.6.0
2045
+ * @history v0.5.4: Originally in `"immutablearray"` module
2046
+ */
2047
+ provide let find = (fn, array) => {
2048
+ reduce((acc, x) => if (acc == None && fn(x)) Some(x) else acc, None, array)
2049
+ }
2050
+
2051
+ /**
2052
+ * Finds the first index in an array where the element satisfies the given condition.
2053
+ *
2054
+ * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
2055
+ * @param array: The array to search
2056
+ * @returns `Some(index)` containing the index of the first element found or `None` otherwise
2057
+ *
2058
+ * @example
2059
+ * use Array.{ module Immutable }
2060
+ * let arr = Immutable.fromList([1, 2, 3])
2061
+ * assert Immutable.findIndex(e => e == 2, arr) == Some(1)
2062
+ * @example
2063
+ * use Array.{ module Immutable }
2064
+ * let arr = Immutable.fromList([1, 3])
2065
+ * assert Immutable.findIndex(e => e == 2, arr) == None
2066
+ *
2067
+ * @since v0.6.0
2068
+ * @history v0.5.4: Originally in `"immutablearray"` module
2069
+ */
2070
+ provide let findIndex = (fn, array) => {
2071
+ let mut i = -1
2072
+ reduce((acc, x) => {
2073
+ i += 1
2074
+ if (acc == None && fn(x)) Some(i) else acc
2075
+ }, None, array)
2076
+ }
2077
+
2078
+ /**
2079
+ * Combines two arrays into a Cartesian product of tuples containing
2080
+ * all ordered pairs `(a, b)`.
2081
+ *
2082
+ * @param array1: The array to provide values for the first tuple element
2083
+ * @param array2: The array to provide values for the second tuple element
2084
+ * @returns The new array containing all pairs of `(a, b)`
2085
+ *
2086
+ * @example
2087
+ * use Array.{ module Immutable }
2088
+ * let arr1 = Immutable.fromList([1, 2])
2089
+ * let arr2 = Immutable.fromList([3, 4])
2090
+ * assert Immutable.product(arr1, arr2) == Immutable.fromList([(1, 3), (1, 4), (2, 3), (2, 4)])
2091
+ *
2092
+ * @since v0.6.0
2093
+ * @history v0.5.4: Originally in `"immutablearray"` module
2094
+ */
2095
+ provide let product = (array1, array2) => {
2096
+ fromList(reduceRight((x1, list) => {
2097
+ reduceRight((x2, list) => [(x1, x2), ...list], list, array2)
2098
+ }, [], array1))
2099
+ }
2100
+
2101
+ /**
2102
+ * Counts the number of elements in an array that satisfy the given condition.
2103
+ *
2104
+ * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
2105
+ * @param array: The array to iterate
2106
+ * @returns The total number of elements that satisfy the condition
2107
+ *
2108
+ * @example
2109
+ * use Array.{ module Immutable }
2110
+ * let arr = Immutable.fromList([1, 1, 2, 3, 4])
2111
+ * assert Immutable.count(e => e == 1, arr) == 2
2112
+ *
2113
+ * @since v0.6.0
2114
+ * @history v0.5.4: Originally in `"immutablearray"` module
2115
+ */
2116
+ provide let count = (fn, array) => {
2117
+ reduce((acc, x) => if (fn(x)) acc + 1 else acc, 0, array)
2118
+ }
2119
+
2120
+ /**
2121
+ * Produces a new array with any duplicates removed.
2122
+ * Uses the generic `==` structural equality operator.
2123
+ *
2124
+ * @param array: The array to filter
2125
+ * @returns The new array with only unique values
2126
+ *
2127
+ * @example
2128
+ * use Array.{ module Immutable }
2129
+ * let arr = Immutable.fromList([1, 1, 2, 3, 2, 4])
2130
+ * assert Immutable.unique(arr) == Immutable.fromList([1, 2, 3, 4])
2131
+ *
2132
+ * @since v0.6.0
2133
+ * @history v0.5.4: Originally in `"immutablearray"` module
2134
+ */
2135
+ provide let unique = array => {
2136
+ // TODO(#1651): improve performance
2137
+ fromList(List.unique(toList(array)))
2138
+ }
2139
+
2140
+ /**
2141
+ * Produces a new array filled with tuples of elements from both given arrays.
2142
+ * The first tuple will contain the first item of each array, the second tuple
2143
+ * will contain the second item of each array, and so on.
2144
+ *
2145
+ * Calling this function with arrays of different sizes will cause the returned
2146
+ * array to have the length of the smaller array.
2147
+ *
2148
+ * @param array1: The array to provide values for the first tuple element
2149
+ * @param array2: The array to provide values for the second tuple element
2150
+ * @returns The new array containing indexed pairs of `(a, b)`
2151
+ *
2152
+ * @example
2153
+ * use Array.{ module Immutable }
2154
+ * let arr1 = Immutable.fromList([1, 2, 3])
2155
+ * let arr2 = Immutable.fromList([4, 5, 6])
2156
+ * assert Immutable.zip(arr1, arr2) == Immutable.fromList([(1, 4), (2, 5), (3, 6)])
2157
+ *
2158
+ * @since v0.6.0
2159
+ * @history v0.5.4: Originally in `"immutablearray"` module
2160
+ */
2161
+ provide let zip = (array1, array2) => {
2162
+ fromList(List.zip(toList(array1), toList(array2)))
2163
+ }
2164
+
2165
+ /**
2166
+ * Produces a new array filled with elements defined by applying a function on
2167
+ * pairs from both given arrays. The first element will contain the result of
2168
+ * applying the function to the first elements of each array, the second element
2169
+ * will contain the result of applying the function to the second elements of
2170
+ * each array, and so on.
2171
+ *
2172
+ * Calling this function with arrays of different sizes will cause the returned
2173
+ * array to have the length of the smaller array.
2174
+ *
2175
+ * @param fn: The function to apply to pairs of elements
2176
+ * @param array1: The array whose elements will each be passed to the function as the first argument
2177
+ * @param array2: The array whose elements will each be passed to the function as the second argument
2178
+ * @returns The new array containing elements derived from applying the function to pairs of input array elements
2179
+ *
2180
+ * @example
2181
+ * use Array.{ module Immutable }
2182
+ * let arr1 = Immutable.fromList([1, 2, 3])
2183
+ * let arr2 = Immutable.fromList([4, 5, 6])
2184
+ * assert Immutable.zipWith((a, b) => a + b, arr1, arr2) == Immutable.fromList([5, 7, 9])
2185
+ * @example
2186
+ * use Array.{ module Immutable }
2187
+ * let arr1 = Immutable.fromList([1, 2, 3])
2188
+ * let arr2 = Immutable.fromList([4, 5, 6])
2189
+ * assert Immutable.zipWith((a, b) => a * b, arr1, arr2) == Immutable.fromList([4, 10, 18])
2190
+ *
2191
+ * @since v0.6.0
2192
+ * @history v0.5.4: Originally in `"immutablearray"` module
2193
+ */
2194
+ provide let zipWith = (fn, array1, array2) => {
2195
+ fromList(List.zipWith(fn, toList(array1), toList(array2)))
2196
+ }
2197
+
2198
+ /**
2199
+ * Produces two arrays by splitting apart an array of tuples.
2200
+ *
2201
+ * @param array: The array of tuples to split
2202
+ * @returns An array containing all elements from the first tuple element and an array containing all elements from the second tuple element
2203
+ *
2204
+ * @example
2205
+ * use Array.{ module Immutable }
2206
+ * let arr1 = Immutable.fromList([(1, 2), (3, 4), (5, 6)])
2207
+ * let arr2 = Immutable.fromList([1, 3, 5])
2208
+ * let arr3 = Immutable.fromList([2, 4, 6])
2209
+ * assert Immutable.unzip(arr1) == (arr2, arr3)
2210
+ *
2211
+ * @since v0.6.0
2212
+ * @history v0.5.4: Originally in `"immutablearray"` module
2213
+ */
2214
+ provide let unzip = array => {
2215
+ let (list1, list2) = List.unzip(toList(array))
2216
+ (fromList(list1), fromList(list2))
2217
+ }
2218
+
2219
+ /**
2220
+ * Concatenates an array of strings into a single string, separated by a separator string.
2221
+ *
2222
+ * @param separator: The separator to insert between items in the string
2223
+ * @param array: The input strings
2224
+ * @returns The concatenated string
2225
+ *
2226
+ * @example
2227
+ * use Array.{ module Immutable }
2228
+ * let arr = Immutable.fromList(["a", "b", "c"])
2229
+ * assert Immutable.join(", ", arr) == "a, b, c"
2230
+ *
2231
+ * @since v0.6.0
2232
+ * @history v0.5.4: Originally in `"immutablearray"` module
2233
+ */
2234
+ provide let join = (separator, array) => {
2235
+ // TODO(#728): Improve performance here with buffer approach
2236
+ let iter = (acc, str) => {
2237
+ match (acc) {
2238
+ None => Some(str),
2239
+ Some(prev) => Some(prev ++ separator ++ str),
2240
+ }
2241
+ }
2242
+ match (reduce(iter, None, array)) {
2243
+ None => "",
2244
+ Some(s) => s,
2245
+ }
2246
+ }
2247
+
2248
+ let clampIndex = (len, index) => {
2249
+ Number.min(len, Number.max(0, wrapNegativeIndex(len, index)))
2250
+ }
2251
+
2252
+ /**
2253
+ * Slices an array given zero-based start and end indexes. The value
2254
+ * at the end index will not be included in the result.
2255
+ *
2256
+ * If either index is a negative number, it will be treated as a reverse index from
2257
+ * the end of the array.
2258
+ *
2259
+ * @param start: The index of the array where the slice will begin (inclusive)
2260
+ * @param end: The index of the array where the slice will end (exclusive)
2261
+ * @param array: The array to be sliced
2262
+ * @returns The subset of the array that was sliced
2263
+ *
2264
+ * @example
2265
+ * use Array.{ module Immutable }
2266
+ * let arr = Immutable.fromList(['a', 'b', 'c'])
2267
+ * assert Immutable.slice(0, end=2, arr) == Immutable.fromList(['a', 'b'])
2268
+ * @example
2269
+ * use Array.{ module Immutable }
2270
+ * let arr = Immutable.fromList(['a', 'b', 'c'])
2271
+ * assert Immutable.slice(1, end=-1, arr) == Immutable.fromList(['b'])
2272
+ *
2273
+ * @since v0.6.0
2274
+ * @history v0.5.4: Originally in `"immutablearray"` module
2275
+ * @history v0.6.0: Default `end` to the Array length
2276
+ */
2277
+ provide let slice = (start, end=length(array), array) => {
2278
+ let begin = clampIndex(array.length, start)
2279
+ let end = clampIndex(array.length, end)
2280
+ let mut i = array.length
2281
+ fromList(reduceRight((x, acc) => {
2282
+ i -= 1
2283
+ if (i >= begin && i < end) [x, ...acc] else acc
2284
+ }, [], array))
2285
+ }
2286
+
2287
+ /**
2288
+ * Sorts the given array based on a given comparator function.
2289
+ *
2290
+ * Ordering is calculated using a comparator function which takes two array elements and must return 0 if both are equal, a positive number if the first is greater, and a negative number if the first is smaller.
2291
+ * @param compare: The comparator function used to indicate sort order
2292
+ * @param array: The array to be sorted
2293
+ * @returns The sorted array
2294
+ *
2295
+ * @example
2296
+ * use Array.{ module Immutable }
2297
+ * let arr = Immutable.fromList([2, 3, 1, 4])
2298
+ * assert Immutable.sort(compare=(a, b) => a - b, arr) == Immutable.fromList([1, 2, 3, 4])
2299
+ *
2300
+ * @since v0.6.0
2301
+ * @history v0.5.4: Originally in `"immutablearray"` module, with `compare` being a required argument
2302
+ */
2303
+ provide let sort = (compare=compare, array) => {
2304
+ fromList(List.sort(compare=compare, toList(array)))
2305
+ }
2306
+
2307
+ /**
2308
+ * Rotates array elements by the specified amount to the left, such that the
2309
+ * `n`th element is the first in the new array.
2310
+ *
2311
+ * If value is negative, array elements will be rotated by the
2312
+ * specified amount to the right. See examples.
2313
+ *
2314
+ * @param n: The number of elements to rotate by
2315
+ * @param array: The array to be rotated
2316
+ *
2317
+ * @example
2318
+ * use Array.{ module Immutable }
2319
+ * let arr = Immutable.fromList([1, 2, 3, 4, 5])
2320
+ * assert Immutable.rotate(2, arr) == Immutable.fromList([3, 4, 5, 1, 2])
2321
+ * @example
2322
+ * use Array.{ module Immutable }
2323
+ * let arr = Immutable.fromList([1, 2, 3, 4, 5])
2324
+ * assert Immutable.rotate(-1, arr) == Immutable.fromList([5, 1, 2, 3, 4])
2325
+ *
2326
+ * @since v0.6.0
2327
+ * @history v0.5.4: Originally in `"immutablearray"` module
2328
+ */
2329
+ provide let rotate = (n, array) => {
2330
+ let sliceI = if (array.length == 0) 0 else n % array.length
2331
+ let before = slice(0, end=sliceI, array)
2332
+ let after = slice(sliceI, end=array.length, array)
2333
+ append(after, before)
2334
+ }
2335
+ }