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 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
@@ -0,0 +1,2 @@
1
+ require 'treemap/tree_map'
2
+ require 'treemap/bounded_map'
@@ -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: []