treemap-fork 1.0.4.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.
- checksums.yaml +7 -0
- data/lib/treemap-fork.rb +2 -0
- data/lib/treemap/bounded_map.rb +380 -0
- data/lib/treemap/tree_map.rb +671 -0
- metadata +54 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 5b36b943e55ad85f7840c4fea099e6138670f68c
|
|
4
|
+
data.tar.gz: 0be95d0f0b306013924d7e721ad636850f4c0b71
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 26a2129fbefeb709ec1aac781287d70780c0344cd07f4126a0ddda7477537b7ff4a5081f45346ce9feebb9a700a14159641d3ba76ea053a235c6521ef317317b
|
|
7
|
+
data.tar.gz: 5c7150ae1db6a53f4810374d399219ee1aa2c6ce828b3e7a2ccc5c29c42fbe337b28f4f8bc5374ed3ef66afbc7d446bddb52d7310ccb93ce515a06b9e8ad4a87
|
data/lib/treemap-fork.rb
ADDED
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
class TreeMap
|
|
2
|
+
module Bound
|
|
3
|
+
INCLUSIVE = 1
|
|
4
|
+
EXCLUSIVE = 2
|
|
5
|
+
NO_BOUND = 3
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
# A map with optional limits on its range.
|
|
9
|
+
# This is intended to be used only by the TreeMap class.
|
|
10
|
+
class BoundedMap
|
|
11
|
+
include Enumerable
|
|
12
|
+
|
|
13
|
+
attr_accessor :treemap, :ascending, :from, :from_bound, :to, :to_bound
|
|
14
|
+
|
|
15
|
+
def initialize(treemap, ascending, from, from_bound, to, to_bound)
|
|
16
|
+
@treemap = treemap
|
|
17
|
+
@ascending = ascending
|
|
18
|
+
@from = from
|
|
19
|
+
@from_bound = from_bound
|
|
20
|
+
@to = to
|
|
21
|
+
@to_bound = to_bound
|
|
22
|
+
|
|
23
|
+
# Validate the bounds. In addition to checking that from <= to, we verify that the comparator supports our bound objects.
|
|
24
|
+
if from_bound != Bound::NO_BOUND && to_bound != Bound::NO_BOUND
|
|
25
|
+
raise "Invalid from and to arguments: #{from} (from) > #{to} (to)" if comparator.call(from, to) > 0
|
|
26
|
+
elsif from_bound != Bound::NO_BOUND
|
|
27
|
+
comparator.call(from, from)
|
|
28
|
+
elsif to_bound != Bound::NO_BOUND
|
|
29
|
+
comparator.call(to, to)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def empty?
|
|
34
|
+
endpoint(true).nil?
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def get(key)
|
|
38
|
+
@treemap.get(key) if in_bounds?(key)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def contains_key?(key)
|
|
42
|
+
in_bounds?(key) && @treemap.contains_key?(key)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def put(key, value)
|
|
46
|
+
raise "Key out of bounds." unless in_bounds?(key)
|
|
47
|
+
put_internal(key, value)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def remove(key)
|
|
51
|
+
@treemap.remove(key) if in_bounds?(key)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Returns true if the key is in bounds.
|
|
55
|
+
# Note: The reference implementation calls this function isInBounds
|
|
56
|
+
def in_bounds?(key)
|
|
57
|
+
in_closed_bounds?(key, @from_bound, @to_bound)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Returns true if the key is in bounds. Use this overload with
|
|
61
|
+
# NO_BOUND to skip bounds checking on either end.
|
|
62
|
+
# Note: The reference implementation calls this function isInBounds
|
|
63
|
+
def in_closed_bounds?(key, from_bound, to_bound)
|
|
64
|
+
if from_bound == Bound::INCLUSIVE
|
|
65
|
+
return false if comparator.call(key, from) < 0 # less than from
|
|
66
|
+
elsif from_bound == Bound::EXCLUSIVE
|
|
67
|
+
return false if comparator.call(key, from) <= 0 # less than or equal to from
|
|
68
|
+
end
|
|
69
|
+
if to_bound == Bound::INCLUSIVE
|
|
70
|
+
return false if comparator.call(key, to) > 0 # greater than 'to'
|
|
71
|
+
elsif to_bound == Bound::EXCLUSIVE
|
|
72
|
+
return false if comparator.call(key, to) >= 0 # greater than or equal to 'to'
|
|
73
|
+
end
|
|
74
|
+
true
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Returns the entry if it is in bounds, or null if it is out of bounds.
|
|
78
|
+
def bound(node, from_bound, to_bound)
|
|
79
|
+
node if node && in_closed_bounds?(node.key, from_bound, to_bound)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Navigable methods
|
|
83
|
+
|
|
84
|
+
def first_entry
|
|
85
|
+
endpoint(true)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def poll_first_entry
|
|
89
|
+
result = endpoint(true)
|
|
90
|
+
@treemap.remove_internal(result) if result
|
|
91
|
+
result
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def first_key
|
|
95
|
+
entry = endpoint(true)
|
|
96
|
+
raise "No such element" unless entry
|
|
97
|
+
entry.key
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def last_entry
|
|
101
|
+
endpoint(false)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def poll_last_entry
|
|
105
|
+
result = endpoint(false)
|
|
106
|
+
@treemap.remove_internal(result) if result
|
|
107
|
+
result
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def last_key
|
|
111
|
+
entry = endpoint(false)
|
|
112
|
+
raise "No such element" unless entry
|
|
113
|
+
entry.key
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# <first> - true for the first element, false for the last
|
|
117
|
+
def endpoint(first)
|
|
118
|
+
node, from, to = if @ascending == first
|
|
119
|
+
node = case @from_bound
|
|
120
|
+
when Bound::NO_BOUND
|
|
121
|
+
@treemap.root.first if @treemap.root
|
|
122
|
+
when Bound::INCLUSIVE
|
|
123
|
+
@treemap.find(@from, Relation::CEILING)
|
|
124
|
+
when Bound::EXCLUSIVE
|
|
125
|
+
@treemap.find(@from, Relation::HIGHER)
|
|
126
|
+
else
|
|
127
|
+
raise "Undefined bound."
|
|
128
|
+
end
|
|
129
|
+
[node, Bound::NO_BOUND, @to_bound]
|
|
130
|
+
else
|
|
131
|
+
node = case @to_bound
|
|
132
|
+
when Bound::NO_BOUND
|
|
133
|
+
@treemap.root.last if @treemap.root
|
|
134
|
+
when Bound::INCLUSIVE
|
|
135
|
+
@treemap.find(@to, Relation::FLOOR)
|
|
136
|
+
when Bound::EXCLUSIVE
|
|
137
|
+
@treemap.find(@to, Relation::LOWER)
|
|
138
|
+
else
|
|
139
|
+
raise "Undefined bound."
|
|
140
|
+
end
|
|
141
|
+
[node, @from_bound, Bound::NO_BOUND]
|
|
142
|
+
end
|
|
143
|
+
bound(node, from, to)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Performs a find on the underlying tree after constraining it to the
|
|
147
|
+
# bounds of this view. Examples:
|
|
148
|
+
#
|
|
149
|
+
# bound is (A..C)
|
|
150
|
+
# find_bounded(B, FLOOR) stays source.find(B, FLOOR)
|
|
151
|
+
#
|
|
152
|
+
# bound is (A..C)
|
|
153
|
+
# find_bounded(C, FLOOR) becomes source.find(C, LOWER)
|
|
154
|
+
#
|
|
155
|
+
# bound is (A..C)
|
|
156
|
+
# find_bounded(D, LOWER) becomes source.find(C, LOWER)
|
|
157
|
+
#
|
|
158
|
+
# bound is (A..C]
|
|
159
|
+
# find_bounded(D, FLOOR) becomes source.find(C, FLOOR)
|
|
160
|
+
#
|
|
161
|
+
# bound is (A..C]
|
|
162
|
+
# find_bounded(D, LOWER) becomes source.find(C, FLOOR)
|
|
163
|
+
def find_bounded(key, relation)
|
|
164
|
+
relation = Relation.for_order(relation, @ascending)
|
|
165
|
+
from_bound_for_check = @from_bound
|
|
166
|
+
to_bound_for_check = @to_bound
|
|
167
|
+
if @to_bound != Bound::NO_BOUND && (relation == Relation::LOWER || relation == Relation::FLOOR)
|
|
168
|
+
comparison = comparator.call(@to, key)
|
|
169
|
+
if comparison <= 0
|
|
170
|
+
key = @to
|
|
171
|
+
if @to_bound == Bound::EXCLUSIVE
|
|
172
|
+
relation = Relation::LOWER # 'to' is too high
|
|
173
|
+
else comparison < 0
|
|
174
|
+
relation = Relation::FLOOR # we already went lower
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
to_bound_for_check = Bound::NO_BOUND # we've already checked the upper bound
|
|
178
|
+
end
|
|
179
|
+
if @from_bound != Bound::NO_BOUND && (relation == Relation::CEILING || relation == Relation::HIGHER)
|
|
180
|
+
comparison = comparator.call(@from, key)
|
|
181
|
+
if comparison >= 0
|
|
182
|
+
key = @from
|
|
183
|
+
if @from_bound == Bound::EXCLUSIVE
|
|
184
|
+
relation = Relation::HIGHER # 'from' is too low
|
|
185
|
+
else comparison > 0
|
|
186
|
+
relation = Relation::CEILING # we already went higher
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
from_bound_for_check = Bound::NO_BOUND # we've already checked the lower bound
|
|
190
|
+
end
|
|
191
|
+
bound(@treemap.find(key, relation), from_bound_for_check, to_bound_for_check)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def lower_entry(key)
|
|
195
|
+
find_bounded(key, Relation::LOWER)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def lower_key(key)
|
|
199
|
+
entry = find_bounded(key, Relation::LOWER)
|
|
200
|
+
entry.key if entry
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def floor_entry(key)
|
|
204
|
+
find_bounded(key, Relation::FLOOR)
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def floor_key(key)
|
|
208
|
+
entry = find_bounded(key, Relation::FLOOR)
|
|
209
|
+
entry.key if entry
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def ceiling_entry(key)
|
|
213
|
+
find_bounded(key, Relation::CEILING)
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def ceiling_key(key)
|
|
217
|
+
entry = find_bounded(key, Relation::CEILING)
|
|
218
|
+
entry.key if entry
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def higher_entry(key)
|
|
222
|
+
find_bounded(key, Relation::HIGHER)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def higher_key(key)
|
|
226
|
+
entry = find_bounded(key, Relation::HIGHER)
|
|
227
|
+
entry.key if entry
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def comparator
|
|
231
|
+
if @ascending
|
|
232
|
+
@treemap.comparator
|
|
233
|
+
else
|
|
234
|
+
->(this, that) { -@treemap.comparator.call(this, that) }
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# View factory methods
|
|
239
|
+
|
|
240
|
+
def entry_set
|
|
241
|
+
Set.new(each_node.to_a)
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def key_set
|
|
245
|
+
Set.new(each_node.map(&:key))
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
alias keys key_set
|
|
249
|
+
|
|
250
|
+
def values
|
|
251
|
+
each_node.map(&:value)
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def descending_map
|
|
255
|
+
BoundedMap.new(@treemap, !@ascending, @from, @from_bound, @to, @to_bound)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# This can be called in 1 of 2 ways:
|
|
259
|
+
# sub_map(from_inclusive, to_exclusive)
|
|
260
|
+
# OR
|
|
261
|
+
# sub_map(from, from_inclusive, to, to_inclusive)
|
|
262
|
+
def sub_map(*args)
|
|
263
|
+
case args.count
|
|
264
|
+
when 2
|
|
265
|
+
from_inclusive, to_exclusive = *args
|
|
266
|
+
bounded_sub_map(from_inclusive, Bound::INCLUSIVE, to_exclusive, Bound::EXCLUSIVE)
|
|
267
|
+
when 4
|
|
268
|
+
from, from_inclusive, to, to_inclusive = *args
|
|
269
|
+
from_bound = from_inclusive ? Bound::INCLUSIVE : Bound::EXCLUSIVE
|
|
270
|
+
to_bound = to_inclusive ? Bound::INCLUSIVE : Bound::EXCLUSIVE
|
|
271
|
+
bounded_sub_map(from, from_bound, to, to_bound)
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def bounded_sub_map(from, from_bound, to, to_bound)
|
|
276
|
+
if !@ascending
|
|
277
|
+
from, to = to, from
|
|
278
|
+
from_bound, to_bound = to_bound, from_bound
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# If both the current and requested bounds are exclusive, the isInBounds check must be
|
|
282
|
+
# inclusive. For example, to create (C..F) from (A..F), the bound 'F' is in bounds.
|
|
283
|
+
if from_bound == Bound::NO_BOUND
|
|
284
|
+
from = @from
|
|
285
|
+
from_bound = @from_bound
|
|
286
|
+
else
|
|
287
|
+
from_bound_to_check = from_bound == @from_bound ? Bound::INCLUSIVE : @from_bound
|
|
288
|
+
raise out_of_bounds(to, from_bound_to_check, @to_bound) if !in_closed_bounds?(from, from_bound_to_check, @to_bound)
|
|
289
|
+
end
|
|
290
|
+
if to_bound == Bound::NO_BOUND
|
|
291
|
+
to = @to
|
|
292
|
+
to_bound = @to_bound
|
|
293
|
+
else
|
|
294
|
+
to_bound_to_check = to_bound == @to_bound ? Bound::INCLUSIVE : @to_bound
|
|
295
|
+
raise out_of_bounds(to, @from_bound, to_bound_to_check) if !in_closed_bounds?(to, @from_bound, to_bound_to_check)
|
|
296
|
+
end
|
|
297
|
+
BoundedMap.new(@treemap, ascending, from, from_bound, to, to_bound)
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
# This can be called in 1 of 2 ways:
|
|
301
|
+
# head_map(to_exclusive)
|
|
302
|
+
# OR
|
|
303
|
+
# head_map(to, inclusive)
|
|
304
|
+
def head_map(*args)
|
|
305
|
+
case args.count
|
|
306
|
+
when 1
|
|
307
|
+
to_exclusive = args.first
|
|
308
|
+
bounded_sub_map(nil, Bound::NO_BOUND, to_exclusive, Bound::EXCLUSIVE)
|
|
309
|
+
when 2
|
|
310
|
+
to, inclusive = *args
|
|
311
|
+
to_bound = inclusive ? Bound::INCLUSIVE : Bound::EXCLUSIVE
|
|
312
|
+
bounded_sub_map(nil, Bound::NO_BOUND, to, to_bound)
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
# This can be called in 1 of 2 ways:
|
|
317
|
+
# tail_map(from_inclusive)
|
|
318
|
+
# OR
|
|
319
|
+
# tail_map(from, inclusive)
|
|
320
|
+
def tail_map(*args)
|
|
321
|
+
case args.count
|
|
322
|
+
when 1
|
|
323
|
+
from_inclusive = args.first
|
|
324
|
+
bounded_sub_map(fromInclusive, Bound::INCLUSIVE, nil, Bound::NO_BOUND)
|
|
325
|
+
when 2
|
|
326
|
+
from, inclusive = *args
|
|
327
|
+
from_bound = inclusive ? Bound::INCLUSIVE : Bound::EXCLUSIVE
|
|
328
|
+
bounded_sub_map(from, from_bound, nil, Bound::NO_BOUND)
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
def out_of_bounds(value, from_bound, to_bound)
|
|
333
|
+
Exception.new("#{value} not in range #{from_bound.left_cap(@from)}..#{to_bound.right_cap(@to)}")
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
# Bounded view implementations
|
|
337
|
+
|
|
338
|
+
# in-order traversal of nodes in tree
|
|
339
|
+
class BoundedNodeIterator < ::TreeMap::NodeIterator
|
|
340
|
+
def initialize(bounded_map, next_node)
|
|
341
|
+
super(next_node)
|
|
342
|
+
@bounded_map = bounded_map
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
def step_forward
|
|
346
|
+
result = super
|
|
347
|
+
@next_node = nil if @next_node && !@bounded_map.in_closed_bounds?(@next_node.key, Bound::NO_BOUND, @bounded_map.to_bound)
|
|
348
|
+
result
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
def step_backward
|
|
352
|
+
result = super
|
|
353
|
+
@next_node = nil if @next_node && !@bounded_map.in_closed_bounds?(@next_node.key, @bounded_map.from_bound, Bound::NO_BOUND)
|
|
354
|
+
result
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
# each {|k,v| puts "#{k}->#{v}"}
|
|
359
|
+
def each(&blk)
|
|
360
|
+
if block_given?
|
|
361
|
+
each_node {|node| blk.call(node.key, node.value) }
|
|
362
|
+
else
|
|
363
|
+
enum_for(:each)
|
|
364
|
+
end
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
# each_node {|node| puts "#{node.key}->#{node.value}"}
|
|
368
|
+
def each_node
|
|
369
|
+
if block_given?
|
|
370
|
+
iter = BoundedNodeIterator.new(self, endpoint(true))
|
|
371
|
+
while iter.has_next?
|
|
372
|
+
yield iter.step_forward()
|
|
373
|
+
end
|
|
374
|
+
else
|
|
375
|
+
enum_for(:each_node)
|
|
376
|
+
end
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
end
|
|
380
|
+
end
|
|
@@ -0,0 +1,671 @@
|
|
|
1
|
+
require 'pp'
|
|
2
|
+
require 'set'
|
|
3
|
+
|
|
4
|
+
# TreeMap is a Ruby port of https://android.googlesource.com/platform/libcore.git/+/android-6.0.1_r32/luni/src/main/java/java/util/TreeMap.java
|
|
5
|
+
# This is an AVL tree based implementation of Java's java.util.TreeMap structure.
|
|
6
|
+
# It implements Java's java.util.NavigableMap interface.
|
|
7
|
+
# Warning: Not all of the reference implementation has been ported.
|
|
8
|
+
class TreeMap
|
|
9
|
+
include Enumerable
|
|
10
|
+
|
|
11
|
+
module Relation
|
|
12
|
+
LOWER = 1
|
|
13
|
+
FLOOR = 2
|
|
14
|
+
EQUAL = 3
|
|
15
|
+
CREATE = 4
|
|
16
|
+
CEILING = 5
|
|
17
|
+
HIGHER = 6
|
|
18
|
+
|
|
19
|
+
def self.for_order(relation, ascending)
|
|
20
|
+
if ascending
|
|
21
|
+
relation
|
|
22
|
+
else
|
|
23
|
+
case relation
|
|
24
|
+
when LOWER
|
|
25
|
+
HIGHER
|
|
26
|
+
when FLOOR
|
|
27
|
+
CEILING
|
|
28
|
+
when EQUAL
|
|
29
|
+
EQUAL
|
|
30
|
+
when CEILING
|
|
31
|
+
FLOOR
|
|
32
|
+
when HIGHER
|
|
33
|
+
LOWER
|
|
34
|
+
else
|
|
35
|
+
raise "Unknown relation: #{relation.inspect}"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
class Node
|
|
42
|
+
attr_accessor :parent, :left, :right, :key, :value, :height
|
|
43
|
+
|
|
44
|
+
def initialize(parent, key)
|
|
45
|
+
@parent = parent
|
|
46
|
+
@left = nil
|
|
47
|
+
@right = nil
|
|
48
|
+
@key = key
|
|
49
|
+
@value = nil
|
|
50
|
+
@height = 1
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def copy(parent)
|
|
54
|
+
result = Node.new(@parent, @key)
|
|
55
|
+
if @left
|
|
56
|
+
result.left = @left.copy(result)
|
|
57
|
+
end
|
|
58
|
+
if @right
|
|
59
|
+
result.right = @right.copy(result)
|
|
60
|
+
end
|
|
61
|
+
result.value = @value
|
|
62
|
+
result.height = @height
|
|
63
|
+
result
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def set_value(new_value)
|
|
67
|
+
old_value = @value
|
|
68
|
+
@value = new_value
|
|
69
|
+
old_value
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def ==(other)
|
|
73
|
+
if other.is_a?(Node)
|
|
74
|
+
@key == other.key && @value == other.value
|
|
75
|
+
else
|
|
76
|
+
false
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
alias eql? ==
|
|
81
|
+
|
|
82
|
+
def hash
|
|
83
|
+
(key.nil? ? 0 : key.hash) ^ (value.nil? ? 0 : value.hash)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def to_s
|
|
87
|
+
"#{@key}=#{@value}"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Returns the next node in an inorder traversal, or null if this is the last node in the tree.
|
|
91
|
+
def next_node
|
|
92
|
+
return @right.first if @right
|
|
93
|
+
|
|
94
|
+
node = self
|
|
95
|
+
parent = node.parent
|
|
96
|
+
while parent
|
|
97
|
+
if parent.left == node
|
|
98
|
+
return parent
|
|
99
|
+
end
|
|
100
|
+
node = parent
|
|
101
|
+
parent = node.parent
|
|
102
|
+
end
|
|
103
|
+
nil
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Returns the previous node in an inorder traversal, or null if this is the first node in the tree.
|
|
107
|
+
def prev_node
|
|
108
|
+
return @left.last if @left
|
|
109
|
+
|
|
110
|
+
node = self
|
|
111
|
+
parent = node.parent
|
|
112
|
+
while parent
|
|
113
|
+
if parent.right == node
|
|
114
|
+
return parent
|
|
115
|
+
end
|
|
116
|
+
node = parent
|
|
117
|
+
parent = node.parent
|
|
118
|
+
end
|
|
119
|
+
nil
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Returns the first node in this subtree.
|
|
123
|
+
def first
|
|
124
|
+
node = self
|
|
125
|
+
child = node.left
|
|
126
|
+
while child
|
|
127
|
+
node = child
|
|
128
|
+
child = node.left
|
|
129
|
+
end
|
|
130
|
+
node
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Returns the last node in this subtree.
|
|
134
|
+
def last
|
|
135
|
+
node = self
|
|
136
|
+
child = node.right
|
|
137
|
+
while child
|
|
138
|
+
node = child
|
|
139
|
+
child = node.right
|
|
140
|
+
end
|
|
141
|
+
node
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
NaturalOrder = ->(this, that) { this <=> that }
|
|
146
|
+
|
|
147
|
+
attr_accessor :comparator, :root, :size
|
|
148
|
+
|
|
149
|
+
# comparator is a function of the form: (this, that) -> int ; where int is -1 if this < that, 0 if this == that, and 1 if this > that
|
|
150
|
+
def initialize(comparator = NaturalOrder)
|
|
151
|
+
@comparator = comparator
|
|
152
|
+
@root = nil
|
|
153
|
+
@size = 0
|
|
154
|
+
@mod_count = 0
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def empty?
|
|
158
|
+
@size == 0
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def get(key)
|
|
162
|
+
entry = find_by_object(key)
|
|
163
|
+
entry.value if entry
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
alias [] get
|
|
167
|
+
|
|
168
|
+
def contains_key?(key)
|
|
169
|
+
find_by_object(key)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def put(key, value)
|
|
173
|
+
put_internal(key, value)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def clear
|
|
177
|
+
@root = nil
|
|
178
|
+
@size = 0
|
|
179
|
+
@mod_count += 1
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def remove(key)
|
|
183
|
+
node = remove_internal_by_key(key)
|
|
184
|
+
node.value if node
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def put_internal(key, value)
|
|
188
|
+
created = find(key, Relation::CREATE)
|
|
189
|
+
result = created.value
|
|
190
|
+
created.value = value
|
|
191
|
+
result
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Returns the node at or adjacent to the given key, creating it if requested.
|
|
195
|
+
def find(key, relation)
|
|
196
|
+
if @root.nil?
|
|
197
|
+
if relation == Relation::CREATE
|
|
198
|
+
@root = Node.new(nil, key)
|
|
199
|
+
@size = 1
|
|
200
|
+
@mod_count += 1
|
|
201
|
+
return @root
|
|
202
|
+
else
|
|
203
|
+
return nil
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
nearest = @root
|
|
208
|
+
while true
|
|
209
|
+
comparison = @comparator.call(key, nearest.key)
|
|
210
|
+
|
|
211
|
+
# we found the requested key
|
|
212
|
+
if comparison == 0
|
|
213
|
+
case relation
|
|
214
|
+
when Relation::LOWER
|
|
215
|
+
return nearest.prev_node
|
|
216
|
+
when Relation::FLOOR, Relation::EQUAL, Relation::CREATE, Relation::CEILING
|
|
217
|
+
return nearest
|
|
218
|
+
when Relation::HIGHER
|
|
219
|
+
return nearest.next_node
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
child = (comparison < 0) ? nearest.left : nearest.right
|
|
224
|
+
if child
|
|
225
|
+
nearest = child
|
|
226
|
+
next # continue
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# We found a nearest node. Every key not in the tree has up to two nearest nodes, one lower and one higher.
|
|
230
|
+
if comparison < 0 # nearest.key is higher
|
|
231
|
+
case relation
|
|
232
|
+
when Relation::LOWER, Relation::FLOOR
|
|
233
|
+
return nearest.prev_node
|
|
234
|
+
when Relation::CEILING, Relation::HIGHER
|
|
235
|
+
return nearest
|
|
236
|
+
when Relation::EQUAL
|
|
237
|
+
return nil
|
|
238
|
+
when Relation::CREATE
|
|
239
|
+
created = Node.new(nearest, key)
|
|
240
|
+
nearest.left = created
|
|
241
|
+
@size += 1
|
|
242
|
+
@mod_count += 1
|
|
243
|
+
rebalance(nearest, true)
|
|
244
|
+
return created
|
|
245
|
+
end
|
|
246
|
+
else # comparison > 0 ; nearest.key is lower
|
|
247
|
+
case relation
|
|
248
|
+
when Relation::LOWER, Relation::FLOOR
|
|
249
|
+
return nearest
|
|
250
|
+
when Relation::CEILING, Relation::HIGHER
|
|
251
|
+
return nearest.next_node
|
|
252
|
+
when Relation::EQUAL
|
|
253
|
+
return nil
|
|
254
|
+
when Relation::CREATE
|
|
255
|
+
created = Node.new(nearest, key)
|
|
256
|
+
nearest.right = created
|
|
257
|
+
@size += 1
|
|
258
|
+
@mod_count += 1
|
|
259
|
+
rebalance(nearest, true)
|
|
260
|
+
return created
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# returns a Node
|
|
267
|
+
def find_by_object(key)
|
|
268
|
+
find(key, Relation::EQUAL)
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# entry is a key-value pair in an array: [key, value]
|
|
272
|
+
# Returns this map's entry that has the same key and value as <entry>, or null if this map has no such entry.
|
|
273
|
+
#
|
|
274
|
+
# This method uses the comparator for key equality rather than <equals>. If this map's comparator isn't consistent with equals,
|
|
275
|
+
# then {@code remove()} and {@code contains()} will violate the collections API.
|
|
276
|
+
#
|
|
277
|
+
# returns a Node
|
|
278
|
+
def find_by_entry(key, value)
|
|
279
|
+
key, value = *entry
|
|
280
|
+
mine = find_by_object(key)
|
|
281
|
+
mine if mine && mine.value == value
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
# Removes {@code node} from this tree, rearranging the tree's structure as necessary.
|
|
285
|
+
# return value not meaningful
|
|
286
|
+
def remove_internal(node)
|
|
287
|
+
left = node.left
|
|
288
|
+
right = node.right
|
|
289
|
+
original_parent = node.parent
|
|
290
|
+
|
|
291
|
+
if left && right
|
|
292
|
+
# To remove a node with both left and right subtrees, move an adjacent node from one of those subtrees into this node's place.
|
|
293
|
+
# Removing the adjacent node may change this node's subtrees. This node may no longer have two subtrees once the adjacent node is gone!
|
|
294
|
+
|
|
295
|
+
adjacent = left.height > right.height ? left.last : right.first
|
|
296
|
+
remove_internal(adjacent) # takes care of rebalance and size--
|
|
297
|
+
|
|
298
|
+
left_height = 0
|
|
299
|
+
left = node.left
|
|
300
|
+
if left
|
|
301
|
+
left_height = left.height
|
|
302
|
+
adjacent.left = left
|
|
303
|
+
left.parent = adjacent
|
|
304
|
+
node.left = nil
|
|
305
|
+
end
|
|
306
|
+
right_height = 0
|
|
307
|
+
right = node.right
|
|
308
|
+
if right
|
|
309
|
+
right_height = right.height
|
|
310
|
+
adjacent.right = right
|
|
311
|
+
right.parent = adjacent
|
|
312
|
+
node.right = nil
|
|
313
|
+
end
|
|
314
|
+
adjacent.height = [left_height, right_height].max + 1
|
|
315
|
+
replace_in_parent(node, adjacent)
|
|
316
|
+
return
|
|
317
|
+
elsif left
|
|
318
|
+
replace_in_parent(node, left)
|
|
319
|
+
node.left = nil
|
|
320
|
+
elsif right
|
|
321
|
+
replace_in_parent(node, right)
|
|
322
|
+
node.right = nil
|
|
323
|
+
else
|
|
324
|
+
replace_in_parent(node, nil)
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
rebalance(original_parent, false)
|
|
328
|
+
@size -= 1
|
|
329
|
+
@mod_count -= 1
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
def remove_internal_by_key(key)
|
|
333
|
+
node = find_by_object(key)
|
|
334
|
+
if node
|
|
335
|
+
remove_internal(node)
|
|
336
|
+
end
|
|
337
|
+
node
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
def replace_in_parent(node, replacement)
|
|
341
|
+
parent = node.parent
|
|
342
|
+
node.parent = nil
|
|
343
|
+
if replacement
|
|
344
|
+
replacement.parent = parent
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
if parent
|
|
348
|
+
if parent.left == node
|
|
349
|
+
parent.left = replacement
|
|
350
|
+
else
|
|
351
|
+
# assert (parent.right == node)
|
|
352
|
+
parent.right = replacement
|
|
353
|
+
end
|
|
354
|
+
else
|
|
355
|
+
@root = replacement
|
|
356
|
+
end
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
# Rebalances the tree by making any AVL rotations necessary between the newly-unbalanced node and the tree's root.
|
|
360
|
+
#
|
|
361
|
+
# @param insert true if the node was unbalanced by an insert; false if it was by a removal.
|
|
362
|
+
def rebalance(unbalanced, insert)
|
|
363
|
+
node = unbalanced
|
|
364
|
+
while node
|
|
365
|
+
left = node.left
|
|
366
|
+
right = node.right
|
|
367
|
+
left_height = left ? left.height : 0
|
|
368
|
+
right_height = right ? right.height : 0
|
|
369
|
+
|
|
370
|
+
delta = left_height - right_height
|
|
371
|
+
if delta == -2
|
|
372
|
+
right_left = right.left
|
|
373
|
+
right_right = right.right
|
|
374
|
+
right_right_height = right_right ? right_right.height : 0
|
|
375
|
+
right_left_height = right_left ? right_left.height : 0
|
|
376
|
+
|
|
377
|
+
right_delta = right_left_height - right_right_height
|
|
378
|
+
if right_delta == -1 || (right_delta == 0 && !insert)
|
|
379
|
+
rotate_left(node)
|
|
380
|
+
else
|
|
381
|
+
# assert (right_delta == 1)
|
|
382
|
+
rotate_right(right) # AVL right left
|
|
383
|
+
rotate_left(node)
|
|
384
|
+
end
|
|
385
|
+
break if insert # no further rotations will be necessary
|
|
386
|
+
elsif delta == 2
|
|
387
|
+
left_left = left.left
|
|
388
|
+
left_right = left.right
|
|
389
|
+
left_right_height = left_right ? left_right.height : 0
|
|
390
|
+
left_left_height = left_left ? left_left.height : 0
|
|
391
|
+
|
|
392
|
+
left_delta = left_left_height - left_right_height
|
|
393
|
+
if left_delta == 1 || (left_delta == 0 && !insert)
|
|
394
|
+
rotate_right(node) # AVL left left
|
|
395
|
+
else
|
|
396
|
+
# assert (left_delta == -1)
|
|
397
|
+
rotate_left(left) # AVL left right
|
|
398
|
+
rotate_right(node)
|
|
399
|
+
end
|
|
400
|
+
break if insert
|
|
401
|
+
elsif delta == 0
|
|
402
|
+
node.height = left_height + 1 # left_height == right_height
|
|
403
|
+
break if insert
|
|
404
|
+
else
|
|
405
|
+
# assert (delta == -1 || delta == 1)
|
|
406
|
+
node.height = [left_height, right_height].max + 1
|
|
407
|
+
break unless insert # the height hasn't changed, so rebalancing is done!
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
node = node.parent
|
|
411
|
+
end
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
# Rotates the subtree so that its root's right child is the new root
|
|
415
|
+
def rotate_left(root)
|
|
416
|
+
left = root.left
|
|
417
|
+
pivot = root.right
|
|
418
|
+
pivot_left = pivot.left
|
|
419
|
+
pivot_right = pivot.right
|
|
420
|
+
|
|
421
|
+
# move the pivot's left child to the root's right
|
|
422
|
+
root.right = pivot_left
|
|
423
|
+
if pivot_left
|
|
424
|
+
pivot_left.parent = root
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
replace_in_parent(root, pivot)
|
|
428
|
+
|
|
429
|
+
# move the root to the pivot's left
|
|
430
|
+
pivot.left = root
|
|
431
|
+
root.parent = pivot
|
|
432
|
+
|
|
433
|
+
# fix heights
|
|
434
|
+
root.height = [left ? left.height : 0, pivot_left ? pivot_left.height : 0].max + 1
|
|
435
|
+
pivot.height = [root.height, pivot_right ? pivot_right.height : 0].max + 1
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
# Rotates the subtree so that its root's left child is the new root
|
|
439
|
+
def rotate_right(root)
|
|
440
|
+
pivot = root.left
|
|
441
|
+
right = root.right
|
|
442
|
+
pivot_left = pivot.left
|
|
443
|
+
pivot_right = pivot.right
|
|
444
|
+
|
|
445
|
+
# move the pivot's right child to the root's left
|
|
446
|
+
root.left = pivot_right
|
|
447
|
+
if pivot_right
|
|
448
|
+
pivot_right.parent = root
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
replace_in_parent(root, pivot)
|
|
452
|
+
|
|
453
|
+
# move the root to the pivot's right
|
|
454
|
+
pivot.right = root
|
|
455
|
+
root.parent = pivot
|
|
456
|
+
|
|
457
|
+
# fix heights
|
|
458
|
+
root.height = [right ? right.height : 0, pivot_right ? pivot_right.height : 0].max + 1
|
|
459
|
+
pivot.height = [root.height, pivot_left ? pivot_left.height : 0].max + 1
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
# Navigable Methods
|
|
463
|
+
|
|
464
|
+
# Returns a key-value mapping associated with the least key in this map, or null if the map is empty.
|
|
465
|
+
def first_entry
|
|
466
|
+
root.first if root
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
def internal_poll_first_entry
|
|
470
|
+
if root
|
|
471
|
+
result = root.first
|
|
472
|
+
remove_internal(result)
|
|
473
|
+
result
|
|
474
|
+
end
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
def poll_first_entry
|
|
478
|
+
internal_poll_first_entry
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
def first_key
|
|
482
|
+
raise "No such element." unless root
|
|
483
|
+
root.first.key
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
# Returns a key-value mapping associated with the greatest key in this map, or null if the map is empty.
|
|
487
|
+
def last_entry
|
|
488
|
+
root.last if root
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
def internal_poll_last_entry
|
|
492
|
+
if root
|
|
493
|
+
result = root.last
|
|
494
|
+
remove_internal(result)
|
|
495
|
+
result
|
|
496
|
+
end
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
def poll_last_entry
|
|
500
|
+
internal_poll_last_entry
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
def last_key
|
|
504
|
+
raise "No such element." unless root
|
|
505
|
+
root.last.key
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
# Returns a key-value mapping associated with the greatest key strictly less than the given key, or null if there is no such key.
|
|
509
|
+
def lower_entry(key)
|
|
510
|
+
find(key, Relation::LOWER)
|
|
511
|
+
end
|
|
512
|
+
|
|
513
|
+
# Returns the greatest key strictly less than the given key, or null if there is no such key.
|
|
514
|
+
def lower_key(key)
|
|
515
|
+
entry = find(key, Relation::LOWER)
|
|
516
|
+
entry.key if entry
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
# Returns a key-value mapping associated with the greatest key less than or equal to the given key, or null if there is no such key.
|
|
520
|
+
def floor_entry(key)
|
|
521
|
+
find(key, Relation::FLOOR)
|
|
522
|
+
end
|
|
523
|
+
|
|
524
|
+
# Returns the greatest key less than or equal to the given key, or null if there is no such key.
|
|
525
|
+
def floor_key(key)
|
|
526
|
+
entry = find(key, Relation::FLOOR)
|
|
527
|
+
entry.key if entry
|
|
528
|
+
end
|
|
529
|
+
|
|
530
|
+
# Returns a key-value mapping associated with the least key greater than or equal to the given key, or null if there is no such key.
|
|
531
|
+
def ceiling_entry(key)
|
|
532
|
+
find(key, Relation::CEILING)
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
# Returns the least key greater than or equal to the given key, or null if there is no such key.
|
|
536
|
+
def ceiling_key(key)
|
|
537
|
+
entry = find(key, Relation::CEILING)
|
|
538
|
+
entry.key if entry
|
|
539
|
+
end
|
|
540
|
+
|
|
541
|
+
# Returns a key-value mapping associated with the least key strictly greater than the given key, or null if there is no such key.
|
|
542
|
+
def higher_entry(key)
|
|
543
|
+
find(key, Relation::HIGHER)
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
# Returns the least key strictly greater than the given key, or null if there is no such key.
|
|
547
|
+
def higher_key(key)
|
|
548
|
+
entry = find(key, Relation::HIGHER)
|
|
549
|
+
entry.key if entry
|
|
550
|
+
end
|
|
551
|
+
|
|
552
|
+
# View factory methods
|
|
553
|
+
|
|
554
|
+
def entry_set
|
|
555
|
+
Set.new(each_node.to_a)
|
|
556
|
+
end
|
|
557
|
+
|
|
558
|
+
def key_set
|
|
559
|
+
Set.new(each_node.map(&:key))
|
|
560
|
+
end
|
|
561
|
+
|
|
562
|
+
alias keys key_set
|
|
563
|
+
|
|
564
|
+
def values
|
|
565
|
+
each_node.map(&:value)
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
# This can be called in 1 of 2 ways:
|
|
569
|
+
# sub_map(from_inclusive, to_exclusive)
|
|
570
|
+
# OR
|
|
571
|
+
# sub_map(from, from_inclusive, to, to_inclusive)
|
|
572
|
+
def sub_map(*args)
|
|
573
|
+
case args.count
|
|
574
|
+
when 2
|
|
575
|
+
from_inclusive, to_exclusive = *args
|
|
576
|
+
BoundedMap.new(self, true, from_inclusive, Bound::INCLUSIVE, to_exclusive, Bound::EXCLUSIVE)
|
|
577
|
+
when 4
|
|
578
|
+
from, from_inclusive, to, to_inclusive = *args
|
|
579
|
+
from_bound = from_inclusive ? Bound::INCLUSIVE : Bound::EXCLUSIVE
|
|
580
|
+
to_bound = to_inclusive ? Bound::INCLUSIVE : Bound::EXCLUSIVE
|
|
581
|
+
BoundedMap.new(self, true, from, from_bound, to, to_bound);
|
|
582
|
+
end
|
|
583
|
+
end
|
|
584
|
+
|
|
585
|
+
# This can be called in 1 of 2 ways:
|
|
586
|
+
# head_map(to_exclusive)
|
|
587
|
+
# OR
|
|
588
|
+
# head_map(to, inclusive)
|
|
589
|
+
def head_map(*args)
|
|
590
|
+
case args.count
|
|
591
|
+
when 1
|
|
592
|
+
to_exclusive = args.first
|
|
593
|
+
BoundedMap.new(self, true, nil, Bound::NO_BOUND, to_exclusive, Bound::EXCLUSIVE)
|
|
594
|
+
when 2
|
|
595
|
+
to, inclusive = *args
|
|
596
|
+
to_bound = inclusive ? Bound::INCLUSIVE : Bound::EXCLUSIVE
|
|
597
|
+
BoundedMap.new(self, true, nil, Bound::NO_BOUND, to, to_bound)
|
|
598
|
+
end
|
|
599
|
+
end
|
|
600
|
+
|
|
601
|
+
# This can be called in 1 of 2 ways:
|
|
602
|
+
# tail_map(from_inclusive)
|
|
603
|
+
# OR
|
|
604
|
+
# tail_map(from, inclusive)
|
|
605
|
+
def tail_map(*args)
|
|
606
|
+
case args.count
|
|
607
|
+
when 1
|
|
608
|
+
from_inclusive = args.first
|
|
609
|
+
BoundedMap.new(self, true, from_inclusive, Bound::INCLUSIVE, nil, Bound::NO_BOUND)
|
|
610
|
+
when 2
|
|
611
|
+
from, inclusive = *args
|
|
612
|
+
from_bound = inclusive ? Bound::INCLUSIVE : Bound::EXCLUSIVE
|
|
613
|
+
BoundedMap.new(self, true, from, from_bound, nil, Bound::NO_BOUND)
|
|
614
|
+
end
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
def descending_map
|
|
618
|
+
BoundedMap.new(self, false, nil, Bound::NO_BOUND, nil, Bound::NO_BOUND)
|
|
619
|
+
end
|
|
620
|
+
|
|
621
|
+
# Tree traversal methods
|
|
622
|
+
|
|
623
|
+
# in-order traversal of nodes in tree
|
|
624
|
+
class NodeIterator
|
|
625
|
+
def initialize(next_node)
|
|
626
|
+
@next_node = next_node
|
|
627
|
+
@last_node = nil
|
|
628
|
+
end
|
|
629
|
+
|
|
630
|
+
def has_next?
|
|
631
|
+
!!@next_node
|
|
632
|
+
end
|
|
633
|
+
|
|
634
|
+
def step_forward
|
|
635
|
+
if @next_node
|
|
636
|
+
@last_node = @next_node
|
|
637
|
+
@next_node = @next_node.next_node
|
|
638
|
+
@last_node
|
|
639
|
+
end
|
|
640
|
+
end
|
|
641
|
+
|
|
642
|
+
def step_backward
|
|
643
|
+
if @next_node
|
|
644
|
+
@last_node = @next_node
|
|
645
|
+
@next_node = @next_node.prev_node
|
|
646
|
+
@last_node
|
|
647
|
+
end
|
|
648
|
+
end
|
|
649
|
+
end
|
|
650
|
+
|
|
651
|
+
# each {|k,v| puts "#{k}->#{v}"}
|
|
652
|
+
def each(&blk)
|
|
653
|
+
if block_given?
|
|
654
|
+
each_node {|node| blk.call(node.key, node.value) }
|
|
655
|
+
else
|
|
656
|
+
enum_for(:each)
|
|
657
|
+
end
|
|
658
|
+
end
|
|
659
|
+
|
|
660
|
+
# each_node {|node| puts "#{node.key}->#{node.value}"}
|
|
661
|
+
def each_node
|
|
662
|
+
if block_given?
|
|
663
|
+
iter = NodeIterator.new(empty? ? nil : @root.first)
|
|
664
|
+
while iter.has_next?
|
|
665
|
+
yield iter.step_forward()
|
|
666
|
+
end
|
|
667
|
+
else
|
|
668
|
+
enum_for(:each_node)
|
|
669
|
+
end
|
|
670
|
+
end
|
|
671
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: treemap-fork
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.4.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- David Ellis
|
|
8
|
+
- Rico Jasper
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2017-12-10 00:00:00.000000000 Z
|
|
13
|
+
dependencies: []
|
|
14
|
+
description: |2
|
|
15
|
+
A Ruby port of the Android implementation of Java's java.util.TreeMap class.
|
|
16
|
+
This is an AVL tree based implementation of Java's java.util.TreeMap structure.
|
|
17
|
+
It implements Java's java.util.NavigableMap interface.
|
|
18
|
+
The reference implementation is at https://android.googlesource.com/platform/libcore.git/+/android-6.0.1_r32/luni/src/main/java/java/util/TreeMap.java
|
|
19
|
+
email:
|
|
20
|
+
- davidkellis@gmail.com
|
|
21
|
+
- jasper.rico@gmail.com
|
|
22
|
+
executables: []
|
|
23
|
+
extensions: []
|
|
24
|
+
extra_rdoc_files: []
|
|
25
|
+
files:
|
|
26
|
+
- lib/treemap-fork.rb
|
|
27
|
+
- lib/treemap/bounded_map.rb
|
|
28
|
+
- lib/treemap/tree_map.rb
|
|
29
|
+
homepage: https://github.com/rjasper/treemap
|
|
30
|
+
licenses:
|
|
31
|
+
- MIT
|
|
32
|
+
metadata: {}
|
|
33
|
+
post_install_message:
|
|
34
|
+
rdoc_options: []
|
|
35
|
+
require_paths:
|
|
36
|
+
- lib
|
|
37
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
38
|
+
requirements:
|
|
39
|
+
- - ">="
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
version: '0'
|
|
42
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0'
|
|
47
|
+
requirements: []
|
|
48
|
+
rubyforge_project:
|
|
49
|
+
rubygems_version: 2.6.12
|
|
50
|
+
signing_key:
|
|
51
|
+
specification_version: 4
|
|
52
|
+
summary: TreeMap is a Ruby port of the Android implementation of Java's java.util.TreeMap
|
|
53
|
+
class.
|
|
54
|
+
test_files: []
|