/
trie.cr
253 lines (217 loc) · 6.75 KB
/
trie.cr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
module Immutable
class Vector(T)
class Trie(T)
BITS_PER_LEVEL = 5_u32
BLOCK_SIZE = (2 ** BITS_PER_LEVEL).to_u32
INDEX_MASK = BLOCK_SIZE - 1
include Enumerable(T)
getter :size, :levels
@children : Array(Trie(T))
@values : Array(T)
@size : Int32
@levels : Int32
@owner : UInt64?
def initialize(@children : Array(Trie(T)), @levels : Int32, @owner : UInt64? = nil)
@size = @children.reduce(0) { |sum, child| sum + child.size }
@values = [] of T
end
def initialize(@values : Array(T), @owner : UInt64? = nil)
@size = @values.size
@levels = 0
@children = [] of Trie(T)
end
def at(index : Int)
return yield if index < 0 || index >= size
lookup(index)
end
def get(index : Int)
at(index) { raise IndexError.new }
end
def update(index : Int, value : T) : Trie(T)
raise IndexError.new if index < 0 || index >= size
set(index, value)
end
def update!(index : Int, value : T, from : UInt64) : Trie(T)
raise IndexError.new if index < 0 || index >= size
set!(index, value, from)
end
def each
i = 0
while i < size
leaf_values = leaf_for(i).values
leaf_values.each do |value|
yield value
i += 1
end
end
self
end
def each
FlattenLeaves.new((0...size).step(BLOCK_SIZE).map do |i|
leaf_for(i).values.each.as(Iterator(T))
end)
end
def push_leaf(leaf : Array(T), from : UInt64? = nil) : Trie(T)
raise ArgumentError.new if leaf.size > BLOCK_SIZE || size % 32 != 0
return Trie.new([self], @levels + 1, from).push_leaf(leaf, from) if full?
return Trie.new(leaf, from) if empty? && leaf?
Trie.new(@children.dup.tap do |cs|
if @levels == 1
cs.push(Trie.new(leaf))
else
if cs.empty? || cs.last.full?
cs << Trie.new([] of Trie(T), @levels - 1)
end
cs[-1] = cs[-1].push_leaf(leaf, from)
end
end, @levels, from)
end
def push_leaf!(leaf : Array(T), from : UInt64) : Trie(T)
raise ArgumentError.new if leaf.size > BLOCK_SIZE || size % 32 != 0
return push_leaf(leaf, from) unless from == @owner
return Trie.new([self], @levels + 1, from).push_leaf!(leaf, from) if full?
return Trie.new(leaf, @owner) if empty? && leaf?
if @levels == 1
@children.push(Trie.new(leaf, from))
else
if @children.empty? || @children.last.full?
@children << Trie.new([] of Trie(T), @levels - 1, from)
end
@children[-1] = @children[-1].push_leaf!(leaf, from)
end
@size = calculate_size
self
end
def pop_leaf(from : UInt64? = nil) : Trie(T)
raise ArgumentError.new if empty? || size % 32 != 0
return Trie.new([] of T, from) if leaf?
child = @children.last.pop_leaf
if child.empty?
return @children.first if @children.size == 2
return Trie.new(@children[0...-1], @levels, from)
end
Trie.new(@children[0...-1].push(child), @levels, from)
end
def pop_leaf!(from : UInt64) : Trie(T)
raise ArgumentError.new if empty? || size % 32 != 0
return pop_leaf(from) unless from == @owner
return Trie.new([] of T, from) if leaf?
@children[-1] = @children[-1].pop_leaf!(from)
if @children[-1].empty?
return @children.first if @children.size == 2
@children.pop
end
@size = calculate_size
self
end
def last
get(size - 1)
end
def last_leaf
leaf_for(@size - 1).values
end
def empty?
@size == 0
end
def leaf?
@levels == 0
end
def inspect
return @values.inspect if leaf?
"[#{@children.map { |c| c.inspect }.join(", ")}]"
end
def clear_owner!
@owner = nil
self
end
def self.empty(owner : UInt64? = nil)
Trie.new([] of T, owner)
end
def self.from(elems : Array(T))
trie = Trie(T).empty
elems.each_slice(BLOCK_SIZE) do |leaf|
trie = trie.push_leaf(leaf)
end
trie
end
def self.from(elems : Array(T), owner : UInt64)
trie = Trie(T).empty(owner)
elems.each_slice(BLOCK_SIZE) do |leaf|
trie = trie.push_leaf!(leaf, owner)
end
trie
end
protected def set(index : Int, value : T, from : UInt64? = nil) : Trie(T)
child_idx = child_index(index)
if leaf?
values = @values.dup.tap { |vs| vs[child_idx] = value }
Trie.new(values, from)
else
children = @children.dup.tap do |cs|
cs[child_idx] = cs[child_idx].set(index, value)
end
Trie.new(children, @levels, from)
end
end
protected def set!(index : Int, value : T, from : UInt64) : Trie(T)
return set(index, value, from) unless from == @owner
child_idx = child_index(index)
if leaf?
@values[child_idx] = value
else
@children[child_idx] = @children[child_idx].set!(index, value, from)
end
@size = calculate_size
self
end
private def calculate_size
if leaf?
@values.size
else
@children.reduce(0) { |sum, child| sum + child.size }
end
end
protected def lookup(index : Int)
child_idx = child_index(index)
return @values[child_idx] if leaf?
@children[child_idx].lookup(index)
end
protected def leaf_for(index : Int)
return self if leaf?
@children[child_index(index)].leaf_for(index)
end
protected def values
@values
end
private def child_index(index)
(index >> (@levels * BITS_PER_LEVEL)) & INDEX_MASK
end
protected def full?
return @values.size == BLOCK_SIZE if leaf?
@size >> ((@levels + 1) * BITS_PER_LEVEL) > 0
end
# :nodoc:
class FlattenLeaves(T)
include Iterator(T)
@chunk : Iterator(T) | Iterator::Stop
def initialize(@generator : Iterator(Iterator(T)))
@chunk = @generator.next
end
def next : T | Iterator::Stop
chunk = @chunk
if chunk.is_a?(Iterator::Stop)
Iterator::Stop.new
else
elem = chunk.next
if elem.is_a?(Iterator::Stop)
@chunk = @generator.next
self.next
else
elem
end
end
end
end
end
end
end