@grain/stdlib 0.5.12 → 0.6.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 (155) hide show
  1. package/CHANGELOG.md +200 -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 +47 -37
  21. package/hash.md +21 -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 +2084 -0
  31. package/json.md +608 -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 +191 -172
  92. package/runtime/numberUtils.md +17 -9
  93. package/runtime/numbers.gr +1695 -1021
  94. package/runtime/numbers.md +1098 -134
  95. package/runtime/string.gr +540 -242
  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/immutablearray.gr DELETED
@@ -1,929 +0,0 @@
1
- /**
2
- * @module ImmutableArray: An immutable array implementation, providing fast arbitrary lookups and modifications.
3
- *
4
- * @example import ImmutableArray from "immutablearray"
5
- *
6
- * @since v0.6.0
7
- */
8
-
9
- import List from "list"
10
- import Array from "array"
11
- import Set from "set"
12
- import Buffer from "buffer"
13
- import Option from "option"
14
- import Number from "number"
15
- import Exception from "runtime/exception"
16
-
17
- // Immutable arrays implemented as relaxed radix balanced trees. This data
18
- // structure allows access and updating in O(log(n)) time, but since the tree
19
- // branching factor is chosen to be a large number (32), these operations run
20
- // in effectively constant time in most practical situations.
21
-
22
- // This implementation was adapted from Elm's Array module
23
- // https://github.com/elm/core/blob/master/src/Array.elm
24
- // license of software used:
25
-
26
- // Copyright 2014-present Evan Czaplicki
27
- //
28
- // Redistribution and use in source and binary forms, with or without
29
- // modification, are permitted provided that the following conditions are met:
30
- //
31
- // 1. Redistributions of source code must retain the above copyright notice,
32
- // this list of conditions and the following disclaimer.
33
- //
34
- // 2. Redistributions in binary form must reproduce the above copyright notice,
35
- // this list of conditions and the following disclaimer in the documentation
36
- // and/or other materials provided with the distribution.
37
- //
38
- // 3. Neither the name of the copyright holder nor the names of its
39
- // contributors may be used to endorse or promote products derived from this
40
- // software without specific prior written permission.
41
- //
42
- // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
43
- // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
44
- // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
45
- // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
46
- // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
47
- // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
48
- // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
49
- // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
50
- // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
51
- // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
52
- // POSSIBILITY OF SUCH DAMAGE.
53
-
54
- // Maximum number of children each tree node can have;
55
- // an arbitrary multiple of 2 that gives a good performance tradeoff
56
- let branchingFactor = 32
57
- // 32 = 2^5
58
- let branchingBits = 5
59
- // To be applied to numbers to bring them within the range [0, 32)
60
- let bitmask = branchingFactor - 1
61
-
62
- type Tree<a> = Array<Node<a>>,
63
- enum Node<a> {
64
- Tree(Tree<a>),
65
- Leaf(Array<a>),
66
- }
67
-
68
- // a helper data structure used for building an array piece by piece
69
- record Builder<a> {
70
- btail: Array<a>,
71
- nodes: List<Node<a>>,
72
- numNodes: Number,
73
- }
74
-
75
- /**
76
- * @section Types: Type declarations included in the ImmutableArray module.
77
- */
78
-
79
- // A "tail" of < 32 values at the end of the array is kept as a performance
80
- // optimization
81
- record ImmutableArray<a> {
82
- length: Number,
83
- shift: Number,
84
- root: Tree<a>,
85
- tail: Array<a>,
86
- }
87
-
88
- /**
89
- * @section Values: Functions and constants for working with immutable arrays.
90
- */
91
-
92
- /**
93
- * An empty array.
94
- *
95
- * @since v0.6.0
96
- */
97
- export let empty = {
98
- let empty = { length: 0, shift: branchingBits, root: [>], tail: [>] }
99
- empty
100
- }
101
-
102
- /**
103
- * Determines if the array contains no elements.
104
- *
105
- * @param array: The array to check
106
- * @returns `true` if the array is empty and `false` otherwise
107
- *
108
- * @since v0.6.0
109
- */
110
- export let isEmpty = array => array.length == 0
111
-
112
- /**
113
- * Provides the length of the input array.
114
- *
115
- * @param array: The array to inspect
116
- * @returns The number of elements in the array
117
- *
118
- * @example length(fromList([1, 2, 3, 4, 5])) == 5
119
- * @since v0.6.0
120
- */
121
- export let length = array => array.length
122
-
123
- let tailIndex = length => {
124
- let shiftedRight = length >> branchingBits
125
- shiftedRight << branchingBits
126
- }
127
-
128
- let wrapNegativeIndex = (len, index) => {
129
- if (index >= 0) index else len + index
130
- }
131
-
132
- let arraySetCopy = (index, value, array) => {
133
- let copy = Array.copy(array)
134
- copy[index] = value
135
- copy
136
- }
137
-
138
- // Appends 2 arrays together, truncating to a max size of 32 for storage in
139
- // a tree node. Also returns number of elements truncated.
140
- let arrayAppendMax32 = (array1, array2) => {
141
- let len1 = Array.length(array1)
142
- let len2 = Array.length(array2)
143
-
144
- let numToAppend = Number.min(len2, branchingFactor - len1)
145
- let toAppend = if (numToAppend < len2) {
146
- Array.slice(0, numToAppend, array2)
147
- } else {
148
- array2
149
- }
150
- let numNotAppended = len1 + len2 - branchingFactor
151
-
152
- (Array.append(array1, toAppend), numNotAppended)
153
- }
154
-
155
- let log32floor = num => {
156
- let mut val = -1
157
- let mut num = num
158
- while (num > 0) {
159
- val += 1
160
- num = num >> branchingBits
161
- }
162
- val
163
- }
164
-
165
- /**
166
- * Retrieves the element from the array at the specified index.
167
- * A negative index is treated as an offset from the end of the array.
168
- *
169
- * @param index: The index to access
170
- * @param array: The array to access
171
- * @returns The element from the array
172
- * @throws IndexOutOfBounds: When the index being accessed is outside the array's bounds
173
- *
174
- * @example get(1, fromList([1, 2, 3, 4])) == 2
175
- * @example get(-1, fromList([1, 2, 3, 4])) == 4
176
- *
177
- * @since v0.6.0
178
- */
179
- export let get = (index, array) => {
180
- let index = wrapNegativeIndex(array.length, index)
181
-
182
- let rec getInner = (shift, node) => {
183
- let pos = index >> shift & bitmask
184
- match (node[pos]) {
185
- Tree(subTree) => getInner(shift - branchingBits, subTree),
186
- Leaf(vals) => vals[index & bitmask],
187
- }
188
- }
189
-
190
- let { length, shift, root, tail } = array
191
- if (index < 0 || index >= length) {
192
- throw Exception.IndexOutOfBounds
193
- } else if (index >= tailIndex(length)) {
194
- tail[index & bitmask]
195
- } else {
196
- getInner(shift, root)
197
- }
198
- }
199
-
200
- /**
201
- * Creates a new array in which the element at the specified index is set to a
202
- * new value. A negative index is treated as an offset from the end of the array.
203
- *
204
- * @param index: The index to update
205
- * @param value: The value to store
206
- * @param array: The array to update
207
- * @returns A new array containing the new element at the given index
208
- * @throws IndexOutOfBounds: When the index being updated is outside the array's bounds
209
- *
210
- * @example set(1, 9, fromList([1, 2, 3, 4, 5])) == fromList([1, 9, 3, 4, 5])
211
- *
212
- * @since v0.6.0
213
- */
214
- export let set = (index, value, array) => {
215
- let index = wrapNegativeIndex(array.length, index)
216
-
217
- let rec setInner = (shift, node) => {
218
- let pos = index >> shift & bitmask
219
- let newVal = match (node[pos]) {
220
- Tree(subTree) => {
221
- Tree(setInner(shift - branchingBits, subTree))
222
- },
223
- Leaf(vals) => {
224
- Leaf(arraySetCopy(index & bitmask, value, vals))
225
- },
226
- }
227
- arraySetCopy(pos, newVal, node)
228
- }
229
-
230
- let { length, shift, root, tail } = array
231
- if (index < 0 || index >= length) {
232
- throw Exception.IndexOutOfBounds
233
- } else if (index >= tailIndex(length)) {
234
- { length, shift, root, tail: arraySetCopy(index & bitmask, value, tail) }
235
- } else {
236
- { length, shift, root: setInner(shift, root), tail }
237
- }
238
- }
239
-
240
- // Inserts a new tail into the array. If the length of the tail equals the
241
- // branching factor, it is instead inserted into the main tree rather than
242
- // the tail
243
- let replaceTail = (newTail, array) => {
244
- let rec insertTailInTree = (shift, node) => {
245
- let pos = array.length >> shift & bitmask
246
- if (pos >= Array.length(node)) {
247
- let newElem = if (shift == branchingBits) {
248
- Leaf(newTail)
249
- } else {
250
- Tree(insertTailInTree(shift - branchingBits, [>]))
251
- }
252
- Array.append(node, [> newElem])
253
- } else {
254
- let newSubTree = match (node[pos]) {
255
- Tree(subTree) => subTree,
256
- Leaf(_) => [> node[pos]],
257
- }
258
- let newNode = Tree(insertTailInTree(shift - branchingBits, newSubTree))
259
- arraySetCopy(pos, newNode, node)
260
- }
261
- }
262
-
263
- let { length, shift, root, tail } = array
264
- let newArrayLen = length + (Array.length(newTail) - Array.length(tail))
265
- if (Array.length(newTail) == branchingFactor) {
266
- let overflow = newArrayLen >> branchingBits > 1 << shift
267
- if (overflow) {
268
- let newShift = shift + branchingBits
269
- let newRoot = insertTailInTree(newShift, [> Tree(root)])
270
- { length: newArrayLen, shift: newShift, root: newRoot, tail: [>] }
271
- } else {
272
- let newRoot = insertTailInTree(shift, root)
273
- { length: newArrayLen, shift, root: newRoot, tail: [>] }
274
- }
275
- } else {
276
- { length: newArrayLen, shift, root, tail: newTail }
277
- }
278
- }
279
-
280
- let appendTree = (toAppend, array) => {
281
- let (appended, numNotAppended) = arrayAppendMax32(array.tail, toAppend)
282
- let newArray = replaceTail(appended, array)
283
- if (numNotAppended > 0) {
284
- let appendLen = Array.length(toAppend)
285
- let newTail = Array.slice(appendLen - numNotAppended, appendLen, toAppend)
286
- replaceTail(newTail, newArray)
287
- } else {
288
- newArray
289
- }
290
- }
291
-
292
- // Flatten an array into a builder
293
- let arrayToBuilder = array => {
294
- let rec reduceFn = (acc, node) => {
295
- match (node) {
296
- Tree(subTree) => Array.reduce(reduceFn, acc, subTree),
297
- Leaf(_) => [node, ...acc],
298
- }
299
- }
300
- let { tail, root, length, _ } = array
301
- {
302
- btail: tail,
303
- nodes: Array.reduce(reduceFn, [], root),
304
- numNodes: length >> branchingBits,
305
- }
306
- }
307
-
308
- // For use to compress a large (> 32) list of nodes into subtrees
309
- let rec compressNodes = (nodes, acc) => {
310
- let node = Array.fromList(List.take(branchingFactor, nodes))
311
- let remaining = List.drop(branchingFactor, nodes)
312
- let newAcc = [Tree(node), ...acc]
313
- match (remaining) {
314
- [] => List.reverse(newAcc),
315
- _ => compressNodes(remaining, newAcc),
316
- }
317
- }
318
-
319
- let builderToArray = builder => {
320
- // Builds the non-tail portion of an array
321
- let rec buildTree = (nodes, numNodes) => {
322
- let newNodeSize = Number.ceil(numNodes / branchingFactor)
323
- match (newNodeSize) {
324
- 1 => Array.fromList(nodes),
325
- _ => buildTree(compressNodes(nodes, []), newNodeSize),
326
- }
327
- }
328
-
329
- let { btail, nodes, numNodes } = builder
330
- match (numNodes) {
331
- 0 =>
332
- {
333
- length: Array.length(btail),
334
- shift: branchingBits,
335
- root: [>],
336
- tail: btail,
337
- },
338
- _ => {
339
- let treeSize = numNodes * branchingFactor
340
- let depth = log32floor(treeSize - 1)
341
- {
342
- length: treeSize + Array.length(btail),
343
- shift: Number.max(1, depth) * branchingBits,
344
- root: buildTree(nodes, numNodes),
345
- tail: btail,
346
- }
347
- },
348
- }
349
- }
350
-
351
- // Append a chunk of <= 32 values to a builder
352
- let appendBuilder = (toAppend, builder) => {
353
- let { btail, nodes, numNodes } = builder
354
- let (appended, numNotAppended) = arrayAppendMax32(btail, toAppend)
355
-
356
- if (numNotAppended >= 0) {
357
- let appendLen = Array.length(toAppend)
358
- {
359
- btail: Array.slice(appendLen - numNotAppended, appendLen, toAppend),
360
- nodes: [Leaf(appended), ...nodes],
361
- numNodes: numNodes + 1,
362
- }
363
- } else {
364
- { btail: appended, nodes, numNodes }
365
- }
366
- }
367
-
368
- /**
369
- * Creates a new array with the elements of the first array followed by
370
- * the elements of the second array.
371
- *
372
- * @param array1: The array containing elements to appear first
373
- * @param array2: The array containing elements to appear second
374
- * @returns The new array containing elements from `array1` followed by elements from `array2`
375
- *
376
- * @example append(fromList([1, 2]), fromList([3, 4, 5])) == fromList([1, 2, 3, 4, 5])
377
- *
378
- * @since v0.6.0
379
- */
380
- export let append = (array1, array2) => {
381
- // Magic number of 4 determined best from benchmarks according to Elm's
382
- // Array implementation
383
- if (array2.length <= branchingFactor * 4) {
384
- let rec reduceFn = (array, node) => {
385
- match (node) {
386
- Tree(subTree) => Array.reduce(reduceFn, array, subTree),
387
- Leaf(vals) => appendTree(vals, array),
388
- }
389
- }
390
- let withoutTail = Array.reduce(reduceFn, array1, array2.root)
391
- appendTree(array2.tail, withoutTail)
392
- } else {
393
- let rec reduceFn = (builder, node) => {
394
- match (node) {
395
- Tree(subTree) => Array.reduce(reduceFn, builder, subTree),
396
- Leaf(vals) => appendBuilder(vals, builder),
397
- }
398
- }
399
- let withoutTail = Array.reduce(
400
- reduceFn,
401
- arrayToBuilder(array1),
402
- array2.root
403
- )
404
- let { btail, nodes, numNodes } = appendBuilder(array2.tail, withoutTail)
405
- builderToArray({ btail, nodes: List.reverse(nodes), numNodes })
406
- }
407
- }
408
-
409
- /**
410
- * Creates a single array containing the elements of all arrays in the
411
- * provided list.
412
- *
413
- * @param arrays: A list containing all arrays to combine
414
- * @returns The new array
415
- *
416
- * @example concat([fromList([1, 2]), fromList([3, 4]), fromList([5, 6])]) == fromList([1, 2, 3, 4, 5, 6])
417
- *
418
- * @since v0.6.0
419
- */
420
- export let concat = arrays => {
421
- List.reduce(append, empty, arrays)
422
- }
423
-
424
- /**
425
- * Creates a new array of the specified length where each element is
426
- * initialized with the result of an initializer function. The initializer
427
- * is called with the index of each array element.
428
- *
429
- * @param length: The length of the new array
430
- * @param fn: The initializer function to call with each index, where the value returned will be used to initialize the element
431
- * @returns The new array
432
- *
433
- * @example init(5, i => i + 3) == fromList([3, 4, 5, 6, 7])
434
- *
435
- * @since v0.6.0
436
- */
437
- export let init = (length, fn) => {
438
- let tailLen = length % branchingFactor
439
- let btail = Array.init(tailLen, i => fn(length - tailLen + i))
440
-
441
- let rec initInner = (beginI, nodes) => {
442
- if (beginI < 0) {
443
- builderToArray({ btail, nodes, numNodes: length >> branchingBits })
444
- } else {
445
- let leaf = Leaf(Array.init(branchingFactor, i => fn(beginI + i)))
446
- initInner(beginI - branchingFactor, [leaf, ...nodes])
447
- }
448
- }
449
- initInner(length - tailLen - branchingFactor, [])
450
- }
451
-
452
- /**
453
- * Creates a new array of the specified length with each element being
454
- * initialized with the given value.
455
- *
456
- * @param length: The length of the new array
457
- * @param value: The value to store at each index
458
- * @returns The new array
459
- *
460
- * @example make(5, "foo") == fromList(["foo", "foo", "foo", "foo", "foo"])
461
- *
462
- * @since v0.6.0
463
- */
464
- export let make = (length, value) => {
465
- init(length, (_) => value)
466
- }
467
-
468
- /**
469
- * Iterates an array, calling an iterator function on each element.
470
- *
471
- * @param fn: The iterator function to call with each element
472
- * @param array: The array to iterate
473
- *
474
- * @since v0.6.0
475
- */
476
- export let forEach = (fn, array) => {
477
- let rec forEachFn = node => {
478
- match (node) {
479
- Tree(subTree) => Array.forEach(forEachFn, subTree),
480
- Leaf(vals) => Array.forEach(fn, vals),
481
- }
482
- }
483
- let { tail, root, _ } = array
484
- Array.forEach(forEachFn, root)
485
- Array.forEach(fn, tail)
486
- }
487
-
488
- /**
489
- * Iterates an array a given number of times, calling an iterator function on each element.
490
- *
491
- * @param fn: The iterator function to call with each element
492
- * @param n: The number of times to iterate the given array
493
- * @param array: The array to iterate
494
- *
495
- * @since v0.6.0
496
- */
497
- export let cycle = (fn, n, array) => {
498
- for (let mut i = 0; i < n; i += 1) {
499
- forEach(fn, array)
500
- }
501
- }
502
-
503
- /**
504
- * Produces a new array initialized with the results of a mapper function
505
- * called on each element of the input array.
506
- *
507
- * @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
508
- * @param array: The array to iterate
509
- * @returns The new array with mapped values
510
- *
511
- * @since v0.6.0
512
- */
513
- export let map = (fn, array) => {
514
- let rec mapFn = node => {
515
- match (node) {
516
- Tree(subTree) => Tree(Array.map(mapFn, subTree)),
517
- Leaf(vals) => Leaf(Array.map(fn, vals)),
518
- }
519
- }
520
- let { length, shift, root, tail } = array
521
- let newRoot = Array.map(mapFn, root)
522
- let newTail = Array.map(fn, tail)
523
- { length, shift, root: newRoot, tail: newTail }
524
- }
525
-
526
- /**
527
- * Combines all elements of an array using a reducer function,
528
- * starting from the "head", or left side, of the array.
529
- *
530
- * In `ImmutableArray.reduce(fn, initial, array)`, `fn` is called with
531
- * an accumulator and each element of the array, and returns
532
- * a new accumulator. The final value is the last accumulator
533
- * returned. The accumulator starts with value `initial`.
534
- *
535
- * @param fn: The reducer function to call on each element, where the value returned will be the next accumulator value
536
- * @param initial: The initial value to use for the accumulator on the first iteration
537
- * @param array: The array to iterate
538
- * @returns The final accumulator returned from `fn`
539
- *
540
- * @example reduce((acc, x) => acc + x, 0, fromList([1, 2, 3])) == 6
541
- *
542
- * @since v0.6.0
543
- */
544
- export let reduce = (fn, initial, array) => {
545
- let rec reduceFn = (acc, node) => {
546
- match (node) {
547
- Tree(subTree) => Array.reduce(reduceFn, acc, subTree),
548
- Leaf(vals) => Array.reduce(fn, acc, vals),
549
- }
550
- }
551
- let { tail, root, _ } = array
552
- let withoutTail = Array.reduce(reduceFn, initial, root)
553
- Array.reduce(fn, withoutTail, tail)
554
- }
555
-
556
- /**
557
- * Combines all elements of an array using a reducer function,
558
- * starting from the "end", or right side, of the array.
559
- *
560
- * In `ImmutableArray.reduceRight(fn, initial, array)`, `fn` is called with
561
- * each element of the array and an accumulator, and returns
562
- * a new accumulator. The final value is the last accumulator
563
- * returned. The accumulator starts with value `initial`.
564
- *
565
- * @param fn: The reducer function to call on each element, where the value returned will be the next accumulator value
566
- * @param initial: The initial value to use for the accumulator on the first iteration
567
- * @param array: The array to iterate
568
- * @returns The final accumulator returned from `fn`
569
- *
570
- * @example reduceRight((x, acc) => acc ++ x, "", fromList(["baz", "bar", "foo"])) == "foobarbaz"
571
- *
572
- * @since v0.6.0
573
- */
574
- export let reduceRight = (fn, initial, array) => {
575
- let rec reduceFn = (node, acc) => {
576
- match (node) {
577
- Tree(subTree) => Array.reduceRight(reduceFn, acc, subTree),
578
- Leaf(vals) => Array.reduceRight(fn, acc, vals),
579
- }
580
- }
581
- let { tail, root, _ } = array
582
- let tailVal = Array.reduceRight(fn, initial, tail)
583
- Array.reduceRight(reduceFn, tailVal, root)
584
- }
585
-
586
- /**
587
- * Produces a new array by calling a function on each element
588
- * of the input array. Each iteration produces an intermediate
589
- * array, which are all appended to produce a "flattened" array
590
- * of all results.
591
- *
592
- * @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
593
- * @param array: The array to iterate
594
- * @returns The new array
595
- *
596
- * @example flatMap(n => fromList([n, n + 1]), fromList([1, 3, 5])) == fromList([1, 2, 3, 4, 5, 6])
597
- *
598
- * @since v0.6.0
599
- */
600
- export let flatMap = (fn, array) => {
601
- reduce((acc, x) => append(acc, fn(x)), empty, array)
602
- }
603
-
604
- /**
605
- * Converts the input list to an array.
606
- *
607
- * @param list: The list to convert
608
- * @returns The array containing all elements from the list
609
- *
610
- * @since v0.6.0
611
- */
612
- export let fromList = list => {
613
- let rec fromListInner = (list, nodes, numNodes) => {
614
- let node = Array.fromList(List.take(branchingFactor, list))
615
- let remaining = List.drop(branchingFactor, list)
616
- if (Array.length(node) < branchingFactor) {
617
- builderToArray({ btail: node, nodes: List.reverse(nodes), numNodes })
618
- } else {
619
- fromListInner(remaining, [Leaf(node), ...nodes], numNodes + 1)
620
- }
621
- }
622
-
623
- fromListInner(list, [], 0)
624
- }
625
-
626
- /**
627
- * Converts the input array to a list.
628
- *
629
- * @param array: The array to convert
630
- * @returns The list containing all elements from the array
631
- *
632
- * @since v0.6.0
633
- */
634
- export let toList = array => {
635
- reduceRight((val, list) => [val, ...list], [], array)
636
- }
637
-
638
- /**
639
- * Produces a new array by calling a function on each element of
640
- * the input array and only including it in the result array if the element satisfies
641
- * the condition.
642
- *
643
- * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
644
- * @param array: The array to iterate
645
- * @returns The new array containing elements where `fn` returned `true`
646
- *
647
- * @since v0.6.0
648
- */
649
- export let filter = (fn, array) => {
650
- fromList(reduceRight((x, arr) => if (fn(x)) [x, ...arr] else arr, [], array))
651
- }
652
-
653
- /**
654
- * Checks that the given condition is satisfied for all
655
- * elements in the input array.
656
- *
657
- * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
658
- * @param array: The array to check
659
- * @returns `true` if all elements satify the condition or `false` otherwise
660
- *
661
- * @since v0.6.0
662
- */
663
- export let every = (fn, array) => {
664
- reduce((acc, x) => acc && fn(x), true, array)
665
- }
666
-
667
- /**
668
- * Checks that the given condition is satisfied **at least
669
- * once** by an element in the input array.
670
- *
671
- * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
672
- * @param array: The array to iterate
673
- * @returns `true` if one or more elements satify the condition or `false` otherwise
674
- *
675
- * @since v0.6.0
676
- */
677
- export let some = (fn, array) => {
678
- reduce((acc, x) => acc || fn(x), false, array)
679
- }
680
-
681
- /**
682
- * Creates a new array with all elements in reverse order.
683
- *
684
- * @param array: The array to reverse
685
- * @returns The new array
686
- *
687
- * @since v0.6.0
688
- */
689
- export let reverse = array => {
690
- fromList(reduce((acc, x) => [x, ...acc], [], array))
691
- }
692
-
693
- /**
694
- * Checks if the value is an element of the input array.
695
- * Uses the generic `==` structural equality operator.
696
- *
697
- * @param search: The value to compare
698
- * @param array: The array to inspect
699
- * @returns `true` if the value exists in the array or `false` otherwise
700
- *
701
- * @since v0.6.0
702
- */
703
- export let contains = (value, array) => {
704
- reduce((acc, x) => acc || x == value, false, array)
705
- }
706
-
707
- /**
708
- * Finds the first element in an array that satifies the given condition.
709
- *
710
- * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
711
- * @param array: The array to search
712
- * @returns `Some(element)` containing the first value found or `None` otherwise
713
- *
714
- * @since v0.6.0
715
- */
716
- export let find = (fn, array) => {
717
- reduce((acc, x) => if (acc == None && fn(x)) Some(x) else acc, None, array)
718
- }
719
-
720
- /**
721
- * Finds the first index in an array where the element satifies the given condition.
722
- *
723
- * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
724
- * @param array: The array to search
725
- * @returns `Some(index)` containing the index of the first element found or `None` otherwise
726
- *
727
- * @since v0.6.0
728
- */
729
- export let findIndex = (fn, array) => {
730
- let mut i = -1
731
- reduce((acc, x) => {
732
- i += 1
733
- if (acc == None && fn(x)) Some(i) else acc
734
- }, None, array)
735
- }
736
-
737
- /**
738
- * Combines two arrays into a Cartesian product of tuples containing
739
- * all ordered pairs `(a, b)`.
740
- *
741
- * @param array1: The array to provide values for the first tuple element
742
- * @param array2: The array to provide values for the second tuple element
743
- * @returns The new array containing all pairs of `(a, b)`
744
- *
745
- * @since v0.6.0
746
- */
747
- export let product = (array1, array2) => {
748
- fromList(
749
- reduceRight((x1, list) => {
750
- reduceRight((x2, list) => [(x1, x2), ...list], list, array2)
751
- }, [], array1)
752
- )
753
- }
754
-
755
- /**
756
- * Counts the number of elements in an array that satisfy the given condition.
757
- *
758
- * @param fn: The function to call on each element, where the returned value indicates if the element satisfies the condition
759
- * @param array: The array to iterate
760
- * @returns The total number of elements that satisfy the condition
761
- *
762
- * @since v0.6.0
763
- */
764
- export let count = (fn, array) => {
765
- reduce((acc, x) => if (fn(x)) acc + 1 else acc, 0, array)
766
- }
767
-
768
- /**
769
- * Produces a new array with any duplicates removed.
770
- * Uses the generic `==` structural equality operator.
771
- *
772
- * @param array: The array to filter
773
- * @returns The new array with only unique values
774
- *
775
- * @since v0.6.0
776
- */
777
- export let unique = array => {
778
- let set = Set.fromList(toList(array))
779
- filter(x => {
780
- let good = Set.contains(x, set)
781
- if (good) {
782
- Set.remove(x, set)
783
- }
784
- good
785
- }, array)
786
- }
787
-
788
- /**
789
- * Produces a new array filled with tuples of elements from both given arrays.
790
- * The first tuple will contain the first item of each array, the second tuple
791
- * will contain the second item of each array, and so on.
792
- *
793
- * Calling this function with arrays of different sizes will cause the returned
794
- * array to have the length of the smaller array.
795
- *
796
- * @param array1: The array to provide values for the first tuple element
797
- * @param array2: The array to provide values for the second tuple element
798
- * @returns The new array containing indexed pairs of `(a, b)`
799
- *
800
- * @since v0.6.0
801
- */
802
- export let zip = (array1, array2) => {
803
- fromList(List.zip(toList(array1), toList(array2)))
804
- }
805
-
806
- /**
807
- * Produces a new array filled with elements defined by applying a function on
808
- * pairs from both given arrays. The first element will contain the result of
809
- * applying the function to the first elements of each array, the second element
810
- * will contain the result of applying the function to the second elements of
811
- * each array, and so on.
812
- *
813
- * Calling this function with arrays of different sizes will cause the returned
814
- * array to have the length of the smaller array.
815
- *
816
- * @param fn: The function to apply to pairs of elements
817
- * @param array1: The array whose elements will each be passed to the function as the first argument
818
- * @param array2: The array whose elements will each be passed to the function as the second argument
819
- * @returns The new array containing elements derived from applying the function to pairs of input array elements
820
- *
821
- * @example zipWith((a, b) => a + b, fromList([1, 2, 3]), fromList([4, 5, 6])) == fromList([5, 7, 9])
822
- * @example zipWith((a, b) => a * b, fromList([1, 2, 3]), fromList([4, 5])) == fromList([4, 10])
823
- *
824
- * @since v0.6.0
825
- */
826
- export let zipWith = (fn, array1, array2) => {
827
- fromList(List.zipWith(fn, toList(array1), toList(array2)))
828
- }
829
-
830
- /**
831
- * Produces two arrays by splitting apart an array of tuples.
832
- *
833
- * @param array: The array of tuples to split
834
- * @returns An array containing all elements from the first tuple element and an array containing all elements from the second tuple element
835
- *
836
- * @since v0.6.0
837
- */
838
- export let unzip = array => {
839
- let (list1, list2) = List.unzip(toList(array))
840
- (fromList(list1), fromList(list2))
841
- }
842
-
843
- /**
844
- * Concatenates an array of strings into a single string, separated by a separator string.
845
- *
846
- * @param separator: The separator to insert between items in the string
847
- * @param array: The input strings
848
- * @returns The concatenated string
849
- *
850
- * @since v0.6.0
851
- */
852
- export let join = (separator, array) => {
853
- let buf = Buffer.make(16)
854
- let mut i = 0
855
- forEach(x => {
856
- let toAdd = if (i != array.length - 1) x ++ separator else x
857
- i += 1
858
- Buffer.addString(toAdd, buf)
859
- }, array)
860
- Buffer.toString(buf)
861
- }
862
-
863
- let clampIndex = (len, index) => {
864
- Number.min(len, Number.max(0, wrapNegativeIndex(len, index)))
865
- }
866
-
867
- /**
868
- * Slices an array given zero-based start and end indexes. The value
869
- * at the end index will not be included in the result.
870
- *
871
- * If either index is a negative number, it will be treated as a reverse index from
872
- * the end of the array.
873
- *
874
- * @param start: The index of the array where the slice will begin (inclusive)
875
- * @param end: The index of the array where the slice will end (exclusive)
876
- * @param array: The array to be sliced
877
- * @returns The subset of the array that was sliced
878
- *
879
- * @example slice(0, 2, fromList(['a', 'b', 'c'])) == fromList(['a', 'b'])
880
- * @example slice(1, -1, fromList(['a', 'b', 'c'])) == fromList(['b'])
881
- *
882
- * @since v0.6.0
883
- */
884
- export let slice = (start, end, array) => {
885
- let begin = clampIndex(array.length, start)
886
- let end = clampIndex(array.length, end)
887
- let mut i = array.length
888
- fromList(
889
- reduceRight((x, acc) => {
890
- i -= 1
891
- if (i >= begin && i < end) [x, ...acc] else acc
892
- }, [], array)
893
- )
894
- }
895
-
896
- /**
897
- * Sorts the given array based on a given comparator function.
898
- *
899
- * 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.
900
- * @param comp: The comparator function used to indicate sort order
901
- * @param array: The array to be sorted
902
- * @returns The sorted array
903
- *
904
- * @since v0.6.0
905
- */
906
- export let sort = (comp, array) => {
907
- fromList(List.sort(comp, toList(array)))
908
- }
909
-
910
- /**
911
- * Rotates array elements by the specified amount to the left.
912
- *
913
- * If value is negative, array elements will be rotated by the
914
- * specified amount to the right. See examples.
915
- *
916
- * @param n: The number of elements to rotate by
917
- * @param array: The array to be rotated
918
- *
919
- * @example rotate(2, fromList([1, 2, 3, 4, 5])) == fromList([3, 4, 5, 1, 2])
920
- * @example rotate(-1, fromList([1, 2, 3, 4, 5])) == fromList([5, 1, 2, 3, 4])
921
- *
922
- * @since v0.6.0
923
- */
924
- export let rotate = (n, array) => {
925
- let sliceI = if (array.length == 0) 0 else n % array.length
926
- let before = slice(0, sliceI, array)
927
- let after = slice(sliceI, array.length, array)
928
- append(after, before)
929
- }