@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.
- package/CHANGELOG.md +201 -0
- package/LICENSE +1 -1
- package/README.md +25 -2
- package/array.gr +1512 -199
- package/array.md +2032 -94
- package/bigint.gr +239 -140
- package/bigint.md +450 -106
- package/buffer.gr +595 -102
- package/buffer.md +903 -145
- package/bytes.gr +401 -110
- package/bytes.md +551 -63
- package/char.gr +228 -49
- package/char.md +373 -7
- package/exception.gr +26 -12
- package/exception.md +29 -5
- package/float32.gr +130 -109
- package/float32.md +185 -57
- package/float64.gr +112 -99
- package/float64.md +185 -57
- package/hash.gr +62 -40
- package/hash.md +27 -3
- package/int16.gr +430 -0
- package/int16.md +618 -0
- package/int32.gr +200 -269
- package/int32.md +254 -289
- package/int64.gr +142 -225
- package/int64.md +254 -289
- package/int8.gr +511 -0
- package/int8.md +786 -0
- package/json.gr +2071 -0
- package/json.md +646 -0
- package/list.gr +120 -68
- package/list.md +125 -80
- package/map.gr +560 -57
- package/map.md +672 -56
- package/marshal.gr +239 -227
- package/marshal.md +36 -4
- package/number.gr +626 -676
- package/number.md +738 -153
- package/option.gr +33 -35
- package/option.md +58 -42
- package/package.json +2 -2
- package/path.gr +148 -187
- package/path.md +47 -96
- package/pervasives.gr +75 -416
- package/pervasives.md +85 -180
- package/priorityqueue.gr +433 -74
- package/priorityqueue.md +422 -54
- package/queue.gr +362 -80
- package/queue.md +433 -38
- package/random.gr +67 -75
- package/random.md +68 -40
- package/range.gr +135 -63
- package/range.md +198 -43
- package/rational.gr +284 -0
- package/rational.md +545 -0
- package/regex.gr +933 -1066
- package/regex.md +59 -60
- package/result.gr +23 -25
- package/result.md +54 -39
- package/runtime/atof/common.gr +78 -82
- package/runtime/atof/common.md +22 -10
- package/runtime/atof/decimal.gr +102 -127
- package/runtime/atof/decimal.md +28 -7
- package/runtime/atof/lemire.gr +56 -71
- package/runtime/atof/lemire.md +9 -1
- package/runtime/atof/parse.gr +83 -110
- package/runtime/atof/parse.md +12 -2
- package/runtime/atof/slow.gr +28 -35
- package/runtime/atof/slow.md +9 -1
- package/runtime/atof/table.gr +19 -18
- package/runtime/atof/table.md +10 -2
- package/runtime/atoi/parse.gr +153 -136
- package/runtime/atoi/parse.md +50 -1
- package/runtime/bigint.gr +410 -517
- package/runtime/bigint.md +71 -57
- package/runtime/compare.gr +176 -85
- package/runtime/compare.md +31 -1
- package/runtime/dataStructures.gr +144 -32
- package/runtime/dataStructures.md +267 -31
- package/runtime/debugPrint.gr +34 -15
- package/runtime/debugPrint.md +37 -5
- package/runtime/equal.gr +53 -52
- package/runtime/equal.md +30 -1
- package/runtime/exception.gr +38 -47
- package/runtime/exception.md +10 -8
- package/runtime/gc.gr +23 -152
- package/runtime/gc.md +13 -17
- package/runtime/malloc.gr +31 -31
- package/runtime/malloc.md +11 -3
- package/runtime/numberUtils.gr +193 -174
- package/runtime/numberUtils.md +29 -9
- package/runtime/numbers.gr +1695 -1021
- package/runtime/numbers.md +1098 -134
- package/runtime/string.gr +543 -245
- package/runtime/string.md +76 -6
- package/runtime/unsafe/constants.gr +30 -13
- package/runtime/unsafe/constants.md +80 -0
- package/runtime/unsafe/conv.gr +55 -28
- package/runtime/unsafe/conv.md +41 -9
- package/runtime/unsafe/memory.gr +10 -30
- package/runtime/unsafe/memory.md +15 -19
- package/runtime/unsafe/tags.gr +37 -21
- package/runtime/unsafe/tags.md +88 -8
- package/runtime/unsafe/wasmf32.gr +30 -36
- package/runtime/unsafe/wasmf32.md +64 -56
- package/runtime/unsafe/wasmf64.gr +30 -36
- package/runtime/unsafe/wasmf64.md +64 -56
- package/runtime/unsafe/wasmi32.gr +49 -66
- package/runtime/unsafe/wasmi32.md +102 -94
- package/runtime/unsafe/wasmi64.gr +52 -79
- package/runtime/unsafe/wasmi64.md +108 -100
- package/runtime/utils/printing.gr +13 -15
- package/runtime/utils/printing.md +11 -3
- package/runtime/wasi.gr +294 -295
- package/runtime/wasi.md +62 -42
- package/set.gr +574 -64
- package/set.md +634 -54
- package/stack.gr +181 -64
- package/stack.md +271 -42
- package/string.gr +453 -533
- package/string.md +241 -151
- package/uint16.gr +369 -0
- package/uint16.md +585 -0
- package/uint32.gr +470 -0
- package/uint32.md +737 -0
- package/uint64.gr +471 -0
- package/uint64.md +737 -0
- package/uint8.gr +369 -0
- package/uint8.md +585 -0
- package/uri.gr +1093 -0
- package/uri.md +477 -0
- package/{sys → wasi}/file.gr +914 -500
- package/{sys → wasi}/file.md +454 -50
- package/wasi/process.gr +292 -0
- package/{sys → wasi}/process.md +164 -6
- package/wasi/random.gr +77 -0
- package/wasi/random.md +80 -0
- package/{sys → wasi}/time.gr +15 -22
- package/{sys → wasi}/time.md +5 -5
- package/immutablearray.gr +0 -929
- package/immutablearray.md +0 -1038
- package/immutablemap.gr +0 -493
- package/immutablemap.md +0 -479
- package/immutablepriorityqueue.gr +0 -360
- package/immutablepriorityqueue.md +0 -291
- package/immutableset.gr +0 -498
- package/immutableset.md +0 -449
- package/runtime/debug.gr +0 -2
- package/runtime/debug.md +0 -6
- package/runtime/unsafe/errors.gr +0 -36
- package/runtime/unsafe/errors.md +0 -204
- package/sys/process.gr +0 -254
- package/sys/random.gr +0 -79
- package/sys/random.md +0 -66
package/map.gr
CHANGED
|
@@ -1,60 +1,60 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* A Map holds key-value pairs. Any value may be used as a key or value. Operations on a Map mutate the internal state, so it never needs to be re-assigned.
|
|
3
|
+
*
|
|
4
|
+
* An immutable map implementation is available in the `Immutable` submodule.
|
|
5
|
+
* @example from "map" include Map
|
|
4
6
|
*
|
|
5
7
|
* @since v0.2.0
|
|
6
8
|
*/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
module Map
|
|
10
|
+
|
|
11
|
+
from "list" include List
|
|
12
|
+
from "array" include Array
|
|
13
|
+
from "option" include Option
|
|
14
|
+
from "runtime/dataStructures" include DataStructures
|
|
15
|
+
use DataStructures.{ allocateArray, untagSimpleNumber }
|
|
16
|
+
from "hash" include Hash
|
|
17
|
+
use Hash.{ hash }
|
|
18
|
+
from "runtime/unsafe/memory" include Memory
|
|
19
|
+
from "runtime/unsafe/wasmi32" include WasmI32
|
|
13
20
|
|
|
14
21
|
// TODO: Consider implementing this as List<(Box<k>, Box<v>)>
|
|
15
|
-
record Bucket<k, v> {
|
|
22
|
+
record rec Bucket<k, v> {
|
|
16
23
|
mut key: k,
|
|
17
24
|
mut value: v,
|
|
18
25
|
mut next: Option<Bucket<k, v>>,
|
|
19
26
|
}
|
|
20
27
|
|
|
21
|
-
|
|
22
|
-
* @section Types: Type declarations included in the Map module.
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
record Map<k, v> {
|
|
28
|
+
abstract record Map<k, v> {
|
|
26
29
|
mut size: Number,
|
|
27
30
|
mut buckets: Array<Option<Bucket<k, v>>>,
|
|
28
31
|
}
|
|
29
32
|
|
|
30
33
|
/**
|
|
31
|
-
*
|
|
34
|
+
* Represents the internal state of a map.
|
|
32
35
|
*/
|
|
36
|
+
provide record InternalMapStats {
|
|
37
|
+
currentSize: Number,
|
|
38
|
+
bucketCount: Number,
|
|
39
|
+
}
|
|
33
40
|
|
|
34
41
|
/**
|
|
35
|
-
* Creates a new empty map with an initial storage of the given size. As
|
|
42
|
+
* Creates a new empty map with an initial storage of the given size. As
|
|
43
|
+
* values are added or removed, the internal storage may grow or shrink.
|
|
44
|
+
* Generally, you won't need to care about the storage size of your map and
|
|
45
|
+
* can use the default size.
|
|
36
46
|
*
|
|
37
47
|
* @param size: The initial storage size of the map
|
|
38
48
|
* @returns An empty map with the given initial storage size
|
|
39
49
|
*
|
|
40
50
|
* @since v0.2.0
|
|
51
|
+
* @history v0.6.0: Merged with `makeSized`; modified signature to accept size
|
|
41
52
|
*/
|
|
42
|
-
|
|
53
|
+
provide let make = (size=16) => { // TODO: This could take an `eq` function to custom comparisons
|
|
43
54
|
let buckets = Array.make(size, None)
|
|
44
55
|
{ size: 0, buckets }
|
|
45
56
|
}
|
|
46
57
|
|
|
47
|
-
/**
|
|
48
|
-
* Creates a new, empty map.
|
|
49
|
-
*
|
|
50
|
-
* @returns An empty map
|
|
51
|
-
*
|
|
52
|
-
* @since v0.2.0
|
|
53
|
-
*/
|
|
54
|
-
export let make = () => {
|
|
55
|
-
makeSized(16)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
58
|
let getBucketIndex = (key, buckets) => {
|
|
59
59
|
let bucketsLength = Array.length(buckets)
|
|
60
60
|
let hashedKey = hash(key)
|
|
@@ -130,7 +130,7 @@ let rec replaceInBucket = (key, value, node) => {
|
|
|
130
130
|
*
|
|
131
131
|
* @since v0.2.0
|
|
132
132
|
*/
|
|
133
|
-
|
|
133
|
+
provide let set = (key, value, map) => {
|
|
134
134
|
let buckets = map.buckets
|
|
135
135
|
let idx = getBucketIndex(key, buckets)
|
|
136
136
|
let bucket = buckets[idx]
|
|
@@ -174,7 +174,7 @@ let rec valueFromBucket = (key, node) => {
|
|
|
174
174
|
*
|
|
175
175
|
* @since v0.2.0
|
|
176
176
|
*/
|
|
177
|
-
|
|
177
|
+
provide let get = (key, map) => {
|
|
178
178
|
let buckets = map.buckets
|
|
179
179
|
let idx = getBucketIndex(key, buckets)
|
|
180
180
|
let bucket = buckets[idx]
|
|
@@ -204,7 +204,7 @@ let rec nodeInBucket = (key, node) => {
|
|
|
204
204
|
*
|
|
205
205
|
* @since v0.2.0
|
|
206
206
|
*/
|
|
207
|
-
|
|
207
|
+
provide let contains = (key, map) => {
|
|
208
208
|
let buckets = map.buckets
|
|
209
209
|
let idx = getBucketIndex(key, buckets)
|
|
210
210
|
let bucket = buckets[idx]
|
|
@@ -236,7 +236,7 @@ let rec removeInBucket = (key, node) => {
|
|
|
236
236
|
*
|
|
237
237
|
* @since v0.2.0
|
|
238
238
|
*/
|
|
239
|
-
|
|
239
|
+
provide let remove = (key, map) => {
|
|
240
240
|
let buckets = map.buckets
|
|
241
241
|
let idx = getBucketIndex(key, buckets)
|
|
242
242
|
let bucket = buckets[idx]
|
|
@@ -265,7 +265,7 @@ export let remove = (key, map) => {
|
|
|
265
265
|
*
|
|
266
266
|
* @since v0.3.0
|
|
267
267
|
*/
|
|
268
|
-
|
|
268
|
+
provide let update = (key, fn, map) => {
|
|
269
269
|
let val = get(key, map)
|
|
270
270
|
match (fn(val)) {
|
|
271
271
|
Some(next) => set(key, next, map),
|
|
@@ -281,7 +281,7 @@ export let update = (key, fn, map) => {
|
|
|
281
281
|
*
|
|
282
282
|
* @since v0.2.0
|
|
283
283
|
*/
|
|
284
|
-
|
|
284
|
+
provide let size = map => {
|
|
285
285
|
map.size
|
|
286
286
|
}
|
|
287
287
|
|
|
@@ -293,7 +293,7 @@ export let size = map => {
|
|
|
293
293
|
*
|
|
294
294
|
* @since v0.2.0
|
|
295
295
|
*/
|
|
296
|
-
|
|
296
|
+
provide let isEmpty = map => {
|
|
297
297
|
size(map) == 0
|
|
298
298
|
}
|
|
299
299
|
|
|
@@ -304,7 +304,7 @@ export let isEmpty = map => {
|
|
|
304
304
|
*
|
|
305
305
|
* @since v0.2.0
|
|
306
306
|
*/
|
|
307
|
-
|
|
307
|
+
provide let clear = map => {
|
|
308
308
|
map.size = 0
|
|
309
309
|
let buckets = map.buckets
|
|
310
310
|
Array.forEachi((bucket, idx) => {
|
|
@@ -331,7 +331,7 @@ let rec forEachBucket = (fn, node) => {
|
|
|
331
331
|
* @since v0.2.0
|
|
332
332
|
* @history v0.5.0: Ensured the iterator function return type is always `Void`
|
|
333
333
|
*/
|
|
334
|
-
|
|
334
|
+
provide let forEach = (fn, map) => {
|
|
335
335
|
let buckets = map.buckets
|
|
336
336
|
Array.forEach(bucket => {
|
|
337
337
|
forEachBucket(fn, bucket)
|
|
@@ -356,7 +356,7 @@ let rec reduceEachBucket = (fn, node, acc) => {
|
|
|
356
356
|
*
|
|
357
357
|
* @since v0.2.0
|
|
358
358
|
*/
|
|
359
|
-
|
|
359
|
+
provide let reduce = (fn, init, map) => {
|
|
360
360
|
let buckets = map.buckets
|
|
361
361
|
let mut acc = init
|
|
362
362
|
Array.forEach(bucket => {
|
|
@@ -373,7 +373,7 @@ export let reduce = (fn, init, map) => {
|
|
|
373
373
|
*
|
|
374
374
|
* @since v0.2.0
|
|
375
375
|
*/
|
|
376
|
-
|
|
376
|
+
provide let keys = map => {
|
|
377
377
|
reduce((list, key, _value) => [key, ...list], [], map)
|
|
378
378
|
}
|
|
379
379
|
|
|
@@ -385,7 +385,7 @@ export let keys = map => {
|
|
|
385
385
|
*
|
|
386
386
|
* @since v0.2.0
|
|
387
387
|
*/
|
|
388
|
-
|
|
388
|
+
provide let values = map => {
|
|
389
389
|
reduce((list, _key, value) => [value, ...list], [], map)
|
|
390
390
|
}
|
|
391
391
|
|
|
@@ -397,7 +397,7 @@ export let values = map => {
|
|
|
397
397
|
*
|
|
398
398
|
* @since v0.2.0
|
|
399
399
|
*/
|
|
400
|
-
|
|
400
|
+
provide let toList = map => {
|
|
401
401
|
reduce((list, key, value) => [(key, value), ...list], [], map)
|
|
402
402
|
}
|
|
403
403
|
|
|
@@ -409,7 +409,7 @@ export let toList = map => {
|
|
|
409
409
|
*
|
|
410
410
|
* @since v0.2.0
|
|
411
411
|
*/
|
|
412
|
-
|
|
412
|
+
provide let fromList = list => {
|
|
413
413
|
let map = make()
|
|
414
414
|
List.forEach(pair => {
|
|
415
415
|
let (key, value) = pair
|
|
@@ -427,24 +427,26 @@ export let fromList = list => {
|
|
|
427
427
|
* @since v0.2.0
|
|
428
428
|
*/
|
|
429
429
|
@unsafe
|
|
430
|
-
|
|
430
|
+
provide let toArray = (map: Map<a, b>) => {
|
|
431
|
+
use WasmI32.{ (*) }
|
|
431
432
|
let length = untagSimpleNumber(map.size)
|
|
432
433
|
let array = WasmI32.toGrain(allocateArray(length))
|
|
433
434
|
@unsafe
|
|
434
435
|
let reducer = (i, key, value) => {
|
|
436
|
+
use WasmI32.{ (+) as addWasmI32 }
|
|
435
437
|
// Assign the values into the array.
|
|
436
438
|
// We store them directly to prevent GC on uninitialized array data.
|
|
437
439
|
let array = WasmI32.fromGrain(array)
|
|
438
440
|
let item = (key, value)
|
|
439
441
|
WasmI32.store(
|
|
440
|
-
|
|
442
|
+
addWasmI32(array, untagSimpleNumber(i) * 4n),
|
|
441
443
|
Memory.incRef(WasmI32.fromGrain(item)),
|
|
442
444
|
8n
|
|
443
445
|
)
|
|
444
446
|
i + 1
|
|
445
447
|
}
|
|
446
448
|
reduce(reducer, 0, map)
|
|
447
|
-
array
|
|
449
|
+
array: Array<(a, b)>
|
|
448
450
|
}
|
|
449
451
|
|
|
450
452
|
/**
|
|
@@ -455,7 +457,7 @@ export let toArray: Map<a, b> -> Array<(a, b)> = map => {
|
|
|
455
457
|
*
|
|
456
458
|
* @since v0.2.0
|
|
457
459
|
*/
|
|
458
|
-
|
|
460
|
+
provide let fromArray = array => {
|
|
459
461
|
let map = make()
|
|
460
462
|
Array.forEach(pair => {
|
|
461
463
|
let (key, value) = pair
|
|
@@ -472,12 +474,16 @@ export let fromArray = array => {
|
|
|
472
474
|
*
|
|
473
475
|
* @since v0.2.0
|
|
474
476
|
*/
|
|
475
|
-
|
|
476
|
-
let keysToRemove = reduce(
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
477
|
+
provide let filter = (fn, map) => {
|
|
478
|
+
let keysToRemove = reduce(
|
|
479
|
+
(list, key, value) => if (!fn(key, value)) {
|
|
480
|
+
[key, ...list]
|
|
481
|
+
} else {
|
|
482
|
+
list
|
|
483
|
+
},
|
|
484
|
+
[],
|
|
485
|
+
map
|
|
486
|
+
)
|
|
481
487
|
List.forEach(key => {
|
|
482
488
|
remove(key, map)
|
|
483
489
|
}, keysToRemove)
|
|
@@ -491,11 +497,10 @@ export let filter = (predicate, map) => {
|
|
|
491
497
|
*
|
|
492
498
|
* @since v0.2.0
|
|
493
499
|
*/
|
|
494
|
-
|
|
495
|
-
filter((key, value) => !
|
|
500
|
+
provide let reject = (fn, map) => {
|
|
501
|
+
filter((key, value) => !fn(key, value), map)
|
|
496
502
|
}
|
|
497
503
|
|
|
498
|
-
// TODO(#190): Should return a Record type instead of a Tuple
|
|
499
504
|
/**
|
|
500
505
|
* Provides data representing the internal state state of the map.
|
|
501
506
|
*
|
|
@@ -503,7 +508,505 @@ export let reject = (predicate, map) => {
|
|
|
503
508
|
* @returns The internal state of the map
|
|
504
509
|
*
|
|
505
510
|
* @since v0.2.0
|
|
511
|
+
* @history v0.6.0: Return `InternalMapStats` record instead of a tuple
|
|
506
512
|
*/
|
|
507
|
-
|
|
508
|
-
|
|
513
|
+
provide let getInternalStats = map => {
|
|
514
|
+
{ currentSize: map.size, bucketCount: Array.length(map.buckets) }
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* An immutable map implementation.
|
|
519
|
+
*
|
|
520
|
+
* @since v0.6.0
|
|
521
|
+
* @history v0.5.4: Originally in `"immutablemap"` module
|
|
522
|
+
*/
|
|
523
|
+
provide module Immutable {
|
|
524
|
+
// implementation based on the paper "Implementing Sets Efficiently in a
|
|
525
|
+
// Functional Language" by Stephen Adams
|
|
526
|
+
record rec Node<k, v> {
|
|
527
|
+
key: k,
|
|
528
|
+
val: v,
|
|
529
|
+
size: Number,
|
|
530
|
+
left: Map<k, v>,
|
|
531
|
+
right: Map<k, v>,
|
|
532
|
+
}
|
|
533
|
+
and abstract enum Map<k, v> {
|
|
534
|
+
Empty,
|
|
535
|
+
Tree(Node<k, v>),
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// semi-arbitrary value chosen for algorithm for determining when to balance
|
|
539
|
+
// trees; no tree can have a left subtree containing this number of times
|
|
540
|
+
// more elements than its right subtree or vice versa
|
|
541
|
+
let weight = 4
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* An empty map
|
|
545
|
+
*
|
|
546
|
+
* @since v0.6.0
|
|
547
|
+
* @history v0.5.4: Originally in `"immutablemap"` module
|
|
548
|
+
*/
|
|
549
|
+
provide let empty = Empty
|
|
550
|
+
|
|
551
|
+
// returns the key-value pair of the minimum key in a tree
|
|
552
|
+
let rec min = node => {
|
|
553
|
+
match (node) {
|
|
554
|
+
Tree({ key, val, left: Empty, _ }) => (key, val),
|
|
555
|
+
Tree({ left, _ }) => min(left),
|
|
556
|
+
Empty => fail "Impossible: min of empty element in Map.Immutable",
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* Provides the count of key-value pairs stored within the map.
|
|
562
|
+
*
|
|
563
|
+
* @param map: The map to inspect
|
|
564
|
+
* @returns The count of key-value pairs in the map
|
|
565
|
+
*
|
|
566
|
+
* @since v0.6.0
|
|
567
|
+
* @history v0.5.4: Originally in `"immutablemap"` module
|
|
568
|
+
*/
|
|
569
|
+
provide let size = map => {
|
|
570
|
+
match (map) {
|
|
571
|
+
Empty => 0,
|
|
572
|
+
Tree({ size, _ }) => size,
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* Determines if the map contains no key-value pairs.
|
|
578
|
+
*
|
|
579
|
+
* @param map: The map to inspect
|
|
580
|
+
* @returns `true` if the given map is empty or `false` otherwise
|
|
581
|
+
*
|
|
582
|
+
* @since v0.6.0
|
|
583
|
+
* @history v0.5.4: Originally in `"immutablemap"` module
|
|
584
|
+
*/
|
|
585
|
+
provide let isEmpty = map => {
|
|
586
|
+
match (map) {
|
|
587
|
+
Empty => true,
|
|
588
|
+
Tree(_) => false,
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
let unwrapTree = node => {
|
|
593
|
+
match (node) {
|
|
594
|
+
Empty =>
|
|
595
|
+
fail "Impossible: Map.Immutable unwrapTree got an empty tree node",
|
|
596
|
+
Tree(tree) => tree,
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// helper function for creating a tree node with correct size from
|
|
601
|
+
// two balanced trees
|
|
602
|
+
let makeNode = (key, val, left, right) => {
|
|
603
|
+
Tree({ key, val, size: 1 + size(left) + size(right), left, right })
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// note: see Figure 1 of paper referenced above for visual illustration of
|
|
607
|
+
// the rotations below
|
|
608
|
+
|
|
609
|
+
// node rotation moving the left subtree of the right node to the left side
|
|
610
|
+
let singleL = (key, val, left, right) => {
|
|
611
|
+
let { key: rKey, val: rVal, left: rl, right: rr, _ } = unwrapTree(right)
|
|
612
|
+
makeNode(rKey, rVal, makeNode(key, val, left, rl), rr)
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// node rotation moving left child of right tree to the root
|
|
616
|
+
let doubleL = (key, val, left, right) => {
|
|
617
|
+
let { key: rKey, val: rVal, left: rl, right: rr, _ } = unwrapTree(right)
|
|
618
|
+
let { key: rlKey, val: rlVal, left: rll, right: rlr, _ } = unwrapTree(rl)
|
|
619
|
+
makeNode(
|
|
620
|
+
rlKey,
|
|
621
|
+
rlVal,
|
|
622
|
+
makeNode(key, val, left, rll),
|
|
623
|
+
makeNode(rKey, rVal, rlr, rr)
|
|
624
|
+
)
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
// node rotation moving the right subtree of the left node to the right side
|
|
628
|
+
let singleR = (key, val, left, right) => {
|
|
629
|
+
let { key: lKey, val: lVal, left: ll, right: lr, _ } = unwrapTree(left)
|
|
630
|
+
makeNode(lKey, lVal, ll, makeNode(key, val, lr, right))
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// node rotation moving right child of left tree to the root
|
|
634
|
+
let doubleR = (key, val, left, right) => {
|
|
635
|
+
let { key: lKey, val: lVal, left: ll, right: lr, _ } = unwrapTree(left)
|
|
636
|
+
let { key: lrKey, val: lrVal, left: lrl, right: lrr, _ } = unwrapTree(lr)
|
|
637
|
+
makeNode(
|
|
638
|
+
lrKey,
|
|
639
|
+
lrVal,
|
|
640
|
+
makeNode(lKey, lVal, ll, lrl),
|
|
641
|
+
makeNode(key, val, lrr, right)
|
|
642
|
+
)
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// creates a new node after either the left or right trees have just had an
|
|
646
|
+
// element inserted or removed from them, maintaining balance in the tree
|
|
647
|
+
let balancedNode = (key, val, left, right) => {
|
|
648
|
+
let makeNodeFn = if (size(left) + size(right) < 2) {
|
|
649
|
+
makeNode
|
|
650
|
+
} else if (size(right) > weight * size(left)) {
|
|
651
|
+
// if the right tree is too much larger than the left then move part of
|
|
652
|
+
// the right tree to the left side
|
|
653
|
+
let { left: rl, right: rr, _ } = unwrapTree(right)
|
|
654
|
+
if (size(rl) < size(rr)) singleL else doubleL
|
|
655
|
+
} else if (size(left) > weight * size(right)) {
|
|
656
|
+
// if the left tree is too much larger than the right then move part of
|
|
657
|
+
// the left tree to the right side
|
|
658
|
+
let { left: ll, right: lr, _ } = unwrapTree(left)
|
|
659
|
+
if (size(lr) < size(ll)) singleR else doubleR
|
|
660
|
+
} else {
|
|
661
|
+
// if neither tree is too much larger than the other then simply create
|
|
662
|
+
// a new node
|
|
663
|
+
makeNode
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
makeNodeFn(key, val, left, right)
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
* Produces a new map containing a new key-value pair. If the key already exists in the map, the value is replaced.
|
|
671
|
+
*
|
|
672
|
+
* @param key: The unique key in the map
|
|
673
|
+
* @param value: The value to store
|
|
674
|
+
* @param map: The base map
|
|
675
|
+
* @returns A new map containing the new key-value pair
|
|
676
|
+
*
|
|
677
|
+
* @since v0.6.0
|
|
678
|
+
* @history v0.5.4: Originally in `"immutablemap"` module
|
|
679
|
+
*/
|
|
680
|
+
provide let rec set = (key, value, map) => {
|
|
681
|
+
match (map) {
|
|
682
|
+
Empty => Tree({ key, val: value, size: 1, left: Empty, right: Empty }),
|
|
683
|
+
Tree({ key: nodeKey, val: nodeVal, left, right, _ }) => {
|
|
684
|
+
match (compare(key, nodeKey)) {
|
|
685
|
+
cmp when cmp < 0 =>
|
|
686
|
+
balancedNode(nodeKey, nodeVal, set(key, value, left), right),
|
|
687
|
+
cmp when cmp > 0 =>
|
|
688
|
+
balancedNode(nodeKey, nodeVal, left, set(key, value, right)),
|
|
689
|
+
_ => makeNode(key, value, left, right),
|
|
690
|
+
}
|
|
691
|
+
},
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
/**
|
|
696
|
+
* Retrieves the value for the given key.
|
|
697
|
+
*
|
|
698
|
+
* @param key: The key to access
|
|
699
|
+
* @param map: The map to access
|
|
700
|
+
* @returns `Some(value)` if the key exists in the map or `None` otherwise
|
|
701
|
+
*
|
|
702
|
+
* @since v0.6.0
|
|
703
|
+
* @history v0.5.4: Originally in `"immutablemap"` module
|
|
704
|
+
*/
|
|
705
|
+
provide let rec get = (key, map) => {
|
|
706
|
+
match (map) {
|
|
707
|
+
Empty => None,
|
|
708
|
+
Tree({ key: nodeKey, val: nodeVal, left, right, _ }) => {
|
|
709
|
+
match (compare(key, nodeKey)) {
|
|
710
|
+
cmp when cmp < 0 => get(key, left),
|
|
711
|
+
cmp when cmp > 0 => get(key, right),
|
|
712
|
+
_ => Some(nodeVal),
|
|
713
|
+
}
|
|
714
|
+
},
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
/**
|
|
719
|
+
* Determines if the map contains the given key. In such a case, it will always contain a value for the given key.
|
|
720
|
+
*
|
|
721
|
+
* @param key: The key to search for
|
|
722
|
+
* @param map: The map to search
|
|
723
|
+
* @returns `true` if the map contains the given key or `false` otherwise
|
|
724
|
+
*
|
|
725
|
+
* @since v0.6.0
|
|
726
|
+
* @history v0.5.4: Originally in `"immutablemap"` module
|
|
727
|
+
*/
|
|
728
|
+
provide let rec contains = (key, map) => {
|
|
729
|
+
Option.isSome(get(key, map))
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
// removes the minimum element from a tree
|
|
733
|
+
let rec removeMin = node => {
|
|
734
|
+
match (node) {
|
|
735
|
+
Tree({ left: Empty, right, _ }) => right,
|
|
736
|
+
Tree({ key, val, left, right, _ }) =>
|
|
737
|
+
balancedNode(key, val, removeMin(left), right),
|
|
738
|
+
_ => fail "Impossible: Map.Immutable removeMin on empty node",
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// helper function for removing a node by creating a new node containing the
|
|
743
|
+
// removed node's left and right subtrees
|
|
744
|
+
let removeInner = (left, right) => {
|
|
745
|
+
match ((left, right)) {
|
|
746
|
+
(Empty, node) | (node, Empty) => node,
|
|
747
|
+
(left, right) => {
|
|
748
|
+
let (minKey, minVal) = min(right)
|
|
749
|
+
balancedNode(minKey, minVal, left, removeMin(right))
|
|
750
|
+
},
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
/**
|
|
755
|
+
* Produces a new map without the key-value pair corresponding to the given
|
|
756
|
+
* key. If the key doesn't exist in the map, the map will be returned unmodified.
|
|
757
|
+
*
|
|
758
|
+
* @param key: The key to exclude
|
|
759
|
+
* @param map: The map to exclude from
|
|
760
|
+
* @returns A new map without the given key
|
|
761
|
+
*
|
|
762
|
+
* @since v0.6.0
|
|
763
|
+
* @history v0.5.4: Originally in `"immutablemap"` module
|
|
764
|
+
*/
|
|
765
|
+
provide let rec remove = (key, map) => {
|
|
766
|
+
match (map) {
|
|
767
|
+
Empty => Empty,
|
|
768
|
+
Tree({ key: nodeKey, val: nodeVal, left, right, _ }) => {
|
|
769
|
+
match (compare(key, nodeKey)) {
|
|
770
|
+
cmp when cmp < 0 =>
|
|
771
|
+
balancedNode(nodeKey, nodeVal, remove(key, left), right),
|
|
772
|
+
cmp when cmp > 0 =>
|
|
773
|
+
balancedNode(nodeKey, nodeVal, left, remove(key, right)),
|
|
774
|
+
_ => removeInner(left, right),
|
|
775
|
+
}
|
|
776
|
+
},
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* Produces a new map by calling an updater function that receives the
|
|
782
|
+
* previously stored value as an `Option` and returns the new value to be
|
|
783
|
+
* stored as an `Option`. If the key didn't exist previously, the value
|
|
784
|
+
* will be `None`. If `None` is returned from the updater function, the
|
|
785
|
+
* key-value pair is excluded.
|
|
786
|
+
*
|
|
787
|
+
* @param key: The unique key in the map
|
|
788
|
+
* @param fn: The updater function
|
|
789
|
+
* @param map: The base map
|
|
790
|
+
* @returns A new map with the value at the given key modified according to the function's output
|
|
791
|
+
*
|
|
792
|
+
* @since v0.6.0
|
|
793
|
+
* @history v0.5.4: Originally in `"immutablemap"` module
|
|
794
|
+
*/
|
|
795
|
+
provide let update = (key, fn, map) => {
|
|
796
|
+
let val = get(key, map)
|
|
797
|
+
match (fn(val)) {
|
|
798
|
+
Some(next) => set(key, next, map),
|
|
799
|
+
None => remove(key, map),
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
/**
|
|
804
|
+
* Iterates the map, calling an iterator function with each key and value.
|
|
805
|
+
*
|
|
806
|
+
* @param fn: The iterator function to call with each key and value
|
|
807
|
+
* @param map: The map to iterate
|
|
808
|
+
*
|
|
809
|
+
* @since v0.6.0
|
|
810
|
+
* @history v0.5.4: Originally in `"immutablemap"` module
|
|
811
|
+
*/
|
|
812
|
+
provide let forEach = (fn, map) => {
|
|
813
|
+
let rec forEachInner = node => {
|
|
814
|
+
match (node) {
|
|
815
|
+
Empty => void,
|
|
816
|
+
Tree({ key, val, left, right, _ }) => {
|
|
817
|
+
forEachInner(left)
|
|
818
|
+
fn(key, val): Void
|
|
819
|
+
forEachInner(right)
|
|
820
|
+
},
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
forEachInner(map)
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
/**
|
|
827
|
+
* Combines all key-value pairs of a map using a reducer function.
|
|
828
|
+
*
|
|
829
|
+
* @param fn: The reducer function to call on each key and value, where the value returned will be the next accumulator value
|
|
830
|
+
* @param init: The initial value to use for the accumulator on the first iteration
|
|
831
|
+
* @param map: The map to iterate
|
|
832
|
+
* @returns The final accumulator returned from `fn`
|
|
833
|
+
*
|
|
834
|
+
* @since v0.6.0
|
|
835
|
+
* @history v0.5.4: Originally in `"immutablemap"` module
|
|
836
|
+
*/
|
|
837
|
+
provide let reduce = (fn, init, map) => {
|
|
838
|
+
let rec reduceInner = (acc, node) => {
|
|
839
|
+
match (node) {
|
|
840
|
+
Empty => acc,
|
|
841
|
+
Tree({ key, val, left, right, _ }) => {
|
|
842
|
+
let newAcc = fn(reduceInner(acc, left), key, val)
|
|
843
|
+
reduceInner(newAcc, right)
|
|
844
|
+
},
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
reduceInner(init, map)
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
// joins two trees with a value, preserving the BST property of left children
|
|
851
|
+
// being less the node and right children being greater than the node
|
|
852
|
+
let rec concat3 = (key, val, left, right) => {
|
|
853
|
+
match ((left, right)) {
|
|
854
|
+
(Empty, node) | (node, Empty) => set(key, val, node),
|
|
855
|
+
(Tree(left) as leftOpt, Tree(right) as rightOpt) => {
|
|
856
|
+
let { size: lSize, key: lKey, left: lLeft, right: lRight, val: lVal } =
|
|
857
|
+
left
|
|
858
|
+
let { size: rSize, key: rKey, left: rLeft, right: rRight, val: rVal } =
|
|
859
|
+
right
|
|
860
|
+
if (weight * lSize < rSize) {
|
|
861
|
+
balancedNode(rKey, rVal, concat3(key, val, leftOpt, rLeft), rRight)
|
|
862
|
+
} else if (weight * rSize < lSize) {
|
|
863
|
+
balancedNode(lKey, lVal, lLeft, concat3(key, val, lRight, rightOpt))
|
|
864
|
+
} else {
|
|
865
|
+
makeNode(key, val, leftOpt, rightOpt)
|
|
866
|
+
}
|
|
867
|
+
},
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
// concatenates two trees of arbitrary size
|
|
872
|
+
let concat = (node1, node2) => {
|
|
873
|
+
match (node2) {
|
|
874
|
+
Empty => node1,
|
|
875
|
+
_ => {
|
|
876
|
+
let (minKey, minVal) = min(node2)
|
|
877
|
+
concat3(minKey, minVal, node1, removeMin(node2))
|
|
878
|
+
},
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
let reduceRight = (fn, init, map) => {
|
|
883
|
+
let rec reduceInner = (acc, node) => {
|
|
884
|
+
match (node) {
|
|
885
|
+
Empty => acc,
|
|
886
|
+
Tree({ key, val, left, right, _ }) => {
|
|
887
|
+
let newAcc = fn(reduceInner(acc, right), key, val)
|
|
888
|
+
reduceInner(newAcc, left)
|
|
889
|
+
},
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
reduceInner(init, map)
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
/**
|
|
896
|
+
* Enumerates all keys in the given map.
|
|
897
|
+
*
|
|
898
|
+
* @param map: The map to enumerate
|
|
899
|
+
* @returns A list containing all keys from the given map
|
|
900
|
+
*
|
|
901
|
+
* @since v0.6.0
|
|
902
|
+
* @history v0.5.4: Originally in `"immutablemap"` module
|
|
903
|
+
*/
|
|
904
|
+
provide let keys = map => {
|
|
905
|
+
reduceRight((list, key, _) => [key, ...list], [], map)
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
/**
|
|
909
|
+
* Enumerates all values in the given map.
|
|
910
|
+
*
|
|
911
|
+
* @param map: The map to enumerate
|
|
912
|
+
* @returns A list containing all values from the given map
|
|
913
|
+
*
|
|
914
|
+
* @since v0.6.0
|
|
915
|
+
* @history v0.5.4: Originally in `"immutablemap"` module
|
|
916
|
+
*/
|
|
917
|
+
provide let values = map => {
|
|
918
|
+
reduceRight((list, _, value) => [value, ...list], [], map)
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
/**
|
|
922
|
+
* Produces a new map excluding the key-value pairs where a predicate function returns `false`.
|
|
923
|
+
*
|
|
924
|
+
* @param fn: The predicate function to indicate which key-value pairs to exclude from the map, where returning `false` indicates the key-value pair should be excluded
|
|
925
|
+
* @param map: The map to iterate
|
|
926
|
+
* @returns A new map excluding the key-value pairs not fulfilling the predicate
|
|
927
|
+
*
|
|
928
|
+
* @since v0.6.0
|
|
929
|
+
* @history v0.5.4: Originally in `"immutablemap"` module
|
|
930
|
+
*/
|
|
931
|
+
provide let filter = (fn, map) => {
|
|
932
|
+
let rec filterInner = node => {
|
|
933
|
+
match (node) {
|
|
934
|
+
Empty => Empty,
|
|
935
|
+
Tree({ key, val, left, right, _ }) => {
|
|
936
|
+
if (fn(key, val)) {
|
|
937
|
+
concat3(key, val, filterInner(left), filterInner(right))
|
|
938
|
+
} else {
|
|
939
|
+
concat(filterInner(left), filterInner(right))
|
|
940
|
+
}
|
|
941
|
+
},
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
filterInner(map)
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
/**
|
|
948
|
+
* Produces a new map excluding the key-value pairs where a predicate function returns `true`.
|
|
949
|
+
*
|
|
950
|
+
* @param fn: The predicate function to indicate which key-value pairs to exclude from the map, where returning `true` indicates the key-value pair should be excluded
|
|
951
|
+
* @param map: The map to iterate
|
|
952
|
+
* @returns A new map excluding the key-value pairs fulfilling the predicate
|
|
953
|
+
*
|
|
954
|
+
* @since v0.6.0
|
|
955
|
+
* @history v0.5.4: Originally in `"immutablemap"` module
|
|
956
|
+
*/
|
|
957
|
+
provide let reject = (fn, map) => {
|
|
958
|
+
filter((key, val) => !fn(key, val), map)
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
/**
|
|
962
|
+
* Creates a map from a list.
|
|
963
|
+
*
|
|
964
|
+
* @param list: The list to convert
|
|
965
|
+
* @returns A map containing all key-value pairs from the list
|
|
966
|
+
*
|
|
967
|
+
* @since v0.6.0
|
|
968
|
+
* @history v0.5.4: Originally in `"immutablemap"` module
|
|
969
|
+
*/
|
|
970
|
+
provide let fromList = list => {
|
|
971
|
+
List.reduce((map, (key, val)) => set(key, val, map), empty, list)
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
/**
|
|
975
|
+
* Enumerates all key-value pairs in the given map.
|
|
976
|
+
*
|
|
977
|
+
* @param map: The map to enumerate
|
|
978
|
+
* @returns A list containing all key-value pairs from the given map
|
|
979
|
+
*
|
|
980
|
+
* @since v0.6.0
|
|
981
|
+
* @history v0.5.4: Originally in `"immutablemap"` module
|
|
982
|
+
*/
|
|
983
|
+
provide let toList = map => {
|
|
984
|
+
reduceRight((list, key, val) => [(key, val), ...list], [], map)
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
/**
|
|
988
|
+
* Creates a map from an array.
|
|
989
|
+
*
|
|
990
|
+
* @param array: The array to convert
|
|
991
|
+
* @returns A map containing all key-value pairs from the array
|
|
992
|
+
*
|
|
993
|
+
* @since v0.6.0
|
|
994
|
+
* @history v0.5.4: Originally in `"immutablemap"` module
|
|
995
|
+
*/
|
|
996
|
+
provide let fromArray = array => {
|
|
997
|
+
Array.reduce((map, (key, val)) => set(key, val, map), empty, array)
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
/**
|
|
1001
|
+
* Converts a map into an array of its key-value pairs.
|
|
1002
|
+
*
|
|
1003
|
+
* @param map: The map to convert
|
|
1004
|
+
* @returns An array containing all key-value pairs from the given map
|
|
1005
|
+
*
|
|
1006
|
+
* @since v0.6.0
|
|
1007
|
+
* @history v0.5.4: Originally in `"immutablemap"` module
|
|
1008
|
+
*/
|
|
1009
|
+
provide let toArray = map => {
|
|
1010
|
+
Array.fromList(toList(map))
|
|
1011
|
+
}
|
|
509
1012
|
}
|