tree_map 1.0.4

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.
@@ -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(@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
@@ -0,0 +1,3 @@
1
+ module TreeMap
2
+ VERSION = "1.0.4"
3
+ end
data/lib/tree_map.rb ADDED
@@ -0,0 +1,3 @@
1
+ require "tree_map/version"
2
+ require 'treemap/tree_map'
3
+ require 'treemap/bounded_map'
data/tree_map.gemspec ADDED
@@ -0,0 +1,34 @@
1
+ lib = File.expand_path("lib", __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "tree_map/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "tree_map"
7
+ spec.version = TreeMap::VERSION
8
+ spec.authors = ["tianl677"]
9
+ spec.email = ["tianlu1677@gmail.com"]
10
+
11
+ spec.summary = %q{avl tree }
12
+ spec.description = %q{avl tree .}
13
+ spec.homepage = "https://github.com/tianlu1677/tree_map"
14
+ spec.license = "MIT"
15
+
16
+ # spec.metadata["allowed_push_host"] = "https://github.com/tianlu1677/tree_map"
17
+
18
+ spec.metadata["homepage_uri"] = spec.homepage
19
+ spec.metadata["source_code_uri"] = "https://github.com/tianlu1677/tree_map."
20
+ spec.metadata["changelog_uri"] = "https://github.com/tianlu1677/tree_map."
21
+
22
+ # Specify which files should be added to the gem when it is released.
23
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
25
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
+ end
27
+ spec.bindir = "exe"
28
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
29
+ spec.require_paths = ["lib"]
30
+
31
+ spec.add_development_dependency "bundler", "~> 2.0"
32
+ spec.add_development_dependency "rake", "~> 10.0"
33
+ spec.add_development_dependency "minitest", "~> 5.0"
34
+ end