ic-mops 0.8.5 → 0.8.7
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/package.json +3 -1
- package/.mops/base@0.7.4/LICENSE +0 -208
- package/.mops/base@0.7.4/README.md +0 -64
- package/.mops/base@0.7.4/mops.toml +0 -5
- package/.mops/base@0.7.4/src/Array.mo +0 -686
- package/.mops/base@0.7.4/src/AssocList.mo +0 -203
- package/.mops/base@0.7.4/src/Blob.mo +0 -55
- package/.mops/base@0.7.4/src/Bool.mo +0 -44
- package/.mops/base@0.7.4/src/Buffer.mo +0 -1937
- package/.mops/base@0.7.4/src/CertifiedData.mo +0 -29
- package/.mops/base@0.7.4/src/Char.mo +0 -67
- package/.mops/base@0.7.4/src/Debug.mo +0 -15
- package/.mops/base@0.7.4/src/Deque.mo +0 -75
- package/.mops/base@0.7.4/src/Error.mo +0 -41
- package/.mops/base@0.7.4/src/ExperimentalCycles.mo +0 -51
- package/.mops/base@0.7.4/src/ExperimentalInternetComputer.mo +0 -36
- package/.mops/base@0.7.4/src/ExperimentalStableMemory.mo +0 -121
- package/.mops/base@0.7.4/src/Float.mo +0 -150
- package/.mops/base@0.7.4/src/Func.mo +0 -38
- package/.mops/base@0.7.4/src/Hash.mo +0 -83
- package/.mops/base@0.7.4/src/HashMap.mo +0 -229
- package/.mops/base@0.7.4/src/Heap.mo +0 -113
- package/.mops/base@0.7.4/src/Int.mo +0 -150
- package/.mops/base@0.7.4/src/Int16.mo +0 -159
- package/.mops/base@0.7.4/src/Int32.mo +0 -160
- package/.mops/base@0.7.4/src/Int64.mo +0 -161
- package/.mops/base@0.7.4/src/Int8.mo +0 -160
- package/.mops/base@0.7.4/src/Iter.mo +0 -220
- package/.mops/base@0.7.4/src/IterType.mo +0 -7
- package/.mops/base@0.7.4/src/List.mo +0 -433
- package/.mops/base@0.7.4/src/Nat.mo +0 -75
- package/.mops/base@0.7.4/src/Nat16.mo +0 -146
- package/.mops/base@0.7.4/src/Nat32.mo +0 -146
- package/.mops/base@0.7.4/src/Nat64.mo +0 -146
- package/.mops/base@0.7.4/src/Nat8.mo +0 -146
- package/.mops/base@0.7.4/src/None.mo +0 -19
- package/.mops/base@0.7.4/src/Option.mo +0 -160
- package/.mops/base@0.7.4/src/Order.mo +0 -46
- package/.mops/base@0.7.4/src/Prelude.mo +0 -33
- package/.mops/base@0.7.4/src/Principal.mo +0 -58
- package/.mops/base@0.7.4/src/RBTree.mo +0 -218
- package/.mops/base@0.7.4/src/Random.mo +0 -188
- package/.mops/base@0.7.4/src/Result.mo +0 -210
- package/.mops/base@0.7.4/src/Stack.mo +0 -40
- package/.mops/base@0.7.4/src/Text.mo +0 -615
- package/.mops/base@0.7.4/src/Time.mo +0 -37
- package/.mops/base@0.7.4/src/Trie.mo +0 -1200
- package/.mops/base@0.7.4/src/TrieMap.mo +0 -180
- package/.mops/base@0.7.4/src/TrieSet.mo +0 -97
- package/.mops/base@0.8.3/LICENSE +0 -208
- package/.mops/base@0.8.3/README.md +0 -64
- package/.mops/base@0.8.3/mops.toml +0 -6
- package/.mops/base@0.8.3/src/Array.mo +0 -717
- package/.mops/base@0.8.3/src/AssocList.mo +0 -404
- package/.mops/base@0.8.3/src/Blob.mo +0 -212
- package/.mops/base@0.8.3/src/Bool.mo +0 -44
- package/.mops/base@0.8.3/src/Buffer.mo +0 -2660
- package/.mops/base@0.8.3/src/CertifiedData.mo +0 -53
- package/.mops/base@0.8.3/src/Char.mo +0 -65
- package/.mops/base@0.8.3/src/Debug.mo +0 -56
- package/.mops/base@0.8.3/src/Deque.mo +0 -243
- package/.mops/base@0.8.3/src/Error.mo +0 -68
- package/.mops/base@0.8.3/src/ExperimentalCycles.mo +0 -151
- package/.mops/base@0.8.3/src/ExperimentalInternetComputer.mo +0 -60
- package/.mops/base@0.8.3/src/ExperimentalStableMemory.mo +0 -348
- package/.mops/base@0.8.3/src/Float.mo +0 -843
- package/.mops/base@0.8.3/src/Func.mo +0 -46
- package/.mops/base@0.8.3/src/Hash.mo +0 -82
- package/.mops/base@0.8.3/src/HashMap.mo +0 -457
- package/.mops/base@0.8.3/src/Heap.mo +0 -233
- package/.mops/base@0.8.3/src/Int.mo +0 -365
- package/.mops/base@0.8.3/src/Int16.mo +0 -521
- package/.mops/base@0.8.3/src/Int32.mo +0 -522
- package/.mops/base@0.8.3/src/Int64.mo +0 -522
- package/.mops/base@0.8.3/src/Int8.mo +0 -522
- package/.mops/base@0.8.3/src/Iter.mo +0 -227
- package/.mops/base@0.8.3/src/IterType.mo +0 -7
- package/.mops/base@0.8.3/src/List.mo +0 -930
- package/.mops/base@0.8.3/src/Nat.mo +0 -305
- package/.mops/base@0.8.3/src/Nat16.mo +0 -144
- package/.mops/base@0.8.3/src/Nat32.mo +0 -144
- package/.mops/base@0.8.3/src/Nat64.mo +0 -144
- package/.mops/base@0.8.3/src/Nat8.mo +0 -144
- package/.mops/base@0.8.3/src/None.mo +0 -19
- package/.mops/base@0.8.3/src/Option.mo +0 -154
- package/.mops/base@0.8.3/src/Order.mo +0 -46
- package/.mops/base@0.8.3/src/Prelude.mo +0 -33
- package/.mops/base@0.8.3/src/Principal.mo +0 -249
- package/.mops/base@0.8.3/src/RBTree.mo +0 -681
- package/.mops/base@0.8.3/src/Random.mo +0 -270
- package/.mops/base@0.8.3/src/Result.mo +0 -209
- package/.mops/base@0.8.3/src/Stack.mo +0 -93
- package/.mops/base@0.8.3/src/Text.mo +0 -761
- package/.mops/base@0.8.3/src/Time.mo +0 -36
- package/.mops/base@0.8.3/src/Timer.mo +0 -62
- package/.mops/base@0.8.3/src/Trie.mo +0 -1603
- package/.mops/base@0.8.3/src/TrieMap.mo +0 -392
- package/.mops/base@0.8.3/src/TrieSet.mo +0 -148
- package/network.txt +0 -1
|
@@ -1,1603 +0,0 @@
|
|
|
1
|
-
/// Functional key-value hash maps.
|
|
2
|
-
///
|
|
3
|
-
/// Functional maps (and sets) whose representation is "canonical", and
|
|
4
|
-
/// independent of operation history (unlike other popular search trees).
|
|
5
|
-
///
|
|
6
|
-
/// The representation we use here comes from Section 6 of ["Incremental computation via function caching", Pugh & Teitelbaum](https://dl.acm.org/citation.cfm?id=75305).
|
|
7
|
-
///
|
|
8
|
-
/// ## <a name="overview"></a>User's overview
|
|
9
|
-
///
|
|
10
|
-
/// This module provides an applicative (functional) hash map.
|
|
11
|
-
/// Notably, each `put` produces a **new trie _and value being replaced, if any_**.
|
|
12
|
-
///
|
|
13
|
-
/// Those looking for a more familiar (imperative,
|
|
14
|
-
/// object-oriented) hash map should consider `TrieMap` or `HashMap` instead.
|
|
15
|
-
///
|
|
16
|
-
/// The basic `Trie` operations consist of:
|
|
17
|
-
/// - `put` - put a key-value into the trie, producing a new version.
|
|
18
|
-
/// - `get` - get a key's value from the trie, or `null` if none.
|
|
19
|
-
/// - `remove` - remove a key's value from the trie
|
|
20
|
-
/// - `iter` - visit every key-value in the trie.
|
|
21
|
-
///
|
|
22
|
-
/// The `put`, `get` and `remove` operations work over `Key` records,
|
|
23
|
-
/// which group the hash of the key with its non-hash key value.
|
|
24
|
-
///
|
|
25
|
-
/// Example:
|
|
26
|
-
/// ```motoko
|
|
27
|
-
/// import Trie "mo:base/Trie";
|
|
28
|
-
/// import Text "mo:base/Text";
|
|
29
|
-
///
|
|
30
|
-
/// // we do this to have shorter type names and thus
|
|
31
|
-
/// // better readibility
|
|
32
|
-
/// type Trie<K, V> = Trie.Trie<K, V>;
|
|
33
|
-
/// type Key<K> = Trie.Key<K>;
|
|
34
|
-
///
|
|
35
|
-
/// // we have to provide `put`, `get` and `remove` with
|
|
36
|
-
/// // a record of type `Key<K> = { hash : Hash.Hash; key : K }`;
|
|
37
|
-
/// // thus we define the following function that takes a value of type `K`
|
|
38
|
-
/// // (in this case `Text`) and returns a `Key<K>` record.
|
|
39
|
-
/// func key(t: Text) : Key<Text> { { hash = Text.hash t; key = t } };
|
|
40
|
-
///
|
|
41
|
-
/// // we start off by creating an empty `Trie`
|
|
42
|
-
/// let t0 : Trie<Text, Nat> = Trie.empty();
|
|
43
|
-
///
|
|
44
|
-
/// // `put` requires 4 arguments:
|
|
45
|
-
/// // - the trie we want to insert the value into,
|
|
46
|
-
/// // - the key of the value we want to insert (note that we use the `key` function defined above),
|
|
47
|
-
/// // - a function that checks for equality of keys, and
|
|
48
|
-
/// // - the value we want to insert.
|
|
49
|
-
/// //
|
|
50
|
-
/// // When inserting a value, `put` returns a tuple of type `(Trie<K, V>, ?V)`.
|
|
51
|
-
/// // to get the new trie that contains the value, we use the `0` projection
|
|
52
|
-
/// // and assign it to `t1` and `t2` respectively.
|
|
53
|
-
/// let t1 : Trie<Text, Nat> = Trie.put(t0, key "hello", Text.equal, 42).0;
|
|
54
|
-
/// let t2 : Trie<Text, Nat> = Trie.put(t1, key "world", Text.equal, 24).0;
|
|
55
|
-
///
|
|
56
|
-
/// // If for a given key there already was a value in the trie, `put` returns
|
|
57
|
-
/// // that previous value as the second element of the tuple.
|
|
58
|
-
/// // in our case we have already inserted the value 42 for the key "hello", so
|
|
59
|
-
/// // `put` returns 42 as the second element of the tuple.
|
|
60
|
-
/// let (t3, n) : (Trie<Text, Nat>, ?Nat) = Trie.put(
|
|
61
|
-
/// t2,
|
|
62
|
-
/// key "hello",
|
|
63
|
-
/// Text.equal,
|
|
64
|
-
/// 0,
|
|
65
|
-
/// );
|
|
66
|
-
/// assert (n == ?42);
|
|
67
|
-
///
|
|
68
|
-
/// // `get` requires 3 arguments:
|
|
69
|
-
/// // - the trie we want to get the value from
|
|
70
|
-
/// // - the key of the value we want to get (note that we use the `key` function defined above)
|
|
71
|
-
/// // - a function that checks for equality of keys
|
|
72
|
-
/// //
|
|
73
|
-
/// // If the given key is nonexistent in the trie, `get` returns `null`.
|
|
74
|
-
/// var value = Trie.get(t3, key "hello", Text.equal); // Returns `?42`
|
|
75
|
-
/// assert(value == ?0);
|
|
76
|
-
/// value := Trie.get(t3, key "universe", Text.equal); // Returns `null`
|
|
77
|
-
/// assert(value == null);
|
|
78
|
-
///
|
|
79
|
-
/// // `remove` requires 3 arguments:
|
|
80
|
-
/// // - the trie we want to remove the value from,
|
|
81
|
-
/// // - the key of the value we want to remove (note that we use the `key` function defined above), and
|
|
82
|
-
/// // - a function that checks for equality of keys.
|
|
83
|
-
/// //
|
|
84
|
-
/// // In the case of keys of type `Text`, we can use `Text.equal`
|
|
85
|
-
/// // to check for equality of keys. Function `remove` returns a tuple of type `(Trie<K, V>, ?V)`.
|
|
86
|
-
/// // where the second element of the tuple is the value that was removed, or `null` if
|
|
87
|
-
/// // there was no value for the given key.
|
|
88
|
-
/// let removedValue : ?Nat = Trie.remove(
|
|
89
|
-
/// t3,
|
|
90
|
-
/// key "hello",
|
|
91
|
-
/// Text.equal,
|
|
92
|
-
/// ).1;
|
|
93
|
-
/// assert (removedValue == ?0);
|
|
94
|
-
///
|
|
95
|
-
/// // To iterate over the Trie, we use the `iter` function that takes a trie
|
|
96
|
-
/// // of type `Trie<K,V>` and returns an iterator of type `Iter<(K,V)>`:
|
|
97
|
-
/// var sum : Nat = 0;
|
|
98
|
-
/// for (kv in Trie.iter(t3)) {
|
|
99
|
-
/// sum += kv.1;
|
|
100
|
-
/// };
|
|
101
|
-
/// assert(sum == 24);
|
|
102
|
-
/// ```
|
|
103
|
-
|
|
104
|
-
// ## Implementation overview
|
|
105
|
-
//
|
|
106
|
-
// A (hash) trie is a binary tree container for key-value pairs that
|
|
107
|
-
// consists of leaf and branch nodes.
|
|
108
|
-
//
|
|
109
|
-
// Each internal **branch node**
|
|
110
|
-
// represents having distinguished its key-value pairs on a single bit of
|
|
111
|
-
// the keys.
|
|
112
|
-
// By following paths in the trie, we determine an increasingly smaller
|
|
113
|
-
// and smaller subset of the keys.
|
|
114
|
-
//
|
|
115
|
-
// Each **leaf node** consists of an association list of key-value pairs.
|
|
116
|
-
//
|
|
117
|
-
// Each non-empty trie node stores a size; we discuss that more below.
|
|
118
|
-
//
|
|
119
|
-
// ### Adaptive depth
|
|
120
|
-
//
|
|
121
|
-
// We say that a leaf is valid if it contains no more than `MAX_LEAF_SIZE`
|
|
122
|
-
// key-value pairs. When a leaf node grows too large, the
|
|
123
|
-
// binary tree produces a new internal binary node, and splits the leaf into
|
|
124
|
-
// a pair of leaves using an additional bit of their keys' hash strings.
|
|
125
|
-
//
|
|
126
|
-
// For small mappings, the trie structure consists of a single
|
|
127
|
-
// leaf, which contains up to MAX_LEAF_SIZE key-value pairs.
|
|
128
|
-
//
|
|
129
|
-
// ### Cached sizes
|
|
130
|
-
//
|
|
131
|
-
// At each branch and leaf, we use a stored size to support a
|
|
132
|
-
// memory-efficient `toArray` function, which itself relies on
|
|
133
|
-
// per-element projection via `nth`; in turn, `nth` directly uses the
|
|
134
|
-
// O(1)-time function `size` for achieving an acceptable level of
|
|
135
|
-
// algorithmic efficiency. Notably, leaves are generally lists of
|
|
136
|
-
// key-value pairs, and we do not store a size for each Cons cell in the
|
|
137
|
-
// list.
|
|
138
|
-
//
|
|
139
|
-
|
|
140
|
-
import Debug "Debug";
|
|
141
|
-
|
|
142
|
-
import Prim "mo:⛔";
|
|
143
|
-
import P "Prelude";
|
|
144
|
-
import Option "Option";
|
|
145
|
-
import Hash "Hash";
|
|
146
|
-
import A "Array";
|
|
147
|
-
|
|
148
|
-
import List "List";
|
|
149
|
-
import AssocList "AssocList";
|
|
150
|
-
import I "Iter";
|
|
151
|
-
|
|
152
|
-
module {
|
|
153
|
-
|
|
154
|
-
let MAX_LEAF_SIZE = 8; // to do -- further profiling and tuning
|
|
155
|
-
|
|
156
|
-
/// Binary hash tries: either empty, a leaf node, or a branch node
|
|
157
|
-
public type Trie<K, V> = {
|
|
158
|
-
#empty;
|
|
159
|
-
#leaf : Leaf<K, V>;
|
|
160
|
-
#branch : Branch<K, V>
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
/// Leaf nodes of trie consist of key-value pairs as a list.
|
|
164
|
-
public type Leaf<K, V> = {
|
|
165
|
-
size : Nat;
|
|
166
|
-
keyvals : AssocList<Key<K>, V>
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
/// Branch nodes of the trie discriminate on a bit position of the keys' hashes.
|
|
170
|
-
/// we never store this bitpos; rather,
|
|
171
|
-
/// we enforce a style where this position is always known from context.
|
|
172
|
-
public type Branch<K, V> = {
|
|
173
|
-
size : Nat;
|
|
174
|
-
left : Trie<K, V>;
|
|
175
|
-
right : Trie<K, V>
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
public type AssocList<K, V> = AssocList.AssocList<K, V>;
|
|
179
|
-
|
|
180
|
-
/// A `Key` for the trie has an associated hash value
|
|
181
|
-
/// - `hash` permits fast inequality checks, and permits collisions, while
|
|
182
|
-
/// - `key` permits precise equality checks, but is only used on values with equal hashes.
|
|
183
|
-
public type Key<K> = {
|
|
184
|
-
hash : Hash.Hash;
|
|
185
|
-
key : K
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
type List<T> = List.List<T>;
|
|
189
|
-
|
|
190
|
-
/// Equality function for two `Key<K>`s, in terms of equality of `K`'s.
|
|
191
|
-
public func equalKey<K>(keq : (K, K) -> Bool) : ((Key<K>, Key<K>) -> Bool) {
|
|
192
|
-
func(key1 : Key<K>, key2 : Key<K>) : Bool {
|
|
193
|
-
Hash.equal(key1.hash, key2.hash) and keq(key1.key, key2.key)
|
|
194
|
-
}
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
/// @deprecated `isValid` is an internal predicate and will be removed in future.
|
|
198
|
-
public func isValid<K, V>(t : Trie<K, V>, _enforceNormal : Bool) : Bool {
|
|
199
|
-
func rec(t : Trie<K, V>, bitpos : ?Hash.Hash, bits : Hash.Hash, mask : Hash.Hash) : Bool {
|
|
200
|
-
switch t {
|
|
201
|
-
case (#empty) {
|
|
202
|
-
true
|
|
203
|
-
};
|
|
204
|
-
case (#leaf(l)) {
|
|
205
|
-
let len = List.size(l.keyvals);
|
|
206
|
-
len <= MAX_LEAF_SIZE and len == l.size and List.all(
|
|
207
|
-
l.keyvals,
|
|
208
|
-
func((k : Key<K>, v : V)) : Bool { ((k.hash & mask) == bits) }
|
|
209
|
-
)
|
|
210
|
-
};
|
|
211
|
-
case (#branch(b)) {
|
|
212
|
-
let bitpos1 = switch bitpos {
|
|
213
|
-
case null { Prim.natToNat32(0) };
|
|
214
|
-
case (?bp) { Prim.natToNat32(Prim.nat32ToNat(bp) + 1) }
|
|
215
|
-
};
|
|
216
|
-
let mask1 = mask | (Prim.natToNat32(1) << bitpos1);
|
|
217
|
-
let bits1 = bits | (Prim.natToNat32(1) << bitpos1);
|
|
218
|
-
let sum = size(b.left) + size(b.right);
|
|
219
|
-
(b.size == sum) and rec(b.left, ?bitpos1, bits, mask1) and rec(b.right, ?bitpos1, bits1, mask1)
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
};
|
|
223
|
-
rec(t, null, 0, 0)
|
|
224
|
-
};
|
|
225
|
-
|
|
226
|
-
/// A 2D trie maps dimension-1 keys to another
|
|
227
|
-
/// layer of tries, each keyed on the dimension-2 keys.
|
|
228
|
-
public type Trie2D<K1, K2, V> = Trie<K1, Trie<K2, V>>;
|
|
229
|
-
|
|
230
|
-
/// A 3D trie maps dimension-1 keys to another
|
|
231
|
-
/// Composition of 2D tries, each keyed on the dimension-2 and dimension-3 keys.
|
|
232
|
-
public type Trie3D<K1, K2, K3, V> = Trie<K1, Trie2D<K2, K3, V>>;
|
|
233
|
-
|
|
234
|
-
/// An empty trie. This is usually the starting point for building a trie.
|
|
235
|
-
///
|
|
236
|
-
/// Example:
|
|
237
|
-
/// ```motoko name=initialize
|
|
238
|
-
/// import { print } "mo:base/Debug";
|
|
239
|
-
/// import Trie "mo:base/Trie";
|
|
240
|
-
/// import Text "mo:base/Text";
|
|
241
|
-
///
|
|
242
|
-
/// // we do this to have shorter type names and thus
|
|
243
|
-
/// // better readibility
|
|
244
|
-
/// type Trie<K, V> = Trie.Trie<K, V>;
|
|
245
|
-
/// type Key<K> = Trie.Key<K>;
|
|
246
|
-
///
|
|
247
|
-
/// // We have to provide `put`, `get` and `remove` with
|
|
248
|
-
/// // a function of return type `Key<K> = { hash : Hash.Hash; key : K }`
|
|
249
|
-
/// func key(t: Text) : Key<Text> { { hash = Text.hash t; key = t } };
|
|
250
|
-
/// // We start off by creating an empty `Trie`
|
|
251
|
-
/// var trie : Trie<Text, Nat> = Trie.empty();
|
|
252
|
-
/// ```
|
|
253
|
-
public func empty<K, V>() : Trie<K, V> { #empty };
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
/// Get the size in O(1) time.
|
|
257
|
-
///
|
|
258
|
-
/// For a more detailed overview of how to use a `Trie`,
|
|
259
|
-
/// see the [User's Overview](#overview).
|
|
260
|
-
///
|
|
261
|
-
/// Example:
|
|
262
|
-
/// ```motoko include=initialize
|
|
263
|
-
/// var size = Trie.size(trie); // Returns 0, as `trie` is empty
|
|
264
|
-
/// assert(size == 0);
|
|
265
|
-
/// trie := Trie.put(trie, key "hello", Text.equal, 42).0;
|
|
266
|
-
/// size := Trie.size(trie); // Returns 1, as we just added a new entry
|
|
267
|
-
/// assert(size == 1);
|
|
268
|
-
/// ```
|
|
269
|
-
|
|
270
|
-
public func size<K, V>(t : Trie<K, V>) : Nat {
|
|
271
|
-
switch t {
|
|
272
|
-
case (#empty) { 0 };
|
|
273
|
-
case (#leaf(l)) { l.size };
|
|
274
|
-
case (#branch(b)) { b.size }
|
|
275
|
-
}
|
|
276
|
-
};
|
|
277
|
-
|
|
278
|
-
/// Construct a branch node, computing the size stored there.
|
|
279
|
-
public func branch<K, V>(l : Trie<K, V>, r : Trie<K, V>) : Trie<K, V> {
|
|
280
|
-
let sum = size(l) + size(r);
|
|
281
|
-
#branch {
|
|
282
|
-
size = sum;
|
|
283
|
-
left = l;
|
|
284
|
-
right = r
|
|
285
|
-
}
|
|
286
|
-
};
|
|
287
|
-
|
|
288
|
-
/// Construct a leaf node, computing the size stored there.
|
|
289
|
-
///
|
|
290
|
-
/// This helper function automatically enforces the MAX_LEAF_SIZE
|
|
291
|
-
/// by constructing branches as necessary; to do so, it also needs the bitpos
|
|
292
|
-
/// of the leaf.
|
|
293
|
-
public func leaf<K, V>(kvs : AssocList<Key<K>, V>, bitpos : Nat) : Trie<K, V> {
|
|
294
|
-
fromList(null, kvs, bitpos)
|
|
295
|
-
};
|
|
296
|
-
|
|
297
|
-
module ListUtil {
|
|
298
|
-
/* Deprecated: List.lenClamp */
|
|
299
|
-
/// Return the list length unless the number of items in the list exceeds
|
|
300
|
-
/// a maximum value. If the list length exceed the maximum, the function
|
|
301
|
-
/// returns `null`.
|
|
302
|
-
public func lenClamp<T>(l : List<T>, max : Nat) : ?Nat {
|
|
303
|
-
func rec(l : List<T>, max : Nat, i : Nat) : ?Nat {
|
|
304
|
-
switch l {
|
|
305
|
-
case null { ?i };
|
|
306
|
-
case (?(_, t)) {
|
|
307
|
-
if (i >= max) { null } else { rec(t, max, i + 1) }
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
};
|
|
311
|
-
rec(l, max, 0)
|
|
312
|
-
}
|
|
313
|
-
};
|
|
314
|
-
|
|
315
|
-
/// Transform a list into a trie, splitting input list into small (leaf) lists, if necessary.
|
|
316
|
-
public func fromList<K, V>(kvc : ?Nat, kvs : AssocList<Key<K>, V>, bitpos : Nat) : Trie<K, V> {
|
|
317
|
-
func rec(kvc : ?Nat, kvs : AssocList<Key<K>, V>, bitpos : Nat) : Trie<K, V> {
|
|
318
|
-
switch kvc {
|
|
319
|
-
case null {
|
|
320
|
-
switch (ListUtil.lenClamp(kvs, MAX_LEAF_SIZE)) {
|
|
321
|
-
case null {} /* fall through to branch case. */;
|
|
322
|
-
case (?len) {
|
|
323
|
-
return #leaf({ size = len; keyvals = kvs })
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
};
|
|
327
|
-
case (?c) {
|
|
328
|
-
if (c == 0) {
|
|
329
|
-
return #empty
|
|
330
|
-
} else if (c <= MAX_LEAF_SIZE) {
|
|
331
|
-
return #leaf({ size = c; keyvals = kvs })
|
|
332
|
-
} else {
|
|
333
|
-
|
|
334
|
-
//fall through to branch case
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
};
|
|
338
|
-
let (ls, l, rs, r) = splitList(kvs, bitpos);
|
|
339
|
-
if (ls == 0 and rs == 0) {
|
|
340
|
-
#empty
|
|
341
|
-
} else if (rs == 0 and ls <= MAX_LEAF_SIZE) {
|
|
342
|
-
#leaf({ size = ls; keyvals = l })
|
|
343
|
-
} else if (ls == 0 and rs <= MAX_LEAF_SIZE) {
|
|
344
|
-
#leaf({ size = rs; keyvals = r })
|
|
345
|
-
} else {
|
|
346
|
-
branch(rec(?ls, l, bitpos + 1), rec(?rs, r, bitpos + 1))
|
|
347
|
-
}
|
|
348
|
-
};
|
|
349
|
-
rec(kvc, kvs, bitpos)
|
|
350
|
-
};
|
|
351
|
-
|
|
352
|
-
/// Clone the trie efficiently, via sharing.
|
|
353
|
-
///
|
|
354
|
-
/// Purely-functional representation permits _O(1)_ copy, via persistent sharing.
|
|
355
|
-
public func clone<K, V>(t : Trie<K, V>) : Trie<K, V> = t;
|
|
356
|
-
|
|
357
|
-
/// Replace the given key's value option with the given one, returning the previous one
|
|
358
|
-
public func replace<K, V>(t : Trie<K, V>, k : Key<K>, k_eq : (K, K) -> Bool, v : ?V) : (Trie<K, V>, ?V) {
|
|
359
|
-
let key_eq = equalKey(k_eq);
|
|
360
|
-
|
|
361
|
-
func rec(t : Trie<K, V>, bitpos : Nat) : (Trie<K, V>, ?V) {
|
|
362
|
-
switch t {
|
|
363
|
-
case (#empty) {
|
|
364
|
-
let (kvs, _) = AssocList.replace(null, k, key_eq, v);
|
|
365
|
-
(leaf(kvs, bitpos), null)
|
|
366
|
-
};
|
|
367
|
-
case (#branch(b)) {
|
|
368
|
-
let bit = Hash.bit(k.hash, bitpos);
|
|
369
|
-
// rebuild either the left or right path with the (k, v) pair
|
|
370
|
-
if (not bit) {
|
|
371
|
-
let (l, v_) = rec(b.left, bitpos + 1);
|
|
372
|
-
(branch(l, b.right), v_)
|
|
373
|
-
} else {
|
|
374
|
-
let (r, v_) = rec(b.right, bitpos + 1);
|
|
375
|
-
(branch(b.left, r), v_)
|
|
376
|
-
}
|
|
377
|
-
};
|
|
378
|
-
case (#leaf(l)) {
|
|
379
|
-
let (kvs2, old_val) = AssocList.replace(l.keyvals, k, key_eq, v);
|
|
380
|
-
(leaf(kvs2, bitpos), old_val)
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
};
|
|
384
|
-
let (to, vo) = rec(t, 0);
|
|
385
|
-
//assert(isValid<K, V>(to, false));
|
|
386
|
-
(to, vo)
|
|
387
|
-
};
|
|
388
|
-
|
|
389
|
-
/// Put the given key's value in the trie; return the new trie, and the previous value associated with the key, if any.
|
|
390
|
-
///
|
|
391
|
-
/// For a more detailed overview of how to use a `Trie`,
|
|
392
|
-
/// see the [User's Overview](#overview).
|
|
393
|
-
///
|
|
394
|
-
/// Example:
|
|
395
|
-
/// ```motoko include=initialize
|
|
396
|
-
/// trie := Trie.put(trie, key "hello", Text.equal, 42).0;
|
|
397
|
-
/// let previousValue = Trie.put(trie, key "hello", Text.equal, 33).1; // Returns ?42
|
|
398
|
-
/// assert(previousValue == ?42);
|
|
399
|
-
/// ```
|
|
400
|
-
public func put<K, V>(t : Trie<K, V>, k : Key<K>, k_eq : (K, K) -> Bool, v : V) : (Trie<K, V>, ?V) {
|
|
401
|
-
replace(t, k, k_eq, ?v)
|
|
402
|
-
};
|
|
403
|
-
|
|
404
|
-
/// Get the value of the given key in the trie, or return null if nonexistent.
|
|
405
|
-
///
|
|
406
|
-
/// For a more detailed overview of how to use a Trie,
|
|
407
|
-
/// see the [User's Overview](#overview).
|
|
408
|
-
///
|
|
409
|
-
/// Example:
|
|
410
|
-
/// ```motoko include=initialize
|
|
411
|
-
/// trie := Trie.put(trie, key "hello", Text.equal, 42).0;
|
|
412
|
-
/// var value = Trie.get(trie, key "hello", Text.equal); // Returns `?42`
|
|
413
|
-
/// assert(value == ?42);
|
|
414
|
-
/// value := Trie.get(trie, key "world", Text.equal); // Returns `null`
|
|
415
|
-
/// assert(value == null);
|
|
416
|
-
/// ```
|
|
417
|
-
public func get<K, V>(t : Trie<K, V>, k : Key<K>, k_eq : (K, K) -> Bool) : ?V = find(t, k, k_eq);
|
|
418
|
-
|
|
419
|
-
/// Find the given key's value in the trie, or return `null` if nonexistent
|
|
420
|
-
///
|
|
421
|
-
/// For a more detailed overview of how to use a `Trie`,
|
|
422
|
-
/// see the [User's Overview](#overview).
|
|
423
|
-
///
|
|
424
|
-
/// Example:
|
|
425
|
-
/// ```motoko include=initialize
|
|
426
|
-
/// trie := Trie.put(trie, key "hello", Text.equal, 42).0;
|
|
427
|
-
/// var value = Trie.find(trie, key "hello", Text.equal); // Returns `?42`
|
|
428
|
-
/// assert(value == ?42);
|
|
429
|
-
/// value := Trie.find(trie, key "world", Text.equal); // Returns `null`
|
|
430
|
-
/// assert(value == null);
|
|
431
|
-
/// ```
|
|
432
|
-
public func find<K, V>(t : Trie<K, V>, k : Key<K>, k_eq : (K, K) -> Bool) : ?V {
|
|
433
|
-
let key_eq = equalKey(k_eq);
|
|
434
|
-
func rec(t : Trie<K, V>, bitpos : Nat) : ?V {
|
|
435
|
-
switch t {
|
|
436
|
-
case (#empty) { null };
|
|
437
|
-
case (#leaf(l)) {
|
|
438
|
-
AssocList.find(l.keyvals, k, key_eq)
|
|
439
|
-
};
|
|
440
|
-
case (#branch(b)) {
|
|
441
|
-
let bit = Hash.bit(k.hash, bitpos);
|
|
442
|
-
if (not bit) {
|
|
443
|
-
rec(b.left, bitpos + 1)
|
|
444
|
-
} else {
|
|
445
|
-
rec(b.right, bitpos + 1)
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
};
|
|
450
|
-
rec(t, 0)
|
|
451
|
-
};
|
|
452
|
-
|
|
453
|
-
func splitAssocList<K, V>(al : AssocList<Key<K>, V>, bitpos : Nat) : (AssocList<Key<K>, V>, AssocList<Key<K>, V>) {
|
|
454
|
-
List.partition(
|
|
455
|
-
al,
|
|
456
|
-
func((k : Key<K>, v : V)) : Bool {
|
|
457
|
-
not Hash.bit(k.hash, bitpos)
|
|
458
|
-
}
|
|
459
|
-
)
|
|
460
|
-
};
|
|
461
|
-
|
|
462
|
-
func splitList<K, V>(l : AssocList<Key<K>, V>, bitpos : Nat) : (Nat, AssocList<Key<K>, V>, Nat, AssocList<Key<K>, V>) {
|
|
463
|
-
func rec(l : AssocList<Key<K>, V>) : (Nat, AssocList<Key<K>, V>, Nat, AssocList<Key<K>, V>) {
|
|
464
|
-
switch l {
|
|
465
|
-
case null { (0, null, 0, null) };
|
|
466
|
-
case (?((k, v), t)) {
|
|
467
|
-
let (cl, l, cr, r) = rec(t);
|
|
468
|
-
if (not Hash.bit(k.hash, bitpos)) { (cl + 1, ?((k, v), l), cr, r) } else {
|
|
469
|
-
(cl, l, cr + 1, ?((k, v), r))
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
};
|
|
474
|
-
rec(l)
|
|
475
|
-
};
|
|
476
|
-
|
|
477
|
-
/// Merge tries, preferring the left trie where there are collisions
|
|
478
|
-
/// in common keys.
|
|
479
|
-
///
|
|
480
|
-
/// note: the `disj` operation generalizes this `merge`
|
|
481
|
-
/// operation in various ways, and does not (in general) lose
|
|
482
|
-
/// information; this operation is a simpler, special case.
|
|
483
|
-
///
|
|
484
|
-
/// For a more detailed overview of how to use a `Trie`,
|
|
485
|
-
/// see the [User's Overview](#overview).
|
|
486
|
-
///
|
|
487
|
-
/// Example:
|
|
488
|
-
/// ```motoko include=initialize
|
|
489
|
-
/// trie := Trie.put(trie, key "hello", Text.equal, 42).0;
|
|
490
|
-
/// trie := Trie.put(trie, key "bye", Text.equal, 42).0;
|
|
491
|
-
/// // trie2 is a copy of trie
|
|
492
|
-
/// var trie2 = Trie.clone(trie);
|
|
493
|
-
/// // trie2 has a different value for "hello"
|
|
494
|
-
/// trie2 := Trie.put(trie2, key "hello", Text.equal, 33).0;
|
|
495
|
-
/// // mergedTrie has the value 42 for "hello", as the left trie is preferred
|
|
496
|
-
/// // in the case of a collision
|
|
497
|
-
/// var mergedTrie = Trie.merge(trie, trie2, Text.equal);
|
|
498
|
-
/// var value = Trie.get(mergedTrie, key "hello", Text.equal);
|
|
499
|
-
/// assert(value == ?42);
|
|
500
|
-
/// ```
|
|
501
|
-
public func merge<K, V>(tl : Trie<K, V>, tr : Trie<K, V>, k_eq : (K, K) -> Bool) : Trie<K, V> {
|
|
502
|
-
let key_eq = equalKey(k_eq);
|
|
503
|
-
func rec(bitpos : Nat, tl : Trie<K, V>, tr : Trie<K, V>) : Trie<K, V> {
|
|
504
|
-
switch (tl, tr) {
|
|
505
|
-
case (#empty, _) { return tr };
|
|
506
|
-
case (_, #empty) { return tl };
|
|
507
|
-
case (#leaf(l1), #leaf(l2)) {
|
|
508
|
-
leaf(
|
|
509
|
-
AssocList.disj(
|
|
510
|
-
l1.keyvals,
|
|
511
|
-
l2.keyvals,
|
|
512
|
-
key_eq,
|
|
513
|
-
func(x : ?V, y : ?V) : V {
|
|
514
|
-
switch (x, y) {
|
|
515
|
-
case (null, null) { P.unreachable() };
|
|
516
|
-
case (null, ?v) { v };
|
|
517
|
-
case (?v, _) { v }
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
),
|
|
521
|
-
bitpos
|
|
522
|
-
)
|
|
523
|
-
};
|
|
524
|
-
case (#leaf(l), _) {
|
|
525
|
-
let (ll, lr) = splitAssocList(l.keyvals, bitpos);
|
|
526
|
-
rec(bitpos, branch(leaf(ll, bitpos), leaf(lr, bitpos)), tr)
|
|
527
|
-
};
|
|
528
|
-
case (_, #leaf(l)) {
|
|
529
|
-
let (ll, lr) = splitAssocList(l.keyvals, bitpos);
|
|
530
|
-
rec(bitpos, tl, branch(leaf(ll, bitpos), leaf(lr, bitpos)))
|
|
531
|
-
};
|
|
532
|
-
case (#branch(b1), #branch(b2)) {
|
|
533
|
-
branch(
|
|
534
|
-
rec(bitpos + 1, b1.left, b2.left),
|
|
535
|
-
rec(bitpos + 1, b1.right, b2.right)
|
|
536
|
-
)
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
};
|
|
540
|
-
rec(0, tl, tr)
|
|
541
|
-
};
|
|
542
|
-
|
|
543
|
-
/// <a name="mergedisjoint"></a>
|
|
544
|
-
///
|
|
545
|
-
/// Merge tries like `merge`, but traps if there are collisions in common keys between the
|
|
546
|
-
/// left and right inputs.
|
|
547
|
-
///
|
|
548
|
-
/// For a more detailed overview of how to use a `Trie`,
|
|
549
|
-
/// see the [User's Overview](#overview).
|
|
550
|
-
///
|
|
551
|
-
/// Example:
|
|
552
|
-
/// ```motoko include=initialize
|
|
553
|
-
/// trie := Trie.put(trie, key "hello", Text.equal, 42).0;
|
|
554
|
-
/// trie := Trie.put(trie, key "bye", Text.equal, 42).0;
|
|
555
|
-
/// // trie2 is a copy of trie
|
|
556
|
-
/// var trie2 = Trie.clone(trie);
|
|
557
|
-
/// // trie2 has a different value for "hello"
|
|
558
|
-
/// trie2 := Trie.put(trie2, key "hello", Text.equal, 33).0;
|
|
559
|
-
/// // `mergeDisjoint` signals a dynamic errror
|
|
560
|
-
/// // in the case of a collision
|
|
561
|
-
/// var mergedTrie = Trie.mergeDisjoint(trie, trie2, Text.equal);
|
|
562
|
-
/// ```
|
|
563
|
-
public func mergeDisjoint<K, V>(tl : Trie<K, V>, tr : Trie<K, V>, k_eq : (K, K) -> Bool) : Trie<K, V> {
|
|
564
|
-
let key_eq = equalKey(k_eq);
|
|
565
|
-
|
|
566
|
-
func rec(bitpos : Nat, tl : Trie<K, V>, tr : Trie<K, V>) : Trie<K, V> {
|
|
567
|
-
switch (tl, tr) {
|
|
568
|
-
case (#empty, _) { return tr };
|
|
569
|
-
case (_, #empty) { return tl };
|
|
570
|
-
case (#leaf(l1), #leaf(l2)) {
|
|
571
|
-
leaf(
|
|
572
|
-
AssocList.disj(
|
|
573
|
-
l1.keyvals,
|
|
574
|
-
l2.keyvals,
|
|
575
|
-
equalKey(k_eq),
|
|
576
|
-
func(x : ?V, y : ?V) : V {
|
|
577
|
-
switch (x, y) {
|
|
578
|
-
case (null, ?v) { v };
|
|
579
|
-
case (?v, null) { v };
|
|
580
|
-
case (_, _) { Debug.trap "Trie.mergeDisjoint"}
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
),
|
|
584
|
-
bitpos
|
|
585
|
-
)
|
|
586
|
-
};
|
|
587
|
-
case (#leaf(l), _) {
|
|
588
|
-
let (ll, lr) = splitAssocList(l.keyvals, bitpos);
|
|
589
|
-
rec(bitpos, branch(leaf(ll, bitpos), leaf(lr, bitpos)), tr)
|
|
590
|
-
};
|
|
591
|
-
case (_, #leaf(l)) {
|
|
592
|
-
let (ll, lr) = splitAssocList(l.keyvals, bitpos);
|
|
593
|
-
rec(bitpos, tl, branch(leaf(ll, bitpos), leaf(lr, bitpos)))
|
|
594
|
-
};
|
|
595
|
-
case (#branch(b1), #branch(b2)) {
|
|
596
|
-
branch(
|
|
597
|
-
rec(bitpos + 1, b1.left, b2.left),
|
|
598
|
-
rec(bitpos + 1, b1.right, b2.right)
|
|
599
|
-
)
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
};
|
|
603
|
-
rec(0, tl, tr)
|
|
604
|
-
};
|
|
605
|
-
|
|
606
|
-
/// Difference of tries. The output consists of pairs of
|
|
607
|
-
/// the left trie whose keys are not present in the right trie; the
|
|
608
|
-
/// values of the right trie are irrelevant.
|
|
609
|
-
///
|
|
610
|
-
/// For a more detailed overview of how to use a `Trie`,
|
|
611
|
-
/// see the [User's Overview](#overview).
|
|
612
|
-
///
|
|
613
|
-
/// Example:
|
|
614
|
-
/// ```motoko include=initialize
|
|
615
|
-
/// trie := Trie.put(trie, key "hello", Text.equal, 42).0;
|
|
616
|
-
/// trie := Trie.put(trie, key "bye", Text.equal, 42).0;
|
|
617
|
-
/// // trie2 is a copy of trie
|
|
618
|
-
/// var trie2 = Trie.clone(trie);
|
|
619
|
-
/// // trie2 now has an additional key
|
|
620
|
-
/// trie2 := Trie.put(trie2, key "ciao", Text.equal, 33).0;
|
|
621
|
-
/// // `diff` returns a trie with the key "ciao",
|
|
622
|
-
/// // as this key is not present in `trie`
|
|
623
|
-
/// // (note that we pass `trie2` as the left trie)
|
|
624
|
-
/// Trie.diff(trie2, trie, Text.equal);
|
|
625
|
-
/// ```
|
|
626
|
-
public func diff<K, V, W>(tl : Trie<K, V>, tr : Trie<K, W>, k_eq : (K, K) -> Bool) : Trie<K, V> {
|
|
627
|
-
let key_eq = equalKey(k_eq);
|
|
628
|
-
|
|
629
|
-
func rec(bitpos : Nat, tl : Trie<K, V>, tr : Trie<K, W>) : Trie<K, V> {
|
|
630
|
-
switch (tl, tr) {
|
|
631
|
-
case (#empty, _) { return #empty };
|
|
632
|
-
case (_, #empty) { return tl };
|
|
633
|
-
case (#leaf(l1), #leaf(l2)) {
|
|
634
|
-
leaf(
|
|
635
|
-
AssocList.diff(
|
|
636
|
-
l1.keyvals,
|
|
637
|
-
l2.keyvals,
|
|
638
|
-
key_eq
|
|
639
|
-
),
|
|
640
|
-
bitpos
|
|
641
|
-
)
|
|
642
|
-
};
|
|
643
|
-
case (#leaf(l), _) {
|
|
644
|
-
let (ll, lr) = splitAssocList(l.keyvals, bitpos);
|
|
645
|
-
rec(bitpos, branch(leaf(ll, bitpos), leaf(lr, bitpos)), tr)
|
|
646
|
-
};
|
|
647
|
-
case (_, #leaf(l)) {
|
|
648
|
-
let (ll, lr) = splitAssocList(l.keyvals, bitpos);
|
|
649
|
-
rec(bitpos, tl, branch(leaf(ll, bitpos), leaf(lr, bitpos)))
|
|
650
|
-
};
|
|
651
|
-
case (#branch(b1), #branch(b2)) {
|
|
652
|
-
branch(
|
|
653
|
-
rec(bitpos + 1, b1.left, b2.left),
|
|
654
|
-
rec(bitpos + 1, b1.right, b2.right)
|
|
655
|
-
)
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
};
|
|
659
|
-
rec(0, tl, tr)
|
|
660
|
-
};
|
|
661
|
-
|
|
662
|
-
/// Map disjunction.
|
|
663
|
-
///
|
|
664
|
-
/// This operation generalizes the notion of "set union" to finite maps.
|
|
665
|
-
///
|
|
666
|
-
/// Produces a "disjunctive image" of the two tries, where the values of
|
|
667
|
-
/// matching keys are combined with the given binary operator.
|
|
668
|
-
///
|
|
669
|
-
/// For unmatched key-value pairs, the operator is still applied to
|
|
670
|
-
/// create the value in the image. To accomodate these various
|
|
671
|
-
/// situations, the operator accepts optional values, but is never
|
|
672
|
-
/// applied to (null, null).
|
|
673
|
-
///
|
|
674
|
-
/// Implements the database idea of an ["outer join"](https://stackoverflow.com/questions/38549/what-is-the-difference-between-inner-join-and-outer-join).
|
|
675
|
-
///
|
|
676
|
-
public func disj<K, V, W, X>(
|
|
677
|
-
tl : Trie<K, V>,
|
|
678
|
-
tr : Trie<K, W>,
|
|
679
|
-
k_eq : (K, K) -> Bool,
|
|
680
|
-
vbin : (?V, ?W) -> X
|
|
681
|
-
) : Trie<K, X> {
|
|
682
|
-
let key_eq = equalKey(k_eq);
|
|
683
|
-
|
|
684
|
-
/* empty right case; build from left only: */
|
|
685
|
-
func recL(t : Trie<K, V>, bitpos : Nat) : Trie<K, X> {
|
|
686
|
-
switch t {
|
|
687
|
-
case (#empty) { #empty };
|
|
688
|
-
case (#leaf(l)) {
|
|
689
|
-
leaf(AssocList.disj(l.keyvals, null, key_eq, vbin), bitpos)
|
|
690
|
-
};
|
|
691
|
-
case (#branch(b)) {
|
|
692
|
-
branch(
|
|
693
|
-
recL(b.left, bitpos + 1),
|
|
694
|
-
recL(b.right, bitpos + 1)
|
|
695
|
-
)
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
};
|
|
699
|
-
|
|
700
|
-
/* empty left case; build from right only: */
|
|
701
|
-
func recR(t : Trie<K, W>, bitpos : Nat) : Trie<K, X> {
|
|
702
|
-
switch t {
|
|
703
|
-
case (#empty) { #empty };
|
|
704
|
-
case (#leaf(l)) {
|
|
705
|
-
leaf(AssocList.disj(null, l.keyvals, key_eq, vbin), bitpos)
|
|
706
|
-
};
|
|
707
|
-
case (#branch(b)) {
|
|
708
|
-
branch(
|
|
709
|
-
recR(b.left, bitpos + 1),
|
|
710
|
-
recR(b.right, bitpos + 1)
|
|
711
|
-
)
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
};
|
|
715
|
-
|
|
716
|
-
/* main recursion */
|
|
717
|
-
func rec(bitpos : Nat, tl : Trie<K, V>, tr : Trie<K, W>) : Trie<K, X> {
|
|
718
|
-
switch (tl, tr) {
|
|
719
|
-
case (#empty, #empty) { #empty };
|
|
720
|
-
case (#empty, _) { recR(tr, bitpos) };
|
|
721
|
-
case (_, #empty) { recL(tl, bitpos) };
|
|
722
|
-
case (#leaf(l1), #leaf(l2)) {
|
|
723
|
-
leaf(AssocList.disj(l1.keyvals, l2.keyvals, key_eq, vbin), bitpos)
|
|
724
|
-
};
|
|
725
|
-
case (#leaf(l), _) {
|
|
726
|
-
let (ll, lr) = splitAssocList(l.keyvals, bitpos);
|
|
727
|
-
rec(bitpos, branch(leaf(ll, bitpos), leaf(lr, bitpos)), tr)
|
|
728
|
-
};
|
|
729
|
-
case (_, #leaf(l)) {
|
|
730
|
-
let (ll, lr) = splitAssocList(l.keyvals, bitpos);
|
|
731
|
-
rec(bitpos, tl, branch(leaf(ll, bitpos), leaf(lr, bitpos)))
|
|
732
|
-
};
|
|
733
|
-
case (#branch(b1), #branch(b2)) {
|
|
734
|
-
branch(
|
|
735
|
-
rec(bitpos + 1, b1.left, b2.left),
|
|
736
|
-
rec(bitpos + 1, b1.right, b2.right)
|
|
737
|
-
)
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
};
|
|
741
|
-
|
|
742
|
-
rec(0, tl, tr)
|
|
743
|
-
};
|
|
744
|
-
|
|
745
|
-
/// Map join.
|
|
746
|
-
///
|
|
747
|
-
/// Implements the database idea of an ["inner join"](https://stackoverflow.com/questions/38549/what-is-the-difference-between-inner-join-and-outer-join).
|
|
748
|
-
///
|
|
749
|
-
/// This operation generalizes the notion of "set intersection" to
|
|
750
|
-
/// finite maps. The values of matching keys are combined with the given binary
|
|
751
|
-
/// operator, and unmatched key-value pairs are not present in the output.
|
|
752
|
-
///
|
|
753
|
-
public func join<K, V, W, X>(
|
|
754
|
-
tl : Trie<K, V>,
|
|
755
|
-
tr : Trie<K, W>,
|
|
756
|
-
k_eq : (K, K) -> Bool,
|
|
757
|
-
vbin : (V, W) -> X
|
|
758
|
-
) : Trie<K, X> {
|
|
759
|
-
let key_eq = equalKey(k_eq);
|
|
760
|
-
|
|
761
|
-
func rec(bitpos : Nat, tl : Trie<K, V>, tr : Trie<K, W>) : Trie<K, X> {
|
|
762
|
-
switch (tl, tr) {
|
|
763
|
-
case (#empty, _) { #empty };
|
|
764
|
-
case (_, #empty) { #empty };
|
|
765
|
-
case (#leaf(l1), #leaf(l2)) {
|
|
766
|
-
leaf(AssocList.join(l1.keyvals, l2.keyvals, key_eq, vbin), bitpos)
|
|
767
|
-
};
|
|
768
|
-
case (#leaf(l), _) {
|
|
769
|
-
let (ll, lr) = splitAssocList(l.keyvals, bitpos);
|
|
770
|
-
rec(bitpos, branch(leaf(ll, bitpos), leaf(lr, bitpos)), tr)
|
|
771
|
-
};
|
|
772
|
-
case (_, #leaf(l)) {
|
|
773
|
-
let (ll, lr) = splitAssocList(l.keyvals, bitpos);
|
|
774
|
-
rec(bitpos, tl, branch(leaf(ll, bitpos), leaf(lr, bitpos)))
|
|
775
|
-
};
|
|
776
|
-
case (#branch(b1), #branch(b2)) {
|
|
777
|
-
branch(
|
|
778
|
-
rec(bitpos + 1, b1.left, b2.left),
|
|
779
|
-
rec(bitpos + 1, b1.right, b2.right)
|
|
780
|
-
)
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
};
|
|
784
|
-
|
|
785
|
-
rec(0, tl, tr)
|
|
786
|
-
};
|
|
787
|
-
|
|
788
|
-
/// This operation gives a recursor for the internal structure of
|
|
789
|
-
/// tries. Many common operations are instantiations of this function,
|
|
790
|
-
/// either as clients, or as hand-specialized versions (e.g., see , map,
|
|
791
|
-
/// mapFilter, some and all below).
|
|
792
|
-
public func foldUp<K, V, X>(t : Trie<K, V>, bin : (X, X) -> X, leaf : (K, V) -> X, empty : X) : X {
|
|
793
|
-
func rec(t : Trie<K, V>) : X {
|
|
794
|
-
switch t {
|
|
795
|
-
case (#empty) { empty };
|
|
796
|
-
case (#leaf(l)) {
|
|
797
|
-
AssocList.fold(
|
|
798
|
-
l.keyvals,
|
|
799
|
-
empty,
|
|
800
|
-
func(k : Key<K>, v : V, x : X) : X { bin(leaf(k.key, v), x) }
|
|
801
|
-
)
|
|
802
|
-
};
|
|
803
|
-
case (#branch(b)) { bin(rec(b.left), rec(b.right)) }
|
|
804
|
-
}
|
|
805
|
-
};
|
|
806
|
-
rec(t)
|
|
807
|
-
};
|
|
808
|
-
|
|
809
|
-
/// Map product.
|
|
810
|
-
///
|
|
811
|
-
/// Conditional _catesian product_, where the given
|
|
812
|
-
/// operation `op` _conditionally_ creates output elements in the
|
|
813
|
-
/// resulting trie.
|
|
814
|
-
///
|
|
815
|
-
/// The keyed structure of the input tries are not relevant for this
|
|
816
|
-
/// operation: all pairs are considered, regardless of keys matching or
|
|
817
|
-
/// not. Moreover, the resulting trie may use keys that are unrelated to
|
|
818
|
-
/// these input keys.
|
|
819
|
-
///
|
|
820
|
-
public func prod<K1, V1, K2, V2, K3, V3>(
|
|
821
|
-
tl : Trie<K1, V1>,
|
|
822
|
-
tr : Trie<K2, V2>,
|
|
823
|
-
op : (K1, V1, K2, V2) -> ?(Key<K3>, V3),
|
|
824
|
-
k3_eq : (K3, K3) -> Bool
|
|
825
|
-
) : Trie<K3, V3> {
|
|
826
|
-
|
|
827
|
-
/*- binary case: merge disjoint results: */
|
|
828
|
-
func merge(a : Trie<K3, V3>, b : Trie<K3, V3>) : Trie<K3, V3> = mergeDisjoint(a, b, k3_eq);
|
|
829
|
-
|
|
830
|
-
/*- "`foldUp` squared" (imagine two nested loops): */
|
|
831
|
-
foldUp(
|
|
832
|
-
tl,
|
|
833
|
-
merge,
|
|
834
|
-
func(k1 : K1, v1 : V1) : Trie<K3, V3> {
|
|
835
|
-
foldUp(
|
|
836
|
-
tr,
|
|
837
|
-
merge,
|
|
838
|
-
func(k2 : K2, v2 : V2) : Trie<K3, V3> {
|
|
839
|
-
switch (op(k1, v1, k2, v2)) {
|
|
840
|
-
case null { #empty };
|
|
841
|
-
case (?(k3, v3)) { (put(#empty, k3, k3_eq, v3)).0 }
|
|
842
|
-
}
|
|
843
|
-
},
|
|
844
|
-
#empty
|
|
845
|
-
)
|
|
846
|
-
},
|
|
847
|
-
#empty
|
|
848
|
-
)
|
|
849
|
-
};
|
|
850
|
-
|
|
851
|
-
/// Returns an iterator of type `Iter` over the key-value entries of the trie.
|
|
852
|
-
///
|
|
853
|
-
/// Each iterator gets a _persistent view_ of the mapping, independent of concurrent updates to the iterated map.
|
|
854
|
-
///
|
|
855
|
-
/// For a more detailed overview of how to use a `Trie`,
|
|
856
|
-
/// see the [User's Overview](#overview).
|
|
857
|
-
///
|
|
858
|
-
/// Example:
|
|
859
|
-
/// ```motoko include=initialize
|
|
860
|
-
/// trie := Trie.put(trie, key "hello", Text.equal, 42).0;
|
|
861
|
-
/// trie := Trie.put(trie, key "bye", Text.equal, 32).0;
|
|
862
|
-
/// // create an Iterator over key-value pairs of trie
|
|
863
|
-
/// let iter = Trie.iter(trie);
|
|
864
|
-
/// // add another key-value pair to `trie`.
|
|
865
|
-
/// // because we created our iterator before
|
|
866
|
-
/// // this update, it will not contain this new key-value pair
|
|
867
|
-
/// trie := Trie.put(trie, key "ciao", Text.equal, 3).0;
|
|
868
|
-
/// var sum : Nat = 0;
|
|
869
|
-
/// for ((k,v) in iter) {
|
|
870
|
-
/// sum += v;
|
|
871
|
-
/// };
|
|
872
|
-
/// assert(sum == 74);
|
|
873
|
-
/// ```
|
|
874
|
-
public func iter<K, V>(t : Trie<K, V>) : I.Iter<(K, V)> {
|
|
875
|
-
object {
|
|
876
|
-
var stack = ?(t, null) : List.List<Trie<K, V>>;
|
|
877
|
-
public func next() : ?(K, V) {
|
|
878
|
-
switch stack {
|
|
879
|
-
case null { null };
|
|
880
|
-
case (?(trie, stack2)) {
|
|
881
|
-
switch trie {
|
|
882
|
-
case (#empty) {
|
|
883
|
-
stack := stack2;
|
|
884
|
-
next()
|
|
885
|
-
};
|
|
886
|
-
case (#leaf({ keyvals = null })) {
|
|
887
|
-
stack := stack2;
|
|
888
|
-
next()
|
|
889
|
-
};
|
|
890
|
-
case (#leaf({ size = c; keyvals = ?((k, v), kvs) })) {
|
|
891
|
-
stack := ?(#leaf({ size = c -1; keyvals = kvs }), stack2);
|
|
892
|
-
?(k.key, v)
|
|
893
|
-
};
|
|
894
|
-
case (#branch(br)) {
|
|
895
|
-
stack := ?(br.left, ?(br.right, stack2));
|
|
896
|
-
next()
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
|
-
}
|
|
903
|
-
};
|
|
904
|
-
|
|
905
|
-
/// Represent the construction of tries as data.
|
|
906
|
-
///
|
|
907
|
-
/// This module provides optimized variants of normal tries, for
|
|
908
|
-
/// more efficient join queries.
|
|
909
|
-
///
|
|
910
|
-
/// The central insight is that for (unmaterialized) join query results, we
|
|
911
|
-
/// do not need to actually build any resulting trie of the resulting
|
|
912
|
-
/// data, but rather, just need a collection of what would be in that
|
|
913
|
-
/// trie. Since query results can be large (quadratic in the DB size),
|
|
914
|
-
/// avoiding the construction of this trie provides a considerable savings.
|
|
915
|
-
///
|
|
916
|
-
/// To get this savings, we use an ADT for the operations that _would_ build this trie,
|
|
917
|
-
/// if evaluated. This structure specializes a rope: a balanced tree representing a
|
|
918
|
-
/// sequence. It is only as balanced as the tries from which we generate
|
|
919
|
-
/// these build ASTs. They have no intrinsic balance properties of their
|
|
920
|
-
/// own.
|
|
921
|
-
///
|
|
922
|
-
public module Build {
|
|
923
|
-
/// The build of a trie, as an AST for a simple DSL.
|
|
924
|
-
public type Build<K, V> = {
|
|
925
|
-
#skip;
|
|
926
|
-
#put : (K, ?Hash.Hash, V);
|
|
927
|
-
#seq : {
|
|
928
|
-
size : Nat;
|
|
929
|
-
left : Build<K, V>;
|
|
930
|
-
right : Build<K, V>
|
|
931
|
-
}
|
|
932
|
-
};
|
|
933
|
-
|
|
934
|
-
/// Size of the build, measured in `#put` operations
|
|
935
|
-
public func size<K, V>(tb : Build<K, V>) : Nat {
|
|
936
|
-
switch tb {
|
|
937
|
-
case (#skip) { 0 };
|
|
938
|
-
case (#put(_, _, _)) { 1 };
|
|
939
|
-
case (#seq(seq)) { seq.size }
|
|
940
|
-
}
|
|
941
|
-
};
|
|
942
|
-
|
|
943
|
-
/// Build sequence of two sub-builds
|
|
944
|
-
public func seq<K, V>(l : Build<K, V>, r : Build<K, V>) : Build<K, V> {
|
|
945
|
-
let sum = size(l) + size(r);
|
|
946
|
-
#seq({ size = sum; left = l; right = r })
|
|
947
|
-
};
|
|
948
|
-
|
|
949
|
-
/// Like [`prod`](#prod), except do not actually do the put calls, just
|
|
950
|
-
/// record them, as a (binary tree) data structure, isomorphic to the
|
|
951
|
-
/// recursion of this function (which is balanced, in expectation).
|
|
952
|
-
public func prod<K1, V1, K2, V2, K3, V3>(
|
|
953
|
-
tl : Trie<K1, V1>,
|
|
954
|
-
tr : Trie<K2, V2>,
|
|
955
|
-
op : (K1, V1, K2, V2) -> ?(K3, V3),
|
|
956
|
-
k3_eq : (K3, K3) -> Bool
|
|
957
|
-
) : Build<K3, V3> {
|
|
958
|
-
|
|
959
|
-
func outer_bin(a : Build<K3, V3>, b : Build<K3, V3>) : Build<K3, V3> {
|
|
960
|
-
seq(a, b)
|
|
961
|
-
};
|
|
962
|
-
|
|
963
|
-
func inner_bin(a : Build<K3, V3>, b : Build<K3, V3>) : Build<K3, V3> {
|
|
964
|
-
seq(a, b)
|
|
965
|
-
};
|
|
966
|
-
|
|
967
|
-
/// double-nested folds
|
|
968
|
-
foldUp(
|
|
969
|
-
tl,
|
|
970
|
-
outer_bin,
|
|
971
|
-
func(k1 : K1, v1 : V1) : Build<K3, V3> {
|
|
972
|
-
foldUp(
|
|
973
|
-
tr,
|
|
974
|
-
inner_bin,
|
|
975
|
-
func(k2 : K2, v2 : V2) : Build<K3, V3> {
|
|
976
|
-
switch (op(k1, v1, k2, v2)) {
|
|
977
|
-
case null { #skip };
|
|
978
|
-
case (?(k3, v3)) { #put(k3, null, v3) }
|
|
979
|
-
}
|
|
980
|
-
},
|
|
981
|
-
#skip
|
|
982
|
-
)
|
|
983
|
-
},
|
|
984
|
-
#skip
|
|
985
|
-
)
|
|
986
|
-
};
|
|
987
|
-
|
|
988
|
-
/// Project the nth key-value pair from the trie build.
|
|
989
|
-
///
|
|
990
|
-
/// This position is meaningful only when the build contains multiple uses of one or more keys, otherwise it is not.
|
|
991
|
-
public func nth<K, V>(tb : Build<K, V>, i : Nat) : ?(K, ?Hash.Hash, V) {
|
|
992
|
-
func rec(tb : Build<K, V>, i : Nat) : ?(K, ?Hash.Hash, V) {
|
|
993
|
-
switch tb {
|
|
994
|
-
case (#skip) { P.unreachable() };
|
|
995
|
-
case (#put(k, h, v)) {
|
|
996
|
-
assert (i == 0);
|
|
997
|
-
?(k, h, v)
|
|
998
|
-
};
|
|
999
|
-
case (#seq(s)) {
|
|
1000
|
-
let size_left = size(s.left);
|
|
1001
|
-
if (i < size_left) { rec(s.left, i) } else {
|
|
1002
|
-
rec(s.right, i - size_left)
|
|
1003
|
-
}
|
|
1004
|
-
}
|
|
1005
|
-
}
|
|
1006
|
-
};
|
|
1007
|
-
|
|
1008
|
-
if (i >= size(tb)) {
|
|
1009
|
-
return null
|
|
1010
|
-
};
|
|
1011
|
-
rec(tb, i)
|
|
1012
|
-
};
|
|
1013
|
-
|
|
1014
|
-
/// Like [`mergeDisjoint`](#mergedisjoint), except that it avoids the
|
|
1015
|
-
/// work of actually merging any tries; rather, just record the work for
|
|
1016
|
-
/// latter (if ever).
|
|
1017
|
-
public func projectInner<K1, K2, V>(t : Trie<K1, Build<K2, V>>) : Build<K2, V> {
|
|
1018
|
-
foldUp(
|
|
1019
|
-
t,
|
|
1020
|
-
func(t1 : Build<K2, V>, t2 : Build<K2, V>) : Build<K2, V> {
|
|
1021
|
-
seq(t1, t2)
|
|
1022
|
-
},
|
|
1023
|
-
func(_ : K1, t : Build<K2, V>) : Build<K2, V> { t },
|
|
1024
|
-
#skip
|
|
1025
|
-
)
|
|
1026
|
-
};
|
|
1027
|
-
|
|
1028
|
-
/// Gather the collection of key-value pairs into an array of a (possibly-distinct) type.
|
|
1029
|
-
public func toArray<K, V, W>(tb : Build<K, V>, f : (K, V) -> W) : [W] {
|
|
1030
|
-
let c = size(tb);
|
|
1031
|
-
let a = A.init<?W>(c, null);
|
|
1032
|
-
var i = 0;
|
|
1033
|
-
func rec(tb : Build<K, V>) {
|
|
1034
|
-
switch tb {
|
|
1035
|
-
case (#skip) {};
|
|
1036
|
-
case (#put(k, _, v)) { a[i] := ?f(k, v); i := i + 1 };
|
|
1037
|
-
case (#seq(s)) { rec(s.left); rec(s.right) }
|
|
1038
|
-
}
|
|
1039
|
-
};
|
|
1040
|
-
rec(tb);
|
|
1041
|
-
A.tabulate(
|
|
1042
|
-
c,
|
|
1043
|
-
func(i : Nat) : W {
|
|
1044
|
-
switch (a[i]) {
|
|
1045
|
-
case null { P.unreachable() };
|
|
1046
|
-
case (?x) { x }
|
|
1047
|
-
}
|
|
1048
|
-
}
|
|
1049
|
-
)
|
|
1050
|
-
};
|
|
1051
|
-
|
|
1052
|
-
};
|
|
1053
|
-
|
|
1054
|
-
/// Fold over the key-value pairs of the trie, using an accumulator.
|
|
1055
|
-
/// The key-value pairs have no reliable or meaningful ordering.
|
|
1056
|
-
///
|
|
1057
|
-
/// For a more detailed overview of how to use a `Trie`,
|
|
1058
|
-
/// see the [User's Overview](#overview).
|
|
1059
|
-
///
|
|
1060
|
-
/// Example:
|
|
1061
|
-
/// ```motoko include=initialize
|
|
1062
|
-
/// trie := Trie.put(trie, key "hello", Text.equal, 42).0;
|
|
1063
|
-
/// trie := Trie.put(trie, key "bye", Text.equal, 32).0;
|
|
1064
|
-
/// trie := Trie.put(trie, key "ciao", Text.equal, 3).0;
|
|
1065
|
-
/// // create an accumulator, in our case the sum of all values
|
|
1066
|
-
/// func calculateSum(k : Text, v : Nat, acc : Nat) : Nat = acc + v;
|
|
1067
|
-
/// // Fold over the trie using the accumulator.
|
|
1068
|
-
/// // Note that 0 is the initial value of the accumulator.
|
|
1069
|
-
/// let sum = Trie.fold(trie, calculateSum, 0);
|
|
1070
|
-
/// assert(sum == 77);
|
|
1071
|
-
/// ```
|
|
1072
|
-
public func fold<K, V, X>(t : Trie<K, V>, f : (K, V, X) -> X, x : X) : X {
|
|
1073
|
-
func rec(t : Trie<K, V>, x : X) : X {
|
|
1074
|
-
switch t {
|
|
1075
|
-
case (#empty) { x };
|
|
1076
|
-
case (#leaf(l)) {
|
|
1077
|
-
AssocList.fold(
|
|
1078
|
-
l.keyvals,
|
|
1079
|
-
x,
|
|
1080
|
-
func(k : Key<K>, v : V, x : X) : X = f(k.key, v, x)
|
|
1081
|
-
)
|
|
1082
|
-
};
|
|
1083
|
-
case (#branch(b)) { rec(b.left, rec(b.right, x)) }
|
|
1084
|
-
}
|
|
1085
|
-
};
|
|
1086
|
-
rec(t, x)
|
|
1087
|
-
};
|
|
1088
|
-
|
|
1089
|
-
/// Test whether a given key-value pair is present, or not.
|
|
1090
|
-
///
|
|
1091
|
-
/// For a more detailed overview of how to use a `Trie`,
|
|
1092
|
-
/// see the [User's Overview](#overview).
|
|
1093
|
-
///
|
|
1094
|
-
/// Example:
|
|
1095
|
-
/// ```motoko include=initialize
|
|
1096
|
-
/// trie := Trie.put(trie, key "hello", Text.equal, 42).0;
|
|
1097
|
-
/// trie := Trie.put(trie, key "bye", Text.equal, 32).0;
|
|
1098
|
-
/// trie := Trie.put(trie, key "ciao", Text.equal, 3).0;
|
|
1099
|
-
/// // `some` takes a function that returns a Boolean indicating whether
|
|
1100
|
-
/// // the key-value pair is present or not
|
|
1101
|
-
/// var isPresent = Trie.some(
|
|
1102
|
-
/// trie,
|
|
1103
|
-
/// func(k : Text, v : Nat) : Bool = k == "bye" and v == 32,
|
|
1104
|
-
/// );
|
|
1105
|
-
/// assert(isPresent == true);
|
|
1106
|
-
/// isPresent := Trie.some(
|
|
1107
|
-
/// trie,
|
|
1108
|
-
/// func(k : Text, v : Nat) : Bool = k == "hello" and v == 32,
|
|
1109
|
-
/// );
|
|
1110
|
-
/// assert(isPresent == false);
|
|
1111
|
-
/// ```
|
|
1112
|
-
public func some<K, V>(t : Trie<K, V>, f : (K, V) -> Bool) : Bool {
|
|
1113
|
-
func rec(t : Trie<K, V>) : Bool {
|
|
1114
|
-
switch t {
|
|
1115
|
-
case (#empty) { false };
|
|
1116
|
-
case (#leaf(l)) {
|
|
1117
|
-
List.some(
|
|
1118
|
-
l.keyvals,
|
|
1119
|
-
func((k : Key<K>, v : V)) : Bool = f(k.key, v)
|
|
1120
|
-
)
|
|
1121
|
-
};
|
|
1122
|
-
case (#branch(b)) { rec(b.left) or rec(b.right) }
|
|
1123
|
-
}
|
|
1124
|
-
};
|
|
1125
|
-
rec(t)
|
|
1126
|
-
};
|
|
1127
|
-
|
|
1128
|
-
/// Test whether all key-value pairs have a given property.
|
|
1129
|
-
///
|
|
1130
|
-
/// For a more detailed overview of how to use a `Trie`,
|
|
1131
|
-
/// see the [User's Overview](#overview).
|
|
1132
|
-
///
|
|
1133
|
-
/// Example:
|
|
1134
|
-
/// ```motoko include=initialize
|
|
1135
|
-
/// trie := Trie.put(trie, key "hello", Text.equal, 42).0;
|
|
1136
|
-
/// trie := Trie.put(trie, key "bye", Text.equal, 32).0;
|
|
1137
|
-
/// trie := Trie.put(trie, key "ciao", Text.equal, 10).0;
|
|
1138
|
-
/// // `all` takes a function that returns a boolean indicating whether
|
|
1139
|
-
/// // the key-value pairs all have a given property, in our case that
|
|
1140
|
-
/// // all values are greater than 9
|
|
1141
|
-
/// var hasProperty = Trie.all(
|
|
1142
|
-
/// trie,
|
|
1143
|
-
/// func(k : Text, v : Nat) : Bool = v > 9,
|
|
1144
|
-
/// );
|
|
1145
|
-
/// assert(hasProperty == true);
|
|
1146
|
-
/// // now we check if all values are greater than 100
|
|
1147
|
-
/// hasProperty := Trie.all(
|
|
1148
|
-
/// trie,
|
|
1149
|
-
/// func(k : Text, v : Nat) : Bool = v > 100,
|
|
1150
|
-
/// );
|
|
1151
|
-
/// assert(hasProperty == false);
|
|
1152
|
-
/// ```
|
|
1153
|
-
public func all<K, V>(t : Trie<K, V>, f : (K, V) -> Bool) : Bool {
|
|
1154
|
-
func rec(t : Trie<K, V>) : Bool {
|
|
1155
|
-
switch t {
|
|
1156
|
-
case (#empty) { true };
|
|
1157
|
-
case (#leaf(l)) {
|
|
1158
|
-
List.all(
|
|
1159
|
-
l.keyvals,
|
|
1160
|
-
func((k : Key<K>, v : V)) : Bool = f(k.key, v)
|
|
1161
|
-
)
|
|
1162
|
-
};
|
|
1163
|
-
case (#branch(b)) { rec(b.left) and rec(b.right) }
|
|
1164
|
-
}
|
|
1165
|
-
};
|
|
1166
|
-
rec(t)
|
|
1167
|
-
};
|
|
1168
|
-
|
|
1169
|
-
/// Project the nth key-value pair from the trie.
|
|
1170
|
-
///
|
|
1171
|
-
/// Note: This position is not meaningful; it's only here so that we
|
|
1172
|
-
/// can inject tries into arrays using functions like `Array.tabulate`.
|
|
1173
|
-
///
|
|
1174
|
-
/// For a more detailed overview of how to use a `Trie`,
|
|
1175
|
-
/// see the [User's Overview](#overview).
|
|
1176
|
-
///
|
|
1177
|
-
/// Example:
|
|
1178
|
-
/// ```motoko include=initialize
|
|
1179
|
-
/// import Array "mo:base/Array";
|
|
1180
|
-
/// trie := Trie.put(trie, key "hello", Text.equal, 42).0;
|
|
1181
|
-
/// trie := Trie.put(trie, key "bye", Text.equal, 32).0;
|
|
1182
|
-
/// trie := Trie.put(trie, key "ciao", Text.equal, 10).0;
|
|
1183
|
-
/// // `tabulate` takes a size parameter, so we check the size of
|
|
1184
|
-
/// // the trie first
|
|
1185
|
-
/// let size = Trie.size(trie);
|
|
1186
|
-
/// // Now we can create an array of the same size passing `nth` as
|
|
1187
|
-
/// // the generator used to fill the array.
|
|
1188
|
-
/// // Note that `toArray` is a convenience function that does the
|
|
1189
|
-
/// // same thing without you having to check whether the tuple is
|
|
1190
|
-
/// // `null` or not, which we're not doing in this example
|
|
1191
|
-
/// let array = Array.tabulate<?(Key<Text>, Nat)>(
|
|
1192
|
-
/// size,
|
|
1193
|
-
/// func n = Trie.nth(trie, n)
|
|
1194
|
-
/// );
|
|
1195
|
-
/// ```
|
|
1196
|
-
public func nth<K, V>(t : Trie<K, V>, i : Nat) : ?(Key<K>, V) {
|
|
1197
|
-
func rec(t : Trie<K, V>, i : Nat) : ?(Key<K>, V) {
|
|
1198
|
-
switch t {
|
|
1199
|
-
case (#empty) { P.unreachable() };
|
|
1200
|
-
case (#leaf(l)) { List.get(l.keyvals, i) };
|
|
1201
|
-
case (#branch(b)) {
|
|
1202
|
-
let size_left = size(b.left);
|
|
1203
|
-
if (i < size_left) { rec(b.left, i) } else {
|
|
1204
|
-
rec(b.right, i - size_left)
|
|
1205
|
-
}
|
|
1206
|
-
}
|
|
1207
|
-
}
|
|
1208
|
-
};
|
|
1209
|
-
if (i >= size(t)) {
|
|
1210
|
-
return null
|
|
1211
|
-
};
|
|
1212
|
-
rec(t, i)
|
|
1213
|
-
};
|
|
1214
|
-
|
|
1215
|
-
/// Gather the collection of key-value pairs into an array of a (possibly-distinct) type.
|
|
1216
|
-
///
|
|
1217
|
-
/// For a more detailed overview of how to use a `Trie`,
|
|
1218
|
-
/// see the [User's Overview](#overview).
|
|
1219
|
-
///
|
|
1220
|
-
/// Example:
|
|
1221
|
-
/// ```motoko include=initialize
|
|
1222
|
-
/// trie := Trie.put(trie, key "hello", Text.equal, 42).0;
|
|
1223
|
-
/// trie := Trie.put(trie, key "bye", Text.equal, 32).0;
|
|
1224
|
-
/// trie := Trie.put(trie, key "ciao", Text.equal, 10).0;
|
|
1225
|
-
/// // `toArray` takes a function that takes a key-value tuple
|
|
1226
|
-
/// // and returns a value of the type you want to use to fill
|
|
1227
|
-
/// // the array.
|
|
1228
|
-
/// // In our case we just return the value
|
|
1229
|
-
/// let array = Trie.toArray<Text, Nat, Nat>(
|
|
1230
|
-
/// trie,
|
|
1231
|
-
/// func (k, v) = v
|
|
1232
|
-
/// );
|
|
1233
|
-
/// ```
|
|
1234
|
-
public func toArray<K, V, W>(t : Trie<K, V>, f : (K, V) -> W) : [W] {
|
|
1235
|
-
let a = A.tabulate<W>(
|
|
1236
|
-
size(t),
|
|
1237
|
-
func(i : Nat) : W {
|
|
1238
|
-
let (k, v) = switch (nth(t, i)) {
|
|
1239
|
-
case null { P.unreachable() };
|
|
1240
|
-
case (?x) { x }
|
|
1241
|
-
};
|
|
1242
|
-
f(k.key, v)
|
|
1243
|
-
}
|
|
1244
|
-
);
|
|
1245
|
-
a
|
|
1246
|
-
};
|
|
1247
|
-
|
|
1248
|
-
/// Test for "deep emptiness": subtrees that have branching structure,
|
|
1249
|
-
/// but no leaves. These can result from naive filtering operations;
|
|
1250
|
-
/// filter uses this function to avoid creating such subtrees.
|
|
1251
|
-
public func isEmpty<K, V>(t : Trie<K, V>) : Bool {
|
|
1252
|
-
size(t) == 0
|
|
1253
|
-
};
|
|
1254
|
-
|
|
1255
|
-
/// Filter the key-value pairs by a given predicate.
|
|
1256
|
-
///
|
|
1257
|
-
/// For a more detailed overview of how to use a `Trie`,
|
|
1258
|
-
/// see the [User's Overview](#overview).
|
|
1259
|
-
///
|
|
1260
|
-
/// Example:
|
|
1261
|
-
/// ```motoko include=initialize
|
|
1262
|
-
/// trie := Trie.put(trie, key "hello", Text.equal, 42).0;
|
|
1263
|
-
/// trie := Trie.put(trie, key "bye", Text.equal, 32).0;
|
|
1264
|
-
/// trie := Trie.put(trie, key "ciao", Text.equal, 10).0;
|
|
1265
|
-
/// // `filter` takes a function that takes a key-value tuple
|
|
1266
|
-
/// // and returns true if the key-value pair should be included.
|
|
1267
|
-
/// // In our case those are pairs with a value greater than 20
|
|
1268
|
-
/// let filteredTrie = Trie.filter<Text, Nat>(
|
|
1269
|
-
/// trie,
|
|
1270
|
-
/// func (k, v) = v > 20
|
|
1271
|
-
/// );
|
|
1272
|
-
/// assert (Trie.all<Text, Nat>(filteredTrie, func(k, v) = v > 20) == true);
|
|
1273
|
-
/// ```
|
|
1274
|
-
public func filter<K, V>(t : Trie<K, V>, f : (K, V) -> Bool) : Trie<K, V> {
|
|
1275
|
-
func rec(t : Trie<K, V>, bitpos : Nat) : Trie<K, V> {
|
|
1276
|
-
switch t {
|
|
1277
|
-
case (#empty) { #empty };
|
|
1278
|
-
case (#leaf(l)) {
|
|
1279
|
-
leaf(
|
|
1280
|
-
List.filter(
|
|
1281
|
-
l.keyvals,
|
|
1282
|
-
func((k : Key<K>, v : V)) : Bool = f(k.key, v)
|
|
1283
|
-
),
|
|
1284
|
-
bitpos
|
|
1285
|
-
)
|
|
1286
|
-
};
|
|
1287
|
-
case (#branch(b)) {
|
|
1288
|
-
let fl = rec(b.left, bitpos + 1);
|
|
1289
|
-
let fr = rec(b.right, bitpos + 1);
|
|
1290
|
-
if (isEmpty(fl) and isEmpty(fr)) {
|
|
1291
|
-
#empty
|
|
1292
|
-
} else {
|
|
1293
|
-
branch(fl, fr)
|
|
1294
|
-
}
|
|
1295
|
-
}
|
|
1296
|
-
}
|
|
1297
|
-
};
|
|
1298
|
-
rec(t, 0)
|
|
1299
|
-
};
|
|
1300
|
-
|
|
1301
|
-
/// Map and filter the key-value pairs by a given predicate.
|
|
1302
|
-
///
|
|
1303
|
-
/// For a more detailed overview of how to use a `Trie`,
|
|
1304
|
-
/// see the [User's Overview](#overview).
|
|
1305
|
-
///
|
|
1306
|
-
/// Example:
|
|
1307
|
-
/// ```motoko include=initialize
|
|
1308
|
-
/// trie := Trie.put(trie, key "hello", Text.equal, 42).0;
|
|
1309
|
-
/// trie := Trie.put(trie, key "bye", Text.equal, 32).0;
|
|
1310
|
-
/// trie := Trie.put(trie, key "ciao", Text.equal, 10).0;
|
|
1311
|
-
/// // `mapFilter` takes a function that takes a key-value tuple
|
|
1312
|
-
/// // and returns a possibly-distinct value if the key-value pair should be included.
|
|
1313
|
-
/// // In our case, we filter for values greater than 20 and map them to their square.
|
|
1314
|
-
/// let filteredTrie = Trie.mapFilter<Text, Nat, Nat>(
|
|
1315
|
-
/// trie,
|
|
1316
|
-
/// func (k, v) = if (v > 20) return ?(v**2) else return null
|
|
1317
|
-
/// );
|
|
1318
|
-
/// assert (Trie.all<Text, Nat>(filteredTrie, func(k, v) = v > 60) == true);
|
|
1319
|
-
/// ```
|
|
1320
|
-
public func mapFilter<K, V, W>(t : Trie<K, V>, f : (K, V) -> ?W) : Trie<K, W> {
|
|
1321
|
-
func rec(t : Trie<K, V>, bitpos : Nat) : Trie<K, W> {
|
|
1322
|
-
switch t {
|
|
1323
|
-
case (#empty) { #empty };
|
|
1324
|
-
case (#leaf(l)) {
|
|
1325
|
-
leaf(
|
|
1326
|
-
List.mapFilter(
|
|
1327
|
-
l.keyvals,
|
|
1328
|
-
// retain key and hash, but update key's value using f:
|
|
1329
|
-
func((k : Key<K>, v : V)) : ?(Key<K>, W) {
|
|
1330
|
-
switch (f(k.key, v)) {
|
|
1331
|
-
case null { null };
|
|
1332
|
-
case (?w) { ?({ key = k.key; hash = k.hash }, w) }
|
|
1333
|
-
}
|
|
1334
|
-
}
|
|
1335
|
-
),
|
|
1336
|
-
bitpos
|
|
1337
|
-
)
|
|
1338
|
-
};
|
|
1339
|
-
case (#branch(b)) {
|
|
1340
|
-
let fl = rec(b.left, bitpos + 1);
|
|
1341
|
-
let fr = rec(b.right, bitpos + 1);
|
|
1342
|
-
if (isEmpty(fl) and isEmpty(fr)) {
|
|
1343
|
-
#empty
|
|
1344
|
-
} else {
|
|
1345
|
-
branch(fl, fr)
|
|
1346
|
-
}
|
|
1347
|
-
}
|
|
1348
|
-
}
|
|
1349
|
-
};
|
|
1350
|
-
|
|
1351
|
-
rec(t, 0)
|
|
1352
|
-
};
|
|
1353
|
-
|
|
1354
|
-
/// Test for equality, but naively, based on structure.
|
|
1355
|
-
/// Does not attempt to remove "junk" in the tree;
|
|
1356
|
-
/// For instance, a "smarter" approach would equate
|
|
1357
|
-
/// `#bin {left = #empty; right = #empty}`
|
|
1358
|
-
/// with
|
|
1359
|
-
/// `#empty`.
|
|
1360
|
-
/// We do not observe that equality here.
|
|
1361
|
-
public func equalStructure<K, V>(
|
|
1362
|
-
tl : Trie<K, V>,
|
|
1363
|
-
tr : Trie<K, V>,
|
|
1364
|
-
keq : (K, K) -> Bool,
|
|
1365
|
-
veq : (V, V) -> Bool
|
|
1366
|
-
) : Bool {
|
|
1367
|
-
func rec(tl : Trie<K, V>, tr : Trie<K, V>) : Bool {
|
|
1368
|
-
switch (tl, tr) {
|
|
1369
|
-
case (#empty, #empty) { true };
|
|
1370
|
-
case (#leaf(l1), #leaf(l2)) {
|
|
1371
|
-
List.equal(
|
|
1372
|
-
l1.keyvals,
|
|
1373
|
-
l2.keyvals,
|
|
1374
|
-
func((k1 : Key<K>, v1 : V), (k2 : Key<K>, v2 : V)) : Bool = keq(k1.key, k2.key) and veq(v1, v2)
|
|
1375
|
-
)
|
|
1376
|
-
};
|
|
1377
|
-
case (#branch(b1), #branch(b2)) {
|
|
1378
|
-
rec(b1.left, b2.left) and rec(b2.right, b2.right)
|
|
1379
|
-
};
|
|
1380
|
-
case _ { false }
|
|
1381
|
-
}
|
|
1382
|
-
};
|
|
1383
|
-
rec(tl, tr)
|
|
1384
|
-
};
|
|
1385
|
-
|
|
1386
|
-
/// Replace the given key's value in the trie,
|
|
1387
|
-
/// and only if successful, do the success continuation,
|
|
1388
|
-
/// otherwise, return the failure value
|
|
1389
|
-
///
|
|
1390
|
-
/// For a more detailed overview of how to use a Trie,
|
|
1391
|
-
/// see the [User's Overview](#overview).
|
|
1392
|
-
///
|
|
1393
|
-
/// Example:
|
|
1394
|
-
/// ```motoko include=initialize
|
|
1395
|
-
/// trie := Trie.put(trie, key "hello", Text.equal, 42).0;
|
|
1396
|
-
/// trie := Trie.put(trie, key "bye", Text.equal, 32).0;
|
|
1397
|
-
/// trie := Trie.put(trie, key "ciao", Text.equal, 10).0;
|
|
1398
|
-
/// // `replaceThen` takes the same arguments as `replace` but also a success continuation
|
|
1399
|
-
/// // and a failure connection that are called in the respective scenarios.
|
|
1400
|
-
/// // if the replace fails, that is the key is not present in the trie, the failure continuation is called.
|
|
1401
|
-
/// // if the replace succeeds, that is the key is present in the trie, the success continuation is called.
|
|
1402
|
-
/// // in this example we are simply returning the Text values `success` and `fail` respectively.
|
|
1403
|
-
/// var continuation = Trie.replaceThen<Text, Nat, Text>(
|
|
1404
|
-
/// trie,
|
|
1405
|
-
/// key "hello",
|
|
1406
|
-
/// Text.equal,
|
|
1407
|
-
/// 12,
|
|
1408
|
-
/// func (t, v) = "success",
|
|
1409
|
-
/// func () = "fail"
|
|
1410
|
-
/// );
|
|
1411
|
-
/// assert (continuation == "success");
|
|
1412
|
-
/// continuation := Trie.replaceThen<Text, Nat, Text>(
|
|
1413
|
-
/// trie,
|
|
1414
|
-
/// key "shalom",
|
|
1415
|
-
/// Text.equal,
|
|
1416
|
-
/// 12,
|
|
1417
|
-
/// func (t, v) = "success",
|
|
1418
|
-
/// func () = "fail"
|
|
1419
|
-
/// );
|
|
1420
|
-
/// assert (continuation == "fail");
|
|
1421
|
-
/// ```
|
|
1422
|
-
public func replaceThen<K, V, X>(
|
|
1423
|
-
t : Trie<K, V>,
|
|
1424
|
-
k : Key<K>,
|
|
1425
|
-
k_eq : (K, K) -> Bool,
|
|
1426
|
-
v2 : V,
|
|
1427
|
-
success : (Trie<K, V>, V) -> X,
|
|
1428
|
-
fail : () -> X
|
|
1429
|
-
) : X {
|
|
1430
|
-
let (t2, ov) = replace(t, k, k_eq, ?v2);
|
|
1431
|
-
switch ov {
|
|
1432
|
-
case null { /* no prior value; failure to remove */ fail() };
|
|
1433
|
-
case (?v1) { success(t2, v1) }
|
|
1434
|
-
}
|
|
1435
|
-
};
|
|
1436
|
-
|
|
1437
|
-
/// Put the given key's value in the trie; return the new trie; assert that no prior value is associated with the key
|
|
1438
|
-
///
|
|
1439
|
-
/// For a more detailed overview of how to use a `Trie`,
|
|
1440
|
-
/// see the [User's Overview](#overview).
|
|
1441
|
-
///
|
|
1442
|
-
/// Example:
|
|
1443
|
-
/// ```motoko include=initialize
|
|
1444
|
-
/// // note that compared to `put`, `putFresh` does not return a tuple
|
|
1445
|
-
/// trie := Trie.putFresh(trie, key "hello", Text.equal, 42);
|
|
1446
|
-
/// trie := Trie.putFresh(trie, key "bye", Text.equal, 32);
|
|
1447
|
-
/// // this will fail as "hello" is already present in the trie
|
|
1448
|
-
/// trie := Trie.putFresh(trie, key "hello", Text.equal, 10);
|
|
1449
|
-
/// ```
|
|
1450
|
-
public func putFresh<K, V>(t : Trie<K, V>, k : Key<K>, k_eq : (K, K) -> Bool, v : V) : Trie<K, V> {
|
|
1451
|
-
let (t2, none) = replace(t, k, k_eq, ?v);
|
|
1452
|
-
switch none {
|
|
1453
|
-
case null {};
|
|
1454
|
-
case (?_) assert false
|
|
1455
|
-
};
|
|
1456
|
-
t2
|
|
1457
|
-
};
|
|
1458
|
-
|
|
1459
|
-
/// Put the given key's value in the 2D trie; return the new 2D trie.
|
|
1460
|
-
public func put2D<K1, K2, V>(
|
|
1461
|
-
t : Trie2D<K1, K2, V>,
|
|
1462
|
-
k1 : Key<K1>,
|
|
1463
|
-
k1_eq : (K1, K1) -> Bool,
|
|
1464
|
-
k2 : Key<K2>,
|
|
1465
|
-
k2_eq : (K2, K2) -> Bool,
|
|
1466
|
-
v : V
|
|
1467
|
-
) : Trie2D<K1, K2, V> {
|
|
1468
|
-
let inner = find(t, k1, k1_eq);
|
|
1469
|
-
let (updated_inner, _) = switch inner {
|
|
1470
|
-
case null { put(#empty, k2, k2_eq, v) };
|
|
1471
|
-
case (?inner) { put(inner, k2, k2_eq, v) }
|
|
1472
|
-
};
|
|
1473
|
-
let (updated_outer, _) = put(t, k1, k1_eq, updated_inner);
|
|
1474
|
-
updated_outer
|
|
1475
|
-
};
|
|
1476
|
-
|
|
1477
|
-
/// Put the given key's value in the trie; return the new trie;
|
|
1478
|
-
public func put3D<K1, K2, K3, V>(
|
|
1479
|
-
t : Trie3D<K1, K2, K3, V>,
|
|
1480
|
-
k1 : Key<K1>,
|
|
1481
|
-
k1_eq : (K1, K1) -> Bool,
|
|
1482
|
-
k2 : Key<K2>,
|
|
1483
|
-
k2_eq : (K2, K2) -> Bool,
|
|
1484
|
-
k3 : Key<K3>,
|
|
1485
|
-
k3_eq : (K3, K3) -> Bool,
|
|
1486
|
-
v : V
|
|
1487
|
-
) : Trie3D<K1, K2, K3, V> {
|
|
1488
|
-
let inner1 = find(t, k1, k1_eq);
|
|
1489
|
-
let (updated_inner1, _) = switch inner1 {
|
|
1490
|
-
case null {
|
|
1491
|
-
put(
|
|
1492
|
-
#empty,
|
|
1493
|
-
k2,
|
|
1494
|
-
k2_eq,
|
|
1495
|
-
(put(#empty, k3, k3_eq, v)).0
|
|
1496
|
-
)
|
|
1497
|
-
};
|
|
1498
|
-
case (?inner1) {
|
|
1499
|
-
let inner2 = find(inner1, k2, k2_eq);
|
|
1500
|
-
let (updated_inner2, _) = switch inner2 {
|
|
1501
|
-
case null { put(#empty, k3, k3_eq, v) };
|
|
1502
|
-
case (?inner2) { put(inner2, k3, k3_eq, v) }
|
|
1503
|
-
};
|
|
1504
|
-
put(inner1, k2, k2_eq, updated_inner2)
|
|
1505
|
-
}
|
|
1506
|
-
};
|
|
1507
|
-
let (updated_outer, _) = put(t, k1, k1_eq, updated_inner1);
|
|
1508
|
-
updated_outer
|
|
1509
|
-
};
|
|
1510
|
-
|
|
1511
|
-
/// Remove the given key's value in the trie; return the new trie
|
|
1512
|
-
///
|
|
1513
|
-
/// For a more detailed overview of how to use a `Trie`,
|
|
1514
|
-
/// see the [User's Overview](#overview).
|
|
1515
|
-
///
|
|
1516
|
-
/// Example:
|
|
1517
|
-
/// ```motoko include=initialize
|
|
1518
|
-
/// trie := Trie.put(trie, key "hello", Text.equal, 42).0;
|
|
1519
|
-
/// trie := Trie.put(trie, key "bye", Text.equal, 32).0;
|
|
1520
|
-
/// // remove the value associated with "hello"
|
|
1521
|
-
/// trie := Trie.remove(trie, key "hello", Text.equal).0;
|
|
1522
|
-
/// assert (Trie.get(trie, key "hello", Text.equal) == null);
|
|
1523
|
-
/// ```
|
|
1524
|
-
public func remove<K, V>(t : Trie<K, V>, k : Key<K>, k_eq : (K, K) -> Bool) : (Trie<K, V>, ?V) {
|
|
1525
|
-
replace(t, k, k_eq, null)
|
|
1526
|
-
};
|
|
1527
|
-
|
|
1528
|
-
/// Remove the given key's value in the trie,
|
|
1529
|
-
/// and only if successful, do the success continuation,
|
|
1530
|
-
/// otherwise, return the failure value
|
|
1531
|
-
public func removeThen<K, V, X>(
|
|
1532
|
-
t : Trie<K, V>,
|
|
1533
|
-
k : Key<K>,
|
|
1534
|
-
k_eq : (K, K) -> Bool,
|
|
1535
|
-
success : (Trie<K, V>, V) -> X,
|
|
1536
|
-
fail : () -> X
|
|
1537
|
-
) : X {
|
|
1538
|
-
let (t2, ov) = replace(t, k, k_eq, null);
|
|
1539
|
-
switch ov {
|
|
1540
|
-
case null { /* no prior value; failure to remove */ fail() };
|
|
1541
|
-
case (?v) { success(t2, v) }
|
|
1542
|
-
}
|
|
1543
|
-
};
|
|
1544
|
-
|
|
1545
|
-
/// remove the given key-key pair's value in the 2D trie; return the
|
|
1546
|
-
/// new trie, and the prior value, if any.
|
|
1547
|
-
public func remove2D<K1, K2, V>(
|
|
1548
|
-
t : Trie2D<K1, K2, V>,
|
|
1549
|
-
k1 : Key<K1>,
|
|
1550
|
-
k1_eq : (K1, K1) -> Bool,
|
|
1551
|
-
k2 : Key<K2>,
|
|
1552
|
-
k2_eq : (K2, K2) -> Bool
|
|
1553
|
-
) : (Trie2D<K1, K2, V>, ?V) {
|
|
1554
|
-
switch (find(t, k1, k1_eq)) {
|
|
1555
|
-
case null { (t, null) };
|
|
1556
|
-
case (?inner) {
|
|
1557
|
-
let (updated_inner, ov) = remove(inner, k2, k2_eq);
|
|
1558
|
-
let (updated_outer, _) = put(t, k1, k1_eq, updated_inner);
|
|
1559
|
-
(updated_outer, ov)
|
|
1560
|
-
}
|
|
1561
|
-
}
|
|
1562
|
-
};
|
|
1563
|
-
|
|
1564
|
-
/// Remove the given key-key pair's value in the 3D trie; return the
|
|
1565
|
-
/// new trie, and the prior value, if any.
|
|
1566
|
-
public func remove3D<K1, K2, K3, V>(
|
|
1567
|
-
t : Trie3D<K1, K2, K3, V>,
|
|
1568
|
-
k1 : Key<K1>,
|
|
1569
|
-
k1_eq : (K1, K1) -> Bool,
|
|
1570
|
-
k2 : Key<K2>,
|
|
1571
|
-
k2_eq : (K2, K2) -> Bool,
|
|
1572
|
-
k3 : Key<K3>,
|
|
1573
|
-
k3_eq : (K3, K3) -> Bool
|
|
1574
|
-
) : (Trie3D<K1, K2, K3, V>, ?V) {
|
|
1575
|
-
switch (find(t, k1, k1_eq)) {
|
|
1576
|
-
case null { (t, null) };
|
|
1577
|
-
case (?inner) {
|
|
1578
|
-
let (updated_inner, ov) = remove2D(inner, k2, k2_eq, k3, k3_eq);
|
|
1579
|
-
let (updated_outer, _) = put(t, k1, k1_eq, updated_inner);
|
|
1580
|
-
(updated_outer, ov)
|
|
1581
|
-
}
|
|
1582
|
-
}
|
|
1583
|
-
};
|
|
1584
|
-
|
|
1585
|
-
/// Like [`mergeDisjoint`](#mergedisjoint), except instead of merging a
|
|
1586
|
-
/// pair, it merges the collection of dimension-2 sub-trees of a 2D
|
|
1587
|
-
/// trie.
|
|
1588
|
-
public func mergeDisjoint2D<K1, K2, V>(
|
|
1589
|
-
t : Trie2D<K1, K2, V>,
|
|
1590
|
-
k1_eq : (K1, K1) -> Bool,
|
|
1591
|
-
k2_eq : (K2, K2) -> Bool
|
|
1592
|
-
) : Trie<K2, V> {
|
|
1593
|
-
foldUp(
|
|
1594
|
-
t,
|
|
1595
|
-
func(t1 : Trie<K2, V>, t2 : Trie<K2, V>) : Trie<K2, V> {
|
|
1596
|
-
mergeDisjoint(t1, t2, k2_eq)
|
|
1597
|
-
},
|
|
1598
|
-
func(_ : K1, t : Trie<K2, V>) : Trie<K2, V> { t },
|
|
1599
|
-
#empty
|
|
1600
|
-
)
|
|
1601
|
-
};
|
|
1602
|
-
|
|
1603
|
-
}
|