rbtree-ruby 0.3.5 → 0.4.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +124 -0
- data/README.ja.md +154 -7
- data/README.md +156 -7
- data/Rakefile +7 -0
- data/lib/rbtree/version.rb +1 -1
- data/lib/rbtree.rb +604 -87
- metadata +1 -1
data/lib/rbtree.rb
CHANGED
|
@@ -54,6 +54,29 @@ require_relative "rbtree/version"
|
|
|
54
54
|
class RBTree
|
|
55
55
|
include Enumerable
|
|
56
56
|
|
|
57
|
+
# Key classes where <tt>(a <=> b) == 0</tt> and <tt>a.eql?(b)</tt> agree for any two
|
|
58
|
+
# instances of that same class, letting {#find_node} skip its fallback descent.
|
|
59
|
+
COHERENT_KEY_CLASSES = [
|
|
60
|
+
Integer, Float, Rational, String, Symbol, Time,
|
|
61
|
+
].to_h { |klass| [klass, true] }
|
|
62
|
+
private_constant :COHERENT_KEY_CLASSES
|
|
63
|
+
|
|
64
|
+
# Declares that <tt>(a <=> b) == 0</tt> and +a.eql?(b)+ agree for instances of the
|
|
65
|
+
# given key class, speeding up lookups that miss the internal hash index.
|
|
66
|
+
#
|
|
67
|
+
# Optimization hint only: undeclared classes are still handled correctly, at
|
|
68
|
+
# O(log n) per missed lookup. Declaring a class that does not satisfy this makes
|
|
69
|
+
# such lookups report a present key as absent.
|
|
70
|
+
#
|
|
71
|
+
# @param klass [Class] the key class to declare coherent
|
|
72
|
+
# @return [Class] the declared class
|
|
73
|
+
# @example
|
|
74
|
+
# RBTree.coherent_key_class(Version)
|
|
75
|
+
def self.coherent_key_class(klass)
|
|
76
|
+
COHERENT_KEY_CLASSES[klass] = true
|
|
77
|
+
klass
|
|
78
|
+
end
|
|
79
|
+
|
|
57
80
|
# Returns the number of key-value pairs stored in the tree.
|
|
58
81
|
# @return [Integer] the number of entries in the tree
|
|
59
82
|
attr_reader :key_count
|
|
@@ -100,9 +123,12 @@ class RBTree
|
|
|
100
123
|
@root = @nil_node
|
|
101
124
|
@min_node = @nil_node
|
|
102
125
|
@max_node = @nil_node
|
|
103
|
-
@hash_index = {} # Hash index for O(1) key lookup
|
|
126
|
+
@hash_index = {} # Hash index for O(1) key lookup, one entry per node
|
|
104
127
|
@node_allocator = node_allocator
|
|
105
128
|
@key_count = 0
|
|
129
|
+
@mod_count = 0 # bumped by changes a traversal can observe
|
|
130
|
+
@key_class = nil # single class of all keys, or false once mixed
|
|
131
|
+
@coherent_keys = false
|
|
106
132
|
|
|
107
133
|
@overwrite = overwrite
|
|
108
134
|
|
|
@@ -111,10 +137,28 @@ class RBTree
|
|
|
111
137
|
end
|
|
112
138
|
end
|
|
113
139
|
|
|
114
|
-
#
|
|
140
|
+
# Creates a deep copy of the tree.
|
|
141
|
+
# Called automatically by `dup` and `clone`.
|
|
142
|
+
#
|
|
143
|
+
# The copy shares the original's node allocator instance.
|
|
144
|
+
#
|
|
145
|
+
# @param orig [RBTree] the original tree to copy
|
|
146
|
+
# @return [void]
|
|
147
|
+
def initialize_copy(orig)
|
|
148
|
+
initialize(
|
|
149
|
+
overwrite: orig.instance_variable_get(:@overwrite),
|
|
150
|
+
node_allocator: orig.instance_variable_get(:@node_allocator))
|
|
151
|
+
orig.each { |k, v| insert(k, v) }
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Returns a Hash containing all key-value pairs from the tree, in ascending key order.
|
|
115
155
|
#
|
|
116
156
|
# @return [Hash] a new Hash with the tree's contents
|
|
117
|
-
def to_h
|
|
157
|
+
def to_h
|
|
158
|
+
h = {}
|
|
159
|
+
each { |k, v| h[k] = v }
|
|
160
|
+
h
|
|
161
|
+
end
|
|
118
162
|
|
|
119
163
|
# Checks if the tree is empty.
|
|
120
164
|
#
|
|
@@ -136,11 +180,19 @@ class RBTree
|
|
|
136
180
|
|
|
137
181
|
# Returns the minimum key-value pair without removing it.
|
|
138
182
|
#
|
|
139
|
-
#
|
|
183
|
+
# With no argument and no block this is an O(1) cached lookup; given a count or a
|
|
184
|
+
# comparison block, `Enumerable#min` semantics apply instead.
|
|
185
|
+
#
|
|
186
|
+
# @param args [Integer] optional number of smallest pairs to return
|
|
187
|
+
# @return [Array, nil] the pair, or the n smallest pairs, or nil if tree is empty
|
|
140
188
|
# @example
|
|
141
189
|
# tree = RBTree.new({3 => 'three', 1 => 'one', 2 => 'two'})
|
|
142
|
-
# tree.min
|
|
143
|
-
|
|
190
|
+
# tree.min # => [1, "one"]
|
|
191
|
+
# tree.min(2) # => [[1, "one"], [2, "two"]]
|
|
192
|
+
def min(*args, &block)
|
|
193
|
+
return super if !args.empty? || block
|
|
194
|
+
min_node&.pair
|
|
195
|
+
end
|
|
144
196
|
|
|
145
197
|
# Returns the maximum key without removing it.
|
|
146
198
|
#
|
|
@@ -152,37 +204,59 @@ class RBTree
|
|
|
152
204
|
|
|
153
205
|
# Returns the maximum key-value pair without removing it.
|
|
154
206
|
#
|
|
155
|
-
#
|
|
207
|
+
# With no argument and no block this is an O(1) cached lookup; given a count or a
|
|
208
|
+
# comparison block, `Enumerable#max` semantics apply instead.
|
|
209
|
+
#
|
|
210
|
+
# @param args [Integer] optional number of largest pairs to return
|
|
211
|
+
# @return [Array, nil] the pair, or the n largest pairs, or nil if tree is empty
|
|
156
212
|
# @example
|
|
157
213
|
# tree = RBTree.new({3 => 'three', 1 => 'one', 2 => 'two'})
|
|
158
|
-
# tree.max
|
|
159
|
-
|
|
214
|
+
# tree.max # => [3, "three"]
|
|
215
|
+
# tree.max(2) # => [[3, "three"], [2, "two"]]
|
|
216
|
+
def max(*args, &block)
|
|
217
|
+
return super if !args.empty? || block
|
|
218
|
+
max_node&.pair
|
|
219
|
+
end
|
|
160
220
|
|
|
161
|
-
# Returns the first key-value pair without removing
|
|
221
|
+
# Returns the first key-value pair, or the first +n+ pairs, without removing them.
|
|
162
222
|
#
|
|
163
|
-
# @
|
|
223
|
+
# @param n [Integer, nil] number of leading pairs to return
|
|
224
|
+
# @return [Array, nil] the pair, or the n smallest pairs in ascending order
|
|
164
225
|
# @example
|
|
165
226
|
# tree = RBTree.new({3 => 'three', 1 => 'one', 2 => 'two'})
|
|
166
|
-
# tree.first
|
|
167
|
-
|
|
227
|
+
# tree.first # => [1, "one"]
|
|
228
|
+
# tree.first(2) # => [[1, "one"], [2, "two"]]
|
|
229
|
+
def first(n = nil) = n.nil? ? min : take(n)
|
|
168
230
|
|
|
169
|
-
# Returns the last key-value pair without removing
|
|
231
|
+
# Returns the last key-value pair, or the last +n+ pairs, without removing them.
|
|
170
232
|
#
|
|
171
|
-
# @
|
|
233
|
+
# @param n [Integer, nil] number of trailing pairs to return
|
|
234
|
+
# @return [Array, nil] the pair, or the n largest pairs in ascending order
|
|
172
235
|
# @example
|
|
173
236
|
# tree = RBTree.new({3 => 'three', 1 => 'one', 2 => 'two'})
|
|
174
|
-
# tree.last
|
|
175
|
-
|
|
237
|
+
# tree.last # => [3, "three"]
|
|
238
|
+
# tree.last(2) # => [[2, "two"], [3, "three"]]
|
|
239
|
+
def last(n = nil)
|
|
240
|
+
return max if n.nil?
|
|
241
|
+
result = []
|
|
242
|
+
reverse_each { |pair| break if result.size >= n; result << pair }
|
|
243
|
+
result.reverse!
|
|
244
|
+
result
|
|
245
|
+
end
|
|
176
246
|
|
|
177
247
|
# Checks if the tree contains the given key.
|
|
178
248
|
#
|
|
249
|
+
# A key counts as present when <tt>(key <=> stored_key) == 0</tt>, even if the two
|
|
250
|
+
# objects are not +eql?+ (e.g. +1.0+ finds a stored +1+).
|
|
251
|
+
#
|
|
179
252
|
# @param key [Object] the key to search for
|
|
180
253
|
# @return [Boolean] true if the key exists in the tree, false otherwise
|
|
181
254
|
# @example
|
|
182
255
|
# tree = RBTree.new({1 => 'one', 2 => 'two'})
|
|
183
|
-
# tree.key?(1)
|
|
184
|
-
# tree.key?(
|
|
185
|
-
|
|
256
|
+
# tree.key?(1) # => true
|
|
257
|
+
# tree.key?(1.0) # => true
|
|
258
|
+
# tree.key?(3) # => false
|
|
259
|
+
def has_key?(key) = @hash_index.key?(key) || !find_node_by_order(key).nil?
|
|
186
260
|
alias :key? :has_key?
|
|
187
261
|
|
|
188
262
|
# Retrieves the value associated with the given key.
|
|
@@ -192,7 +266,7 @@ class RBTree
|
|
|
192
266
|
# @example
|
|
193
267
|
# tree = RBTree.new({1 => 'one', 2 => 'two'})
|
|
194
268
|
# tree.get(1) # => "one"
|
|
195
|
-
def value(key) = @hash_index[key]&.value
|
|
269
|
+
def value(key) = (@hash_index[key] || find_node_by_order(key))&.value
|
|
196
270
|
alias :get :value
|
|
197
271
|
|
|
198
272
|
# Retrieves a value associated with the given key, or a range of entries if a Range is provided.
|
|
@@ -353,6 +427,9 @@ class RBTree
|
|
|
353
427
|
raise ArgumentError, "Source must be iterable"
|
|
354
428
|
end
|
|
355
429
|
|
|
430
|
+
# Self-insertion (e.g. tree.merge!(tree)) must not mutate what it iterates.
|
|
431
|
+
source = source.to_a if source.equal?(self)
|
|
432
|
+
|
|
356
433
|
source.each do |*pair|
|
|
357
434
|
key, value = nil, nil
|
|
358
435
|
if pair.size == 1 && pair[0].is_a?(Array)
|
|
@@ -369,21 +446,54 @@ class RBTree
|
|
|
369
446
|
end
|
|
370
447
|
alias :[]= :insert
|
|
371
448
|
|
|
449
|
+
# Returns a new tree containing the merged contents of self and other.
|
|
450
|
+
#
|
|
451
|
+
# When a block is given, it is called with (key, old_value, new_value) for
|
|
452
|
+
# duplicate keys, and the block's return value is used.
|
|
453
|
+
#
|
|
454
|
+
# @param other [RBTree, Hash, Enumerable] the source to merge from
|
|
455
|
+
# @yield [key, old_value, new_value] called for duplicate keys when block given
|
|
456
|
+
# @return [RBTree] a new tree with merged contents
|
|
457
|
+
def merge(other, &block)
|
|
458
|
+
dup.merge!(other, &block)
|
|
459
|
+
end
|
|
460
|
+
|
|
372
461
|
# Merges the contents of another tree, hash, or enumerable into this tree.
|
|
373
462
|
#
|
|
463
|
+
# When a block is given, it is called with (key, old_value, new_value) for
|
|
464
|
+
# duplicate keys, and the block's return value is used.
|
|
465
|
+
#
|
|
374
466
|
# @param other [RBTree, Hash, Enumerable] the source to merge from
|
|
375
|
-
# @param overwrite [Boolean] whether to overwrite existing keys (default: true)
|
|
467
|
+
# @param overwrite [Boolean] whether to overwrite existing keys (default: true). Ignored if block given.
|
|
468
|
+
# @yield [key, old_value, new_value] called for duplicate keys when block given
|
|
376
469
|
# @return [RBTree] self
|
|
377
|
-
def merge!(other, overwrite: true)
|
|
470
|
+
def merge!(other, overwrite: true, &block)
|
|
378
471
|
if defined?(MultiRBTree) && other.is_a?(MultiRBTree)
|
|
379
472
|
raise ArgumentError, "Cannot merge MultiRBTree into RBTree"
|
|
380
473
|
end
|
|
381
|
-
|
|
474
|
+
if block
|
|
475
|
+
other_enum = other.is_a?(Hash) || other.is_a?(RBTree) ? other : other.each
|
|
476
|
+
# Snapshot on self-merge; see RBTree#insert.
|
|
477
|
+
other_enum = other_enum.to_a if other_enum.equal?(self)
|
|
478
|
+
other_enum.each do |k, v|
|
|
479
|
+
if has_key?(k)
|
|
480
|
+
insert_entry(k, block.call(k, value(k), v), overwrite: true)
|
|
481
|
+
else
|
|
482
|
+
insert_entry(k, v)
|
|
483
|
+
end
|
|
484
|
+
end
|
|
485
|
+
else
|
|
486
|
+
insert(other, overwrite: overwrite)
|
|
487
|
+
end
|
|
382
488
|
self
|
|
383
489
|
end
|
|
384
490
|
|
|
385
491
|
# Deletes the key-value pair with the specified key.
|
|
386
492
|
#
|
|
493
|
+
# Entries whose value is +nil+ or +false+ are deleted correctly, so the return
|
|
494
|
+
# value alone cannot distinguish them from a missing key; use {#has_key?} first
|
|
495
|
+
# if that matters.
|
|
496
|
+
#
|
|
387
497
|
# @param key [Object] the key to delete
|
|
388
498
|
# @return [Object, nil] the value associated with the deleted key, or nil if not found
|
|
389
499
|
# @example
|
|
@@ -391,8 +501,9 @@ class RBTree
|
|
|
391
501
|
# tree.delete(1) # => "one"
|
|
392
502
|
# tree.delete(3) # => nil
|
|
393
503
|
def delete_key(key)
|
|
394
|
-
return nil unless (
|
|
395
|
-
|
|
504
|
+
return nil unless (z = find_node(key))
|
|
505
|
+
value = z.value
|
|
506
|
+
delete_found_node(z)
|
|
396
507
|
value
|
|
397
508
|
end
|
|
398
509
|
alias :delete :delete_key
|
|
@@ -427,11 +538,18 @@ class RBTree
|
|
|
427
538
|
|
|
428
539
|
# Removes all key-value pairs from the tree.
|
|
429
540
|
#
|
|
541
|
+
# Runs in O(1): the nodes are left to the garbage collector, and the allocator is
|
|
542
|
+
# told of the bulk discard so that pool statistics stay accurate.
|
|
543
|
+
#
|
|
430
544
|
# @return [RBTree] self
|
|
431
545
|
def clear
|
|
546
|
+
@node_allocator.discard(@key_count) if @key_count > 0
|
|
432
547
|
@root = @min_node = @max_node = @nil_node
|
|
433
548
|
@hash_index.clear
|
|
434
549
|
@key_count = 0
|
|
550
|
+
@key_class = nil
|
|
551
|
+
@coherent_keys = false
|
|
552
|
+
@mod_count += 1
|
|
435
553
|
self
|
|
436
554
|
end
|
|
437
555
|
|
|
@@ -457,7 +575,8 @@ class RBTree
|
|
|
457
575
|
# tree.delete(k) if k.even?
|
|
458
576
|
# end
|
|
459
577
|
def keys(reverse: false, safe: false, &block)
|
|
460
|
-
|
|
578
|
+
# `size`, not `key_count`: MultiRBTree yields a key once per value it holds.
|
|
579
|
+
return enum_for(__method__, reverse: reverse, safe: safe) { size } unless block_given?
|
|
461
580
|
each(reverse: reverse, safe: safe) { |key, _| yield key }
|
|
462
581
|
self
|
|
463
582
|
end
|
|
@@ -602,6 +721,72 @@ class RBTree
|
|
|
602
721
|
self
|
|
603
722
|
end
|
|
604
723
|
|
|
724
|
+
# Returns a new tree containing key-value pairs for which the block returns true.
|
|
725
|
+
#
|
|
726
|
+
# @yield [key, value] each key-value pair
|
|
727
|
+
# @return [RBTree, Enumerator] a new tree with selected pairs, or Enumerator if no block
|
|
728
|
+
def select(&block)
|
|
729
|
+
return enum_for(__method__) { size } unless block_given?
|
|
730
|
+
result = new_derived_tree
|
|
731
|
+
each { |k, v| result.insert(k, v) if block.call(k, v) }
|
|
732
|
+
result
|
|
733
|
+
end
|
|
734
|
+
|
|
735
|
+
# Returns a new tree containing key-value pairs for which the block returns false.
|
|
736
|
+
#
|
|
737
|
+
# @yield [key, value] each key-value pair
|
|
738
|
+
# @return [RBTree, Enumerator] a new tree with non-rejected pairs, or Enumerator if no block
|
|
739
|
+
def reject(&block)
|
|
740
|
+
return enum_for(__method__) { size } unless block_given?
|
|
741
|
+
result = new_derived_tree
|
|
742
|
+
each { |k, v| result.insert(k, v) unless block.call(k, v) }
|
|
743
|
+
result
|
|
744
|
+
end
|
|
745
|
+
|
|
746
|
+
# Deletes key-value pairs for which the block returns true. Returns nil if no changes were made.
|
|
747
|
+
#
|
|
748
|
+
# @yield [key, value] each key-value pair
|
|
749
|
+
# @return [RBTree, nil, Enumerator] self if changed, nil if unchanged, or Enumerator if no block
|
|
750
|
+
def reject!(&block)
|
|
751
|
+
return enum_for(__method__) { size } unless block_given?
|
|
752
|
+
size_before = size
|
|
753
|
+
delete_if(&block)
|
|
754
|
+
size == size_before ? nil : self
|
|
755
|
+
end
|
|
756
|
+
|
|
757
|
+
# Keeps key-value pairs for which the block returns true, deleting the rest. Modifies the tree in place.
|
|
758
|
+
#
|
|
759
|
+
# @yield [key, value] each key-value pair
|
|
760
|
+
# @return [RBTree, Enumerator] self, or Enumerator if no block
|
|
761
|
+
def keep_if(&block)
|
|
762
|
+
return enum_for(__method__) { size } unless block_given?
|
|
763
|
+
each(safe: true) { |k, v| delete(k) unless block.call(k, v) }
|
|
764
|
+
self
|
|
765
|
+
end
|
|
766
|
+
|
|
767
|
+
# Deletes key-value pairs for which the block returns true. Modifies the tree in place.
|
|
768
|
+
#
|
|
769
|
+
# @yield [key, value] each key-value pair
|
|
770
|
+
# @return [RBTree, Enumerator] self, or Enumerator if no block
|
|
771
|
+
def delete_if(&block)
|
|
772
|
+
return enum_for(__method__) { size } unless block_given?
|
|
773
|
+
each(safe: true) { |k, v| delete(k) if block.call(k, v) }
|
|
774
|
+
self
|
|
775
|
+
end
|
|
776
|
+
|
|
777
|
+
# Returns a new tree with keys and values swapped.
|
|
778
|
+
#
|
|
779
|
+
# For RBTree, duplicate values result in later keys overwriting earlier ones.
|
|
780
|
+
# For MultiRBTree, all key-value pairs are preserved.
|
|
781
|
+
# Values must implement <=> to serve as keys in the new tree.
|
|
782
|
+
#
|
|
783
|
+
# @return [RBTree, MultiRBTree] a new tree with keys and values inverted
|
|
784
|
+
def invert
|
|
785
|
+
result = new_derived_tree
|
|
786
|
+
each { |k, v| result.insert(v, k) }
|
|
787
|
+
result
|
|
788
|
+
end
|
|
789
|
+
|
|
605
790
|
# Returns a string representation of the tree.
|
|
606
791
|
#
|
|
607
792
|
# Shows the first 5 entries and total size. Useful for debugging.
|
|
@@ -613,19 +798,41 @@ class RBTree
|
|
|
613
798
|
"#<#{self.class}:0x#{object_id.to_s(16)} size=#{size} {#{content}#{suffix}}>"
|
|
614
799
|
end
|
|
615
800
|
|
|
616
|
-
# Validates the red-black tree properties.
|
|
801
|
+
# Validates the red-black tree properties and the auxiliary structures.
|
|
617
802
|
#
|
|
618
803
|
# Checks that:
|
|
619
|
-
# 1. Root is black
|
|
804
|
+
# 1. Root is black and has no parent
|
|
620
805
|
# 2. All paths from root to leaves have the same number of black nodes
|
|
621
806
|
# 3. No red node has a red child
|
|
622
|
-
# 4. Keys are
|
|
807
|
+
# 4. Keys are ordered against bounds inherited from ancestors
|
|
808
|
+
# 5. Every child's parent pointer points back at its parent
|
|
809
|
+
# 6. The Hash index holds exactly one entry per node, mapped to that node
|
|
810
|
+
# 7. `@min_node` / `@max_node` are the actual extremes, and `key_count` is right
|
|
623
811
|
#
|
|
624
812
|
# @return [Boolean] true if all properties are satisfied, false otherwise
|
|
625
813
|
def valid?
|
|
626
814
|
return false if @root.color == Node::RED
|
|
815
|
+
return false if @root != @nil_node && @root.parent != @nil_node
|
|
627
816
|
return false if check_black_height(@root) == -1
|
|
628
|
-
return false unless check_order(@root)
|
|
817
|
+
return false unless check_order(@root, nil, nil)
|
|
818
|
+
return false unless check_links(@root)
|
|
819
|
+
|
|
820
|
+
count = 0
|
|
821
|
+
first_node = nil
|
|
822
|
+
last_node = nil
|
|
823
|
+
indexed = true
|
|
824
|
+
each_node_asc do |n|
|
|
825
|
+
count += 1
|
|
826
|
+
first_node ||= n
|
|
827
|
+
last_node = n
|
|
828
|
+
indexed &&= @hash_index[n.key].equal?(n)
|
|
829
|
+
end
|
|
830
|
+
|
|
831
|
+
return false unless indexed
|
|
832
|
+
return false unless count == @key_count
|
|
833
|
+
return false unless @hash_index.size == @key_count
|
|
834
|
+
return false unless (first_node || @nil_node).equal?(@min_node)
|
|
835
|
+
return false unless (last_node || @nil_node).equal?(@max_node)
|
|
629
836
|
true
|
|
630
837
|
end
|
|
631
838
|
|
|
@@ -635,6 +842,108 @@ class RBTree
|
|
|
635
842
|
def min_node = (@min_node == @nil_node) ? nil : @min_node
|
|
636
843
|
def max_node = (@max_node == @nil_node) ? nil : @max_node
|
|
637
844
|
|
|
845
|
+
# Creates an empty tree of the same class, keeping this tree's allocator.
|
|
846
|
+
#
|
|
847
|
+
# @return [RBTree] a new empty tree
|
|
848
|
+
def new_derived_tree = self.class.new(node_allocator: @node_allocator)
|
|
849
|
+
|
|
850
|
+
# Locates the node holding the given key, by O(1) hash index or by ordering.
|
|
851
|
+
#
|
|
852
|
+
# The index is keyed by +eql?+ while the tree orders by <tt><=></tt>, so a key
|
|
853
|
+
# such as +1.0+ misses the index but still matches a stored +1+.
|
|
854
|
+
#
|
|
855
|
+
# @param key [Object] the key to locate
|
|
856
|
+
# @return [Node, nil] the node holding an equal key, or nil if absent
|
|
857
|
+
def find_node(key) = @hash_index[key] || find_node_by_order(key)
|
|
858
|
+
|
|
859
|
+
# Resolves a key the hash index missed, using the tree's <tt><=></tt> ordering.
|
|
860
|
+
#
|
|
861
|
+
# Split from {#find_node} so hot callers can inline the hash probe and pay no
|
|
862
|
+
# method call on a hit. The descent is skipped when every stored key and the
|
|
863
|
+
# lookup key share a class in {COHERENT_KEY_CLASSES}, since a match would then
|
|
864
|
+
# have been found in the index already. Incomparable keys report absence rather
|
|
865
|
+
# than raising, so lookups stay total.
|
|
866
|
+
#
|
|
867
|
+
# @param key [Object] the key to locate
|
|
868
|
+
# @return [Node, nil] the node holding an equal key, or nil if absent
|
|
869
|
+
def find_node_by_order(key)
|
|
870
|
+
return nil if @coherent_keys && key.instance_of?(@key_class)
|
|
871
|
+
|
|
872
|
+
x = @root
|
|
873
|
+
while x != @nil_node
|
|
874
|
+
cmp = key <=> x.key
|
|
875
|
+
return nil unless cmp
|
|
876
|
+
return x if cmp == 0
|
|
877
|
+
x = cmp < 0 ? x.left : x.right
|
|
878
|
+
end
|
|
879
|
+
nil
|
|
880
|
+
end
|
|
881
|
+
|
|
882
|
+
# Records the class of a newly inserted key for {#find_node_by_order}.
|
|
883
|
+
#
|
|
884
|
+
# Deletion never relaxes the result: a stale pessimistic value costs a descent,
|
|
885
|
+
# never correctness.
|
|
886
|
+
#
|
|
887
|
+
# @param key [Object] the key of the node being created
|
|
888
|
+
# @return [void]
|
|
889
|
+
def note_key_class(key)
|
|
890
|
+
klass = key.class
|
|
891
|
+
return if @key_class.equal?(klass)
|
|
892
|
+
|
|
893
|
+
if @key_class.nil?
|
|
894
|
+
@key_class = klass
|
|
895
|
+
@coherent_keys = COHERENT_KEY_CLASSES.include?(klass)
|
|
896
|
+
else
|
|
897
|
+
@key_class = false
|
|
898
|
+
@coherent_keys = false
|
|
899
|
+
end
|
|
900
|
+
end
|
|
901
|
+
|
|
902
|
+
# Compares two keys, rejecting incomparable ones with a clear error.
|
|
903
|
+
#
|
|
904
|
+
# @param a [Object] the left-hand key
|
|
905
|
+
# @param b [Object] the right-hand key
|
|
906
|
+
# @return [Integer] the result of <tt>a <=> b</tt>
|
|
907
|
+
# @raise [ArgumentError] if the keys cannot be compared
|
|
908
|
+
def compare!(a, b)
|
|
909
|
+
cmp = a <=> b
|
|
910
|
+
return cmp if cmp
|
|
911
|
+
raise ArgumentError, "comparison of #{a.class} with #{b.inspect} failed"
|
|
912
|
+
end
|
|
913
|
+
|
|
914
|
+
# Reports mutation detected during a non-safe traversal.
|
|
915
|
+
#
|
|
916
|
+
# Such a traversal walks live node links, and released nodes are recycled by the
|
|
917
|
+
# allocator, so mutating during one can yield keys out of order rather than
|
|
918
|
+
# merely skip them. Callers compare `@mod_count` inline and only call this on
|
|
919
|
+
# mismatch, keeping the per-element cost to an integer comparison.
|
|
920
|
+
#
|
|
921
|
+
# @raise [RuntimeError] always
|
|
922
|
+
# @return [void]
|
|
923
|
+
def concurrent_modification!
|
|
924
|
+
raise RuntimeError,
|
|
925
|
+
"can't modify #{self.class} during iteration (pass `safe: true` to iterate safely)"
|
|
926
|
+
end
|
|
927
|
+
|
|
928
|
+
# Walks every node in ascending key order, following live tree links.
|
|
929
|
+
#
|
|
930
|
+
# @yield [node] each node
|
|
931
|
+
# @return [void]
|
|
932
|
+
def each_node_asc
|
|
933
|
+
stack = []
|
|
934
|
+
current = @root
|
|
935
|
+
while current != @nil_node || !stack.empty?
|
|
936
|
+
if current != @nil_node
|
|
937
|
+
stack << current
|
|
938
|
+
current = current.left
|
|
939
|
+
else
|
|
940
|
+
current = stack.pop
|
|
941
|
+
yield current
|
|
942
|
+
current = current.right
|
|
943
|
+
end
|
|
944
|
+
end
|
|
945
|
+
end
|
|
946
|
+
|
|
638
947
|
# Inserts a single key-value pair.
|
|
639
948
|
#
|
|
640
949
|
# @param key [Object] the key to insert
|
|
@@ -658,6 +967,9 @@ class RBTree
|
|
|
658
967
|
|
|
659
968
|
# Generic entry insertion logic shared between RBTree and MultiRBTree.
|
|
660
969
|
#
|
|
970
|
+
# A key that is <tt><=></tt>-equal to a stored key updates that node, which keeps
|
|
971
|
+
# the key object it was created with, so the index stays at one entry per node.
|
|
972
|
+
#
|
|
661
973
|
# @param key [Object] the key to insert
|
|
662
974
|
# @yield [node, is_new] yields the existing node (if any) and whether it's a new insertion
|
|
663
975
|
# @yieldparam node [Node, nil] the existing node or nil
|
|
@@ -665,6 +977,7 @@ class RBTree
|
|
|
665
977
|
# @yieldreturn [Object]
|
|
666
978
|
# - if is_new: the initial value for the new node
|
|
667
979
|
# - if !is_new: the value to return from insert_entry
|
|
980
|
+
# @raise [ArgumentError] if the key cannot be compared with the stored keys
|
|
668
981
|
def insert_entry_generic(key)
|
|
669
982
|
if (node = @hash_index[key])
|
|
670
983
|
return yield(node, false)
|
|
@@ -675,6 +988,9 @@ class RBTree
|
|
|
675
988
|
while x != @nil_node
|
|
676
989
|
y = x
|
|
677
990
|
cmp = key <=> x.key
|
|
991
|
+
unless cmp
|
|
992
|
+
raise ArgumentError, "comparison of #{key.class} with #{x.key.inspect} failed"
|
|
993
|
+
end
|
|
678
994
|
if cmp == 0
|
|
679
995
|
return yield(x, false)
|
|
680
996
|
elsif cmp < 0
|
|
@@ -708,6 +1024,8 @@ class RBTree
|
|
|
708
1024
|
end
|
|
709
1025
|
|
|
710
1026
|
@hash_index[key] = z
|
|
1027
|
+
note_key_class(key)
|
|
1028
|
+
@mod_count += 1
|
|
711
1029
|
true
|
|
712
1030
|
end
|
|
713
1031
|
|
|
@@ -748,13 +1066,13 @@ class RBTree
|
|
|
748
1066
|
|
|
749
1067
|
if safe
|
|
750
1068
|
pair = !min ? find_min :
|
|
751
|
-
include_min &&
|
|
752
|
-
while pair && (!max || pair[0]
|
|
1069
|
+
include_min && find_node(min)&.pair || find_successor(min)
|
|
1070
|
+
while pair && (!max || (pair[0] <=> max) < 0)
|
|
753
1071
|
current_key = pair[0]
|
|
754
1072
|
yield pair
|
|
755
1073
|
pair = find_successor(current_key)
|
|
756
1074
|
end
|
|
757
|
-
yield pair if pair && max && include_max && pair[0] ==
|
|
1075
|
+
yield pair if pair && max && include_max && (pair[0] <=> max) == 0
|
|
758
1076
|
else
|
|
759
1077
|
start_node, stack = resolve_startup_asc(min, include_min)
|
|
760
1078
|
traverse_from_asc(start_node, stack, max, include_max, &block)
|
|
@@ -780,13 +1098,13 @@ class RBTree
|
|
|
780
1098
|
|
|
781
1099
|
if safe
|
|
782
1100
|
pair = !max ? find_max :
|
|
783
|
-
include_max &&
|
|
784
|
-
while pair && (!min || pair[0]
|
|
1101
|
+
include_max && find_node(max)&.pair || find_predecessor(max)
|
|
1102
|
+
while pair && (!min || (pair[0] <=> min) > 0)
|
|
785
1103
|
current_key = pair[0]
|
|
786
1104
|
yield pair
|
|
787
1105
|
pair = find_predecessor(current_key)
|
|
788
1106
|
end
|
|
789
|
-
yield pair if pair && min && include_min && pair[0] ==
|
|
1107
|
+
yield pair if pair && min && include_min && (pair[0] <=> min) == 0
|
|
790
1108
|
else
|
|
791
1109
|
start_node, stack = resolve_startup_desc(max, include_max)
|
|
792
1110
|
traverse_from_desc(start_node, stack, min, include_min, &block)
|
|
@@ -838,7 +1156,8 @@ class RBTree
|
|
|
838
1156
|
return [@min_node, reconstruct_stack_asc(@min_node)]
|
|
839
1157
|
end
|
|
840
1158
|
|
|
841
|
-
# 2. Use Hash index if key exists
|
|
1159
|
+
# 2. Use Hash index if key exists. A pure O(1) probe, not {#find_node}: on a
|
|
1160
|
+
# miss, step 3's descent already places a <=>-equal node correctly.
|
|
842
1161
|
if min && (node = @hash_index[min])
|
|
843
1162
|
start_node = include_min ? node : successor_node_of(node)
|
|
844
1163
|
return [start_node, reconstruct_stack_asc(start_node)]
|
|
@@ -884,16 +1203,21 @@ class RBTree
|
|
|
884
1203
|
# @yieldparam key [Object] the key
|
|
885
1204
|
# @yieldparam val [Object] the value
|
|
886
1205
|
def traverse_from_asc(current, stack, max, include_max, &block)
|
|
1206
|
+
mod = @mod_count
|
|
887
1207
|
while current != @nil_node || !stack.empty?
|
|
888
1208
|
if current != @nil_node
|
|
889
1209
|
if max
|
|
890
1210
|
cmp = current.key <=> max
|
|
891
1211
|
if cmp >= 0
|
|
892
|
-
|
|
1212
|
+
if include_max && cmp == 0
|
|
1213
|
+
yield current.pair
|
|
1214
|
+
concurrent_modification! unless @mod_count == mod
|
|
1215
|
+
end
|
|
893
1216
|
return
|
|
894
1217
|
end
|
|
895
1218
|
end
|
|
896
1219
|
yield current.pair
|
|
1220
|
+
concurrent_modification! unless @mod_count == mod
|
|
897
1221
|
current = current.right
|
|
898
1222
|
while current != @nil_node
|
|
899
1223
|
stack << current
|
|
@@ -916,7 +1240,7 @@ class RBTree
|
|
|
916
1240
|
return [@max_node, reconstruct_stack_desc(@max_node)]
|
|
917
1241
|
end
|
|
918
1242
|
|
|
919
|
-
# 2. Use Hash index if key exists
|
|
1243
|
+
# 2. Use Hash index if key exists; see the note in #resolve_startup_asc.
|
|
920
1244
|
if max && (node = @hash_index[max])
|
|
921
1245
|
start_node = include_max ? node : predecessor_node_of(node)
|
|
922
1246
|
return [start_node, reconstruct_stack_desc(start_node)]
|
|
@@ -962,16 +1286,21 @@ class RBTree
|
|
|
962
1286
|
# @yieldparam key [Object] the key
|
|
963
1287
|
# @yieldparam val [Object] the value
|
|
964
1288
|
def traverse_from_desc(current, stack, min, include_min, &block)
|
|
1289
|
+
mod = @mod_count
|
|
965
1290
|
while current != @nil_node || !stack.empty?
|
|
966
1291
|
if current != @nil_node
|
|
967
1292
|
if min
|
|
968
1293
|
cmp = current.key <=> min
|
|
969
1294
|
if cmp <= 0
|
|
970
|
-
|
|
1295
|
+
if include_min && cmp == 0
|
|
1296
|
+
yield current.pair
|
|
1297
|
+
concurrent_modification! unless @mod_count == mod
|
|
1298
|
+
end
|
|
971
1299
|
return
|
|
972
1300
|
end
|
|
973
1301
|
end
|
|
974
1302
|
yield current.pair
|
|
1303
|
+
concurrent_modification! unless @mod_count == mod
|
|
975
1304
|
current = current.left
|
|
976
1305
|
while current != @nil_node
|
|
977
1306
|
stack << current
|
|
@@ -1033,7 +1362,17 @@ class RBTree
|
|
|
1033
1362
|
#
|
|
1034
1363
|
# @param key [Object] the key to delete
|
|
1035
1364
|
# @return [Object, nil] the value of the deleted node, or nil if not found
|
|
1036
|
-
def delete_indexed_node(key) = (z =
|
|
1365
|
+
def delete_indexed_node(key) = (z = find_node(key)) && delete_found_node(z)
|
|
1366
|
+
|
|
1367
|
+
# Removes an already-located node, unindexing it under its own key so that a node
|
|
1368
|
+
# found through a non-+eql?+ but <tt><=></tt>-equal key is still removed correctly.
|
|
1369
|
+
#
|
|
1370
|
+
# @param z [Node] the node to remove
|
|
1371
|
+
# @return [Object] the value of the removed node
|
|
1372
|
+
def delete_found_node(z)
|
|
1373
|
+
@hash_index.delete(z.key)
|
|
1374
|
+
delete_node(z)
|
|
1375
|
+
end
|
|
1037
1376
|
|
|
1038
1377
|
# Removes a node from the tree and restores red-black properties.
|
|
1039
1378
|
#
|
|
@@ -1092,6 +1431,7 @@ class RBTree
|
|
|
1092
1431
|
|
|
1093
1432
|
value = z.value
|
|
1094
1433
|
release_node(z)
|
|
1434
|
+
@mod_count += 1
|
|
1095
1435
|
value
|
|
1096
1436
|
end
|
|
1097
1437
|
|
|
@@ -1229,8 +1569,9 @@ class RBTree
|
|
|
1229
1569
|
# @return [Node] the predecessor node, or @nil_node if none exists
|
|
1230
1570
|
def find_predecessor_node(key)
|
|
1231
1571
|
# If key is larger than max_key, return max_node
|
|
1232
|
-
return @max_node if max_key && (key
|
|
1233
|
-
#
|
|
1572
|
+
return @max_node if (mk = max_key) && compare!(key, mk) > 0
|
|
1573
|
+
# O(1) hash fast path; on a miss the descent below steps away from a
|
|
1574
|
+
# <=>-equal node in the right direction anyway.
|
|
1234
1575
|
if (node = @hash_index[key])
|
|
1235
1576
|
return predecessor_node_of(node)
|
|
1236
1577
|
end
|
|
@@ -1269,10 +1610,10 @@ class RBTree
|
|
|
1269
1610
|
# @return [Node] the successor node, or @nil_node if none exists
|
|
1270
1611
|
def find_successor_node(key)
|
|
1271
1612
|
# If key is larger than or equal to max_key, return nil
|
|
1272
|
-
return @nil_node if max_key && (key
|
|
1613
|
+
return @nil_node if (mk = max_key) && compare!(key, mk) >= 0
|
|
1273
1614
|
# If key is smaller than min_key, return min_node
|
|
1274
|
-
return @min_node if min_key && (key
|
|
1275
|
-
#
|
|
1615
|
+
return @min_node if (nk = min_key) && compare!(key, nk) < 0
|
|
1616
|
+
# O(1) hash fast path; see the note in #find_predecessor_node.
|
|
1276
1617
|
if (node = @hash_index[key])
|
|
1277
1618
|
return successor_node_of(node)
|
|
1278
1619
|
end
|
|
@@ -1431,15 +1772,31 @@ class RBTree
|
|
|
1431
1772
|
left_h + (node.color == Node::BLACK ? 1 : 0)
|
|
1432
1773
|
end
|
|
1433
1774
|
|
|
1434
|
-
|
|
1775
|
+
# Recursively checks the binary-search-tree ordering.
|
|
1776
|
+
#
|
|
1777
|
+
# Bounds are inherited from ancestors, so a key misplaced relative to a higher
|
|
1778
|
+
# ancestor is rejected even when it is ordered correctly against its parent.
|
|
1779
|
+
#
|
|
1780
|
+
# @param node [Node] the current node
|
|
1781
|
+
# @param lo [Object, nil] exclusive lower bound inherited from ancestors
|
|
1782
|
+
# @param hi [Object, nil] exclusive upper bound inherited from ancestors
|
|
1783
|
+
# @return [Boolean] true if the subtree is correctly ordered
|
|
1784
|
+
def check_order(node, lo, hi)
|
|
1785
|
+
return true if node == @nil_node
|
|
1786
|
+
return false if lo && (node.key <=> lo) <= 0
|
|
1787
|
+
return false if hi && (node.key <=> hi) >= 0
|
|
1788
|
+
check_order(node.left, lo, node.key) && check_order(node.right, node.key, hi)
|
|
1789
|
+
end
|
|
1790
|
+
|
|
1791
|
+
# Recursively checks that every child points back at its parent.
|
|
1792
|
+
#
|
|
1793
|
+
# @param node [Node] the current node
|
|
1794
|
+
# @return [Boolean] true if all parent links in the subtree are consistent
|
|
1795
|
+
def check_links(node)
|
|
1435
1796
|
return true if node == @nil_node
|
|
1436
|
-
if node.left != @nil_node &&
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
if node.right != @nil_node && (node.right.key <=> node.key) <= 0
|
|
1440
|
-
return false
|
|
1441
|
-
end
|
|
1442
|
-
check_order(node.left) && check_order(node.right)
|
|
1797
|
+
return false if node.left != @nil_node && !node.left.parent.equal?(node)
|
|
1798
|
+
return false if node.right != @nil_node && !node.right.parent.equal?(node)
|
|
1799
|
+
check_links(node.left) && check_links(node.right)
|
|
1443
1800
|
end
|
|
1444
1801
|
end
|
|
1445
1802
|
|
|
@@ -1508,6 +1865,11 @@ end
|
|
|
1508
1865
|
# @author Masahito Suzuki
|
|
1509
1866
|
# @since 0.1.2
|
|
1510
1867
|
class MultiRBTree < RBTree
|
|
1868
|
+
# Sentinel for "no key given", so +nil+ and +false+ stay usable as keys.
|
|
1869
|
+
# @api private
|
|
1870
|
+
NO_KEY = Object.new
|
|
1871
|
+
private_constant :NO_KEY
|
|
1872
|
+
|
|
1511
1873
|
def initialize(*args, **kwargs)
|
|
1512
1874
|
@value_count = 0
|
|
1513
1875
|
super
|
|
@@ -1516,38 +1878,90 @@ class MultiRBTree < RBTree
|
|
|
1516
1878
|
# Returns the number of values stored in the tree.
|
|
1517
1879
|
# @return [Integer] the number of values in the tree
|
|
1518
1880
|
def size = @value_count
|
|
1881
|
+
|
|
1882
|
+
# Removes all elements from the tree.
|
|
1883
|
+
# @return [MultiRBTree] self
|
|
1884
|
+
def clear
|
|
1885
|
+
@value_count = 0
|
|
1886
|
+
super
|
|
1887
|
+
end
|
|
1888
|
+
|
|
1889
|
+
# Returns a Hash mapping each key to an Array of its values.
|
|
1890
|
+
#
|
|
1891
|
+
# Keys are inserted in ascending order, and each value Array is a fresh copy,
|
|
1892
|
+
# so mutating the result cannot corrupt the tree.
|
|
1893
|
+
#
|
|
1894
|
+
# @return [Hash] a new Hash with the tree's contents
|
|
1895
|
+
# @example
|
|
1896
|
+
# tree = MultiRBTree.new
|
|
1897
|
+
# tree.insert(1, 'a'); tree.insert(1, 'b')
|
|
1898
|
+
# tree.to_h # => {1 => ["a", "b"]}
|
|
1899
|
+
def to_h
|
|
1900
|
+
h = {}
|
|
1901
|
+
each { |k, v| (h[k] ||= []) << v }
|
|
1902
|
+
h
|
|
1903
|
+
end
|
|
1904
|
+
|
|
1905
|
+
# Validates everything `RBTree#valid?` does, plus that every node holds a
|
|
1906
|
+
# non-empty Array of values and that `size` matches the total value count.
|
|
1907
|
+
#
|
|
1908
|
+
# @return [Boolean] true if all properties are satisfied, false otherwise
|
|
1909
|
+
def valid?
|
|
1910
|
+
return false unless super
|
|
1911
|
+
total = 0
|
|
1912
|
+
ok = true
|
|
1913
|
+
each_node_asc do |n|
|
|
1914
|
+
ok &&= n.value.is_a?(Array) && !n.value.empty?
|
|
1915
|
+
total += n.value.size if n.value.is_a?(Array)
|
|
1916
|
+
end
|
|
1917
|
+
ok && total == @value_count
|
|
1918
|
+
end
|
|
1519
1919
|
|
|
1520
1920
|
# Returns the minimum key-value pair without removing it.
|
|
1521
1921
|
#
|
|
1922
|
+
# As with `RBTree#min`, a count or comparison block switches to `Enumerable#min`.
|
|
1923
|
+
#
|
|
1522
1924
|
# @param last [Boolean] whether to return the last value (default: false)
|
|
1523
1925
|
# @return [Array(Object, Object), nil] a two-element array [key, value], or nil if tree is empty
|
|
1524
1926
|
# @example
|
|
1525
1927
|
# tree = MultiRBTree.new({3 => 'three', 1 => 'one', 2 => 'two'})
|
|
1526
1928
|
# tree.min # => [1, "one"]
|
|
1527
|
-
def min(last: false
|
|
1929
|
+
def min(*args, last: false, &block)
|
|
1930
|
+
return super(*args, &block) if !args.empty? || block
|
|
1931
|
+
(n = min_node) && [n.key, n.value.send(last ? :last : :first)]
|
|
1932
|
+
end
|
|
1528
1933
|
|
|
1529
1934
|
# Returns the maximum key-value pair without removing it.
|
|
1530
1935
|
#
|
|
1936
|
+
# As with `RBTree#max`, a count or comparison block switches to `Enumerable#max`.
|
|
1937
|
+
#
|
|
1531
1938
|
# @param last [Boolean] whether to return the last value (default: false)
|
|
1532
1939
|
# @return [Array(Object, Object), nil] a two-element array [key, value], or nil if tree is empty
|
|
1533
1940
|
# @example
|
|
1534
1941
|
# tree = MultiRBTree.new({3 => 'three', 1 => 'one', 2 => 'two'})
|
|
1535
1942
|
# tree.max # => [3, "three"]
|
|
1536
|
-
def max(last: false
|
|
1943
|
+
def max(*args, last: false, &block)
|
|
1944
|
+
return super(*args, &block) if !args.empty? || block
|
|
1945
|
+
(n = max_node) && [n.key, n.value.send(last ? :last : :first)]
|
|
1946
|
+
end
|
|
1537
1947
|
|
|
1538
|
-
# Returns the last key-value pair without removing
|
|
1948
|
+
# Returns the last key-value pair, or the last +n+ pairs, without removing them.
|
|
1539
1949
|
#
|
|
1540
|
-
# @
|
|
1950
|
+
# @param n [Integer, nil] number of trailing pairs to return
|
|
1951
|
+
# @return [Array, nil] the pair, or the n last pairs in ascending order
|
|
1541
1952
|
# @example
|
|
1542
1953
|
# tree = MultiRBTree.new({3 => 'three', 1 => 'one', 2 => 'two'})
|
|
1543
1954
|
# tree.last # => [3, "three"]
|
|
1544
|
-
def last = max(last: true)
|
|
1955
|
+
def last(n = nil) = n.nil? ? max(last: true) : super(n)
|
|
1545
1956
|
|
|
1546
|
-
# Returns the number of values for a given key or the total number of key-value
|
|
1957
|
+
# Returns the number of values for a given key, or the total number of key-value
|
|
1958
|
+
# pairs when called without an argument.
|
|
1959
|
+
#
|
|
1960
|
+
# The default is a sentinel, so +nil+ and +false+ are looked up as ordinary keys.
|
|
1547
1961
|
#
|
|
1548
|
-
# @param key [Object
|
|
1962
|
+
# @param key [Object] the key to look up; omit for the total count
|
|
1549
1963
|
# @return [Integer] the number of values for the key, or total count if no key is given
|
|
1550
|
-
def value_count(key =
|
|
1964
|
+
def value_count(key = NO_KEY) = key.equal?(NO_KEY) ? size : (find_node(key)&.value&.size || 0)
|
|
1551
1965
|
|
|
1552
1966
|
# Retrieves a value associated with the given key.
|
|
1553
1967
|
#
|
|
@@ -1560,7 +1974,7 @@ class MultiRBTree < RBTree
|
|
|
1560
1974
|
# tree.insert(1, 'second')
|
|
1561
1975
|
# tree.get(1) # => "first"
|
|
1562
1976
|
# tree.get(1, last: true) # => "second"
|
|
1563
|
-
def value(key, last: false) = @hash_index[key]&.value&.send(last ? :last : :first)
|
|
1977
|
+
def value(key, last: false) = (@hash_index[key] || find_node_by_order(key))&.value&.send(last ? :last : :first)
|
|
1564
1978
|
alias :get :value
|
|
1565
1979
|
|
|
1566
1980
|
# Retrieves the first value associated with the given key.
|
|
@@ -1580,15 +1994,17 @@ class MultiRBTree < RBTree
|
|
|
1580
1994
|
# Retrieves all values associated with the given key.
|
|
1581
1995
|
#
|
|
1582
1996
|
# @param key [Object] the key to look up
|
|
1583
|
-
# @
|
|
1997
|
+
# @param reverse [Boolean] if true, yield the values in reverse insertion order (default: false)
|
|
1998
|
+
# @return [Enumerator, nil] an Enumerator over the values, or nil if the key is absent (when a block is given)
|
|
1584
1999
|
# @example
|
|
1585
2000
|
# tree = MultiRBTree.new
|
|
1586
2001
|
# tree.insert(1, 'first')
|
|
1587
2002
|
# tree.insert(1, 'second')
|
|
1588
|
-
# tree.values(1).to_a
|
|
2003
|
+
# tree.values(1).to_a # => ["first", "second"]
|
|
2004
|
+
# tree.values(1, reverse: true).to_a # => ["second", "first"]
|
|
1589
2005
|
def values(key, reverse: false)
|
|
1590
|
-
return enum_for(__method__, key) { value_count(key) } unless block_given?
|
|
1591
|
-
|
|
2006
|
+
return enum_for(__method__, key, reverse: reverse) { value_count(key) } unless block_given?
|
|
2007
|
+
find_node(key)&.value&.send(reverse ? :reverse_each : :each) { |v| yield v }
|
|
1592
2008
|
end
|
|
1593
2009
|
alias :get_all :values
|
|
1594
2010
|
|
|
@@ -1648,9 +2064,13 @@ class MultiRBTree < RBTree
|
|
|
1648
2064
|
# tree.delete_value(1) # => "first"
|
|
1649
2065
|
# tree.delete_value(1, last: true) # => "second" (if more values existed)
|
|
1650
2066
|
def delete_value(key, last: false)
|
|
1651
|
-
(z =
|
|
2067
|
+
(z = find_node(key)) or return nil
|
|
1652
2068
|
value = z.value.send(last ? :pop : :shift)
|
|
1653
|
-
z.value.empty?
|
|
2069
|
+
if z.value.empty?
|
|
2070
|
+
delete_found_node(z)
|
|
2071
|
+
else
|
|
2072
|
+
@mod_count += 1
|
|
2073
|
+
end
|
|
1654
2074
|
@value_count -= 1
|
|
1655
2075
|
value
|
|
1656
2076
|
end
|
|
@@ -1683,9 +2103,9 @@ class MultiRBTree < RBTree
|
|
|
1683
2103
|
# vals = tree.delete(1) # removes both values
|
|
1684
2104
|
# vals.size # => 2
|
|
1685
2105
|
def delete_key(key)
|
|
1686
|
-
return nil unless (z =
|
|
2106
|
+
return nil unless (z = find_node(key))
|
|
1687
2107
|
@value_count -= (value = z.value).size
|
|
1688
|
-
|
|
2108
|
+
delete_found_node(z)
|
|
1689
2109
|
value
|
|
1690
2110
|
end
|
|
1691
2111
|
alias :delete :delete_key
|
|
@@ -1697,9 +2117,14 @@ class MultiRBTree < RBTree
|
|
|
1697
2117
|
# tree = MultiRBTree.new({3 => 'three', 1 => 'one', 2 => 'two'})
|
|
1698
2118
|
# tree.shift # => [1, "one"]
|
|
1699
2119
|
def shift
|
|
1700
|
-
(
|
|
2120
|
+
(n = min_node) or return nil
|
|
2121
|
+
key, vals = n.pair
|
|
1701
2122
|
val = vals.shift
|
|
1702
|
-
vals.empty?
|
|
2123
|
+
if vals.empty?
|
|
2124
|
+
delete_found_node(n)
|
|
2125
|
+
else
|
|
2126
|
+
@mod_count += 1
|
|
2127
|
+
end
|
|
1703
2128
|
@value_count -= 1
|
|
1704
2129
|
[key, val]
|
|
1705
2130
|
end
|
|
@@ -1711,16 +2136,61 @@ class MultiRBTree < RBTree
|
|
|
1711
2136
|
# tree = MultiRBTree.new({3 => 'three', 1 => 'one', 2 => 'two'})
|
|
1712
2137
|
# tree.pop # => [3, "three"]
|
|
1713
2138
|
def pop
|
|
1714
|
-
(
|
|
2139
|
+
(n = max_node) or return nil
|
|
2140
|
+
key, vals = n.pair
|
|
1715
2141
|
val = vals.pop
|
|
1716
|
-
vals.empty?
|
|
2142
|
+
if vals.empty?
|
|
2143
|
+
delete_found_node(n)
|
|
2144
|
+
else
|
|
2145
|
+
@mod_count += 1
|
|
2146
|
+
end
|
|
1717
2147
|
@value_count -= 1
|
|
1718
2148
|
[key, val]
|
|
1719
2149
|
end
|
|
1720
2150
|
|
|
2151
|
+
# Keeps key-value pairs for which the block returns true, deleting the rest.
|
|
2152
|
+
# Unlike RBTree, this removes individual values rather than entire keys.
|
|
2153
|
+
#
|
|
2154
|
+
# @yield [key, value] each key-value pair
|
|
2155
|
+
# @return [MultiRBTree, Enumerator] self, or Enumerator if no block
|
|
2156
|
+
def keep_if(&block)
|
|
2157
|
+
return enum_for(__method__) { size } unless block_given?
|
|
2158
|
+
filter_values! { |k, v| block.call(k, v) }
|
|
2159
|
+
self
|
|
2160
|
+
end
|
|
2161
|
+
|
|
2162
|
+
# Deletes key-value pairs for which the block returns true.
|
|
2163
|
+
# Unlike RBTree, this removes individual values rather than entire keys.
|
|
2164
|
+
#
|
|
2165
|
+
# @yield [key, value] each key-value pair
|
|
2166
|
+
# @return [MultiRBTree, Enumerator] self, or Enumerator if no block
|
|
2167
|
+
def delete_if(&block)
|
|
2168
|
+
return enum_for(__method__) { size } unless block_given?
|
|
2169
|
+
filter_values! { |k, v| !block.call(k, v) }
|
|
2170
|
+
self
|
|
2171
|
+
end
|
|
2172
|
+
|
|
1721
2173
|
# @!visibility private
|
|
1722
2174
|
private
|
|
1723
2175
|
|
|
2176
|
+
# Filters values in-place across all nodes.
|
|
2177
|
+
# Keeps only values for which the block returns true.
|
|
2178
|
+
# Removes nodes whose value arrays become empty.
|
|
2179
|
+
# Updates @value_count accordingly.
|
|
2180
|
+
def filter_values!
|
|
2181
|
+
emptied = []
|
|
2182
|
+
@hash_index.each_value do |node|
|
|
2183
|
+
before = node.value.size
|
|
2184
|
+
node.value.select! { |v| yield node.key, v }
|
|
2185
|
+
removed = before - node.value.size
|
|
2186
|
+
next if removed == 0
|
|
2187
|
+
@value_count -= removed
|
|
2188
|
+
@mod_count += 1
|
|
2189
|
+
emptied << node if node.value.empty?
|
|
2190
|
+
end
|
|
2191
|
+
emptied.each { |node| delete_found_node(node) }
|
|
2192
|
+
end
|
|
2193
|
+
|
|
1724
2194
|
# Inserts a value for the given key.
|
|
1725
2195
|
#
|
|
1726
2196
|
# If the key already exists, the value is appended to its list.
|
|
@@ -1741,6 +2211,7 @@ class MultiRBTree < RBTree
|
|
|
1741
2211
|
[value]
|
|
1742
2212
|
else
|
|
1743
2213
|
node.value << value
|
|
2214
|
+
@mod_count += 1 # observable to a traversal walking this value list
|
|
1744
2215
|
true
|
|
1745
2216
|
end
|
|
1746
2217
|
end
|
|
@@ -1748,22 +2219,49 @@ class MultiRBTree < RBTree
|
|
|
1748
2219
|
|
|
1749
2220
|
# Traverses the tree in ascending order, yielding each key-value pair.
|
|
1750
2221
|
#
|
|
1751
|
-
#
|
|
2222
|
+
# Each node's value list is flattened to one [key, value] pair per value. Safe
|
|
2223
|
+
# mode snapshots the list, so removing values of the key being iterated cannot
|
|
2224
|
+
# skip its siblings, though such values may still be yielded.
|
|
2225
|
+
#
|
|
2226
|
+
# @param min [Object] the lower bound
|
|
2227
|
+
# @param max [Object] the upper bound
|
|
2228
|
+
# @param include_min [Boolean] whether to include the lower bound
|
|
2229
|
+
# @param include_max [Boolean] whether to include the upper bound
|
|
2230
|
+
# @param safe [Boolean] whether to use safe traversal
|
|
1752
2231
|
# @yield [Array(Object, Object)] each key-value pair
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
2232
|
+
def traverse_range_asc(min, max, include_min, include_max, safe: false, &block)
|
|
2233
|
+
if safe
|
|
2234
|
+
super(min, max, include_min, include_max, safe: true) do |k, vals|
|
|
2235
|
+
vals.dup.each { |v| yield [k, v] }
|
|
2236
|
+
end
|
|
2237
|
+
else
|
|
2238
|
+
mod = @mod_count
|
|
2239
|
+
super(min, max, include_min, include_max, safe: false) do |k, vals|
|
|
2240
|
+
vals.each { |v| yield [k, v]; concurrent_modification! unless @mod_count == mod }
|
|
2241
|
+
end
|
|
2242
|
+
end
|
|
1757
2243
|
end
|
|
1758
2244
|
|
|
1759
2245
|
# Traverses the tree in descending order, yielding each key-value pair.
|
|
1760
2246
|
#
|
|
1761
|
-
# @param
|
|
2247
|
+
# @param min [Object] the lower bound
|
|
2248
|
+
# @param max [Object] the upper bound
|
|
2249
|
+
# @param include_min [Boolean] whether to include the lower bound
|
|
2250
|
+
# @param include_max [Boolean] whether to include the upper bound
|
|
2251
|
+
# @param safe [Boolean] whether to use safe traversal
|
|
1762
2252
|
# @yield [Array(Object, Object)] each key-value pair
|
|
1763
|
-
# @
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
2253
|
+
# @see #traverse_range_asc
|
|
2254
|
+
def traverse_range_desc(min, max, include_min, include_max, safe: false, &block)
|
|
2255
|
+
if safe
|
|
2256
|
+
super(min, max, include_min, include_max, safe: true) do |k, vals|
|
|
2257
|
+
vals.dup.reverse_each { |v| yield [k, v] }
|
|
2258
|
+
end
|
|
2259
|
+
else
|
|
2260
|
+
mod = @mod_count
|
|
2261
|
+
super(min, max, include_min, include_max, safe: false) do |k, vals|
|
|
2262
|
+
vals.reverse_each { |v| yield [k, v]; concurrent_modification! unless @mod_count == mod }
|
|
2263
|
+
end
|
|
2264
|
+
end
|
|
1767
2265
|
end
|
|
1768
2266
|
end
|
|
1769
2267
|
|
|
@@ -1834,6 +2332,15 @@ class RBTree::NodeAllocator
|
|
|
1834
2332
|
#
|
|
1835
2333
|
# @param node [Node] the node to release
|
|
1836
2334
|
def release(node) = nil
|
|
2335
|
+
|
|
2336
|
+
# Notifies the allocator that +count+ nodes were dropped in bulk rather than
|
|
2337
|
+
# released individually, as `RBTree#clear` does.
|
|
2338
|
+
#
|
|
2339
|
+
# The nodes are not reclaimed for reuse; this only keeps live-node statistics right.
|
|
2340
|
+
#
|
|
2341
|
+
# @param count [Integer] the number of nodes discarded
|
|
2342
|
+
# @return [void]
|
|
2343
|
+
def discard(count) = nil
|
|
1837
2344
|
end
|
|
1838
2345
|
|
|
1839
2346
|
# Internal node pool for RBTree.
|
|
@@ -1947,12 +2454,22 @@ class RBTree::AutoShrinkNodePool < RBTree::NodePool
|
|
|
1947
2454
|
@min_active_in_interval = @active_nodes if @active_nodes < @min_active_in_interval
|
|
1948
2455
|
|
|
1949
2456
|
@check_count += 1
|
|
1950
|
-
|
|
2457
|
+
|
|
1951
2458
|
perform_maintenance if @check_count >= @check_interval
|
|
1952
2459
|
|
|
1953
2460
|
super if @pool.size < @current_target_capacity
|
|
1954
2461
|
end
|
|
1955
2462
|
|
|
2463
|
+
# Accounts for nodes dropped in bulk, so that the live-node count comes back down
|
|
2464
|
+
# after a `clear` instead of inflating the observed fluctuation range.
|
|
2465
|
+
#
|
|
2466
|
+
# @param count [Integer] the number of nodes discarded
|
|
2467
|
+
# @return [void]
|
|
2468
|
+
def discard(count)
|
|
2469
|
+
@active_nodes -= count
|
|
2470
|
+
@min_active_in_interval = @active_nodes if @active_nodes < @min_active_in_interval
|
|
2471
|
+
end
|
|
2472
|
+
|
|
1956
2473
|
private
|
|
1957
2474
|
|
|
1958
2475
|
def perform_maintenance
|