treemap-fork 1.0.4.1

Sign up to get free protection for your applications and to get access to all the features.
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: []