treemap 1.0.0

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