treemap 1.0.0

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.
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: []