rbtree-ruby 0.3.2 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 327862d13e55ed6406023e1d10676e4b5e94541c59400d9205380af08822e4bf
4
- data.tar.gz: 465e1a514390286bf0165db4dfa9f25ada4cd3d1410b0115b11eba15b03dcf33
3
+ metadata.gz: 911e435d0a205afc1a62791f857ac66d6d6300d2aa332566aa916e65e16148d2
4
+ data.tar.gz: d1a2b2aec3404901985cfec8a54c5b3df87d12bd4c8872833ba111dd3641033b
5
5
  SHA512:
6
- metadata.gz: 1f354523b8fb78b746cf256949887b89184ce1dcd190403a7024949cded391de24ce6f932720a58ae24592b4b453f3901b1896c9904f546aa03fe4b1524698c2
7
- data.tar.gz: e1219a20ea771f3fe1ca2fc160d737c787a1a0e81cb5389b63f320dcd1d2ac9d2cc71d9df2b9698d73afc98069f76d39285ca8106691f0025f1d5a5fa8b6bf91
6
+ metadata.gz: 6b1a96900494faa73b223e5905897299799c9a5432aa6cbdd95c6881c48c8666fe82f1653907231918fd027413eed3036da64a21087bfded6307a434c3de63b2
7
+ data.tar.gz: 6456acef3636bf7f857611a22407c4f4a053c632a973b2dfc05913d99b55a338c3a4dbce41d03074c7589354cc4bad2765c5e4be683d2a080248ea30b6a22b04
data/CHANGELOG.md CHANGED
@@ -5,6 +5,27 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.3.4] - 2026-01-25
9
+
10
+ ### Added
11
+ - **Range-based Lookup**: Support for passing `Range` objects to the bracket operator `[]`.
12
+ - `tree[2..4]` -> `between(2, 4)`
13
+ - `tree[2...4]` -> `between(2, 4, include_max: false)`
14
+ - `tree[5..]` -> `gte(5)`
15
+ - `tree[..10]` -> `lte(10)`
16
+ - `tree[...10]` -> `lt(10)`
17
+ - Returns an `Enumerator` of `[key, value]` pairs.
18
+ - *Note: `gt` (greater than exclusive) is not covered by standard Ruby Range syntax and remains available via the `gt` method.*
19
+
20
+ ## [0.3.3] - 2026-01-23
21
+
22
+ ### Optimized
23
+ - **find_successor_node**: Optimized performance for scans starting from the beginning (or before `min_key`) when the `safe: true` option is used. Added a fast-path (short-circuit) when the given key is smaller than `min_key`.
24
+
25
+ ### Fixed
26
+ - **MultiRBTree Aliases**: Fixed `delete`, `get`, and `[]` aliases in `MultiRBTree`. They now correctly point to the overridden methods in the derived class, ensuring correct value handling and size tracking.
27
+ - **enum_for Syntax**: Syntactic corrections for `enum_for` to ensure robust keyword argument handling in enumerators.
28
+
8
29
  ## [0.3.2] - 2026-01-15
9
30
 
10
31
  ### Added
@@ -198,6 +219,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
198
219
  - ASCII diagrams for tree rotation operations
199
220
  - MIT License (Copyright © 2026 Masahito Suzuki)
200
221
 
222
+ [0.3.4]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.3.4
223
+ [0.3.3]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.3.3
201
224
  [0.3.2]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.3.2
202
225
  [0.3.1]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.3.1
203
226
  [0.3.0]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.3.0
data/README.ja.md CHANGED
@@ -6,11 +6,14 @@ Red-Black Tree(赤黒木)データ構造のピュアRuby実装です。挿
6
6
 
7
7
  ## 特徴
8
8
 
9
- - **自己平衡二分探索木**: 赤黒木の性質により最適なパフォーマンスを維持
10
- - **順序付き操作**: 効率的な範囲クエリ、最小/最大値の取得、ソート済みイテレーション
11
- - **複数値サポート**: `MultiRBTree`クラスで同一キーに複数の値を格納可能
12
- - **ピュアRuby**: C拡張不要、あらゆるRuby実装で動作
13
- - **充実したドキュメント**: 使用例付きの包括的なRDocドキュメント
9
+ - **自己平衡二分探索木**: 赤黒木の性質により最適なパフォーマンスを維持。挿入・削除・検索がすべてO(log n)。
10
+ - **順序付き操作**: ソート済みイテレーション、範囲クエリ(lt, gt, between)、最小/最大値取得が高速に実行可能。
11
+ - **複数値サポート**: `MultiRBTree`クラスで同一キーに複数の値を格納。値は挿入順に保持され、最初または最後の値を個別にアクセス可能。
12
+ - **ピュアRuby**: C拡張不要。MRI, JRuby, TruffleRubyなどあらゆるRuby実装で動作。
13
+ - **ハイブリッドインデックス**: 内部ハッシュインデックスにより、キー検索と存在確認がO(1)の超高速アクセスを実現。
14
+ - **メモリ効率**: ノードプールによるオブジェクト再利用と自動縮小機能でGC負荷を大幅に削減。長時間実行アプリにも適応。
15
+ - **最近傍検索**: 数値キーに対して、最も近いキーペアをO(log n)で効率的に探索。
16
+ - **安全なイテレーション**: `safe: true`オプションにより、イテレーション中に他の操作(削除・挿入)を安全に実行可能。
14
17
 
15
18
  ## インストール
16
19
 
@@ -77,9 +80,16 @@ tree.max # => [20, "twenty"]
77
80
 
78
81
  # 範囲クエリ(Enumeratorを返す、配列には.to_aを使用)
79
82
  tree.lt(10).to_a # => [[1, "one"], [2, "two"], [3, "three"]]
80
- tree.gte(10).to_a # => [[10, "ten"], [20, "twenty"]]
83
+ tree.gte(10).each { |k, v| puts k } # ブロック渡しでのループ
81
84
  tree.between(2, 10).to_a # => [[2, "two"], [3, "three"], [10, "ten"]]
82
85
 
86
+ # []でのRangeオブジェクトの利用 (v0.3.4+)
87
+ tree[..10].to_a # lte(10)相当
88
+ tree[2..10].each { |k, v| ... } # Rangeでのループ
89
+ tree[2...10].to_a # between(2, 10, include_max: false)相当
90
+ tree[10..].to_a # gte(10)相当
91
+ tree[2..10, reverse: true].to_a # オプション指定可能
92
+
83
93
  # shiftとpop
84
94
  tree.shift # => [1, "one"] (最小値を削除)
85
95
  tree.pop # => [20, "twenty"] (最大値を削除)
data/README.md CHANGED
@@ -6,11 +6,14 @@ A pure Ruby implementation of the Red-Black Tree data structure, providing effic
6
6
 
7
7
  ## Features
8
8
 
9
- - **Self-Balancing Binary Search Tree**: Maintains optimal performance through red-black tree properties
10
- - **Ordered Operations**: Efficient range queries, min/max retrieval, and sorted iteration
11
- - **Multi-Value Support**: `MultiRBTree` class for storing multiple values per key
12
- - **Pure Ruby**: No C extensions required, works on any Ruby implementation
13
- - **Well-Documented**: Comprehensive RDoc documentation with examples
9
+ - **Self-Balancing Binary Search Tree**: Maintains optimal performance through red-black tree properties. All insertions, deletions, and lookups run in O(log n).
10
+ - **Ordered Operations**: Efficient sorted iteration, range queries (`lt`, `gt`, `between`), min/max retrieval.
11
+ - **Multi-Value Support**: `MultiRBTree` class stores multiple values per key, with access to first or last value individually.
12
+ - **Pure Ruby**: No C extensions required. Works on MRI, JRuby, TruffleRuby, and all Ruby implementations.
13
+ - **Hybrid Indexing**: Internal hash index enables O(1) key lookup and membership checks — matching standard Hash performance.
14
+ - **Memory Efficiency**: Node recycling with auto-shrinking pool (`AutoShrinkNodePool`) drastically reduces GC pressure in long-running apps.
15
+ - **Nearest Key Search**: Finds the closest numeric key in O(log n) time — ideal for spatial or temporal queries.
16
+ - **Safe Iteration**: Use `safe: true` to safely modify the tree (insert/delete) during iteration.
14
17
 
15
18
  ## Installation
16
19
 
@@ -77,9 +80,16 @@ tree.max # => [20, "twenty"]
77
80
 
78
81
  # Range queries (return Enumerator, use .to_a for Array)
79
82
  tree.lt(10).to_a # => [[1, "one"], [2, "two"], [3, "three"]]
80
- tree.gte(10).to_a # => [[10, "ten"], [20, "twenty"]]
83
+ tree.gte(10).each { |k, v| puts k } # Block iteration
81
84
  tree.between(2, 10).to_a # => [[2, "two"], [3, "three"], [10, "ten"]]
82
85
 
86
+ # Range objects in [] (v0.3.4+)
87
+ tree[..10].to_a # lte(10)
88
+ tree[2..10].each { |k, v| ... } # Block iteration on Range
89
+ tree[2...10].to_a # between(2, 10, include_max: false)
90
+ tree[10..].to_a # gte(10)
91
+ tree[2..10, reverse: true].to_a # with options
92
+
83
93
  # Shift and pop
84
94
  tree.shift # => [1, "one"] (removes minimum)
85
95
  tree.pop # => [20, "twenty"] (removes maximum)
@@ -2,5 +2,5 @@
2
2
 
3
3
  class RBTree
4
4
  # The version of the rbtree-ruby gem
5
- VERSION = "0.3.2"
5
+ VERSION = "0.3.4"
6
6
  end
data/lib/rbtree.rb CHANGED
@@ -191,11 +191,35 @@ class RBTree
191
191
  # @example
192
192
  # tree = RBTree.new({1 => 'one', 2 => 'two'})
193
193
  # tree.get(1) # => "one"
194
- # tree[2] # => "two"
195
- # tree[3] # => nil
196
194
  def value(key) = @hash_index[key]&.value
197
195
  alias :get :value
198
- alias :[] :value
196
+
197
+ # Retrieves a value associated with the given key, or a range of entries if a Range is provided.
198
+ #
199
+ # @param key_or_range [Object, Range] the key to look up or a Range for query
200
+ # @param ... [Hash] additional options to pass to the respective lookup method
201
+ # @return [Object, Enumerator, nil]
202
+ # - If a key is provided: the associated value, or nil if not found
203
+ # - If a Range is provided: an Enumerator yielding [key, value] pairs
204
+ # @example Single key lookup
205
+ # tree[2] # => "two"
206
+ # @example Range lookup
207
+ # tree[2..4].to_a # => [[2, "two"], [3, "three"], [4, "four"]]
208
+ # tree[...3].to_a # => [[1, "one"], [2, "two"]]
209
+ def [](key_or_range, **)
210
+ return value(key_or_range, **) if !key_or_range.is_a?(Range)
211
+
212
+ r = key_or_range
213
+ r.begin ? (
214
+ r.end ?
215
+ between(r.begin, r.end, include_max: !r.exclude_end?, **) :
216
+ gte(r.begin, **)
217
+ ) : (
218
+ r.end ?
219
+ (r.exclude_end? ? lt(r.end, **) : lte(r.end, **)) :
220
+ each(**)
221
+ )
222
+ end
199
223
 
200
224
  # Returns the key with the key closest to the given key.
201
225
  #
@@ -432,7 +456,7 @@ class RBTree
432
456
  # tree.delete(k) if k.even?
433
457
  # end
434
458
  def keys(reverse: false, safe: false, &block)
435
- return enum_for(:keys, reverse: reverse, safe: safe) { @key_count } unless block_given?
459
+ return enum_for(__method__, reverse: reverse, safe: safe) { @key_count } unless block_given?
436
460
  each(reverse: reverse, safe: safe) { |key, _| yield key }
437
461
  self
438
462
  end
@@ -459,7 +483,7 @@ class RBTree
459
483
  # tree.delete(k) if k.even?
460
484
  # end
461
485
  def each(reverse: false, safe: false, &block)
462
- return enum_for(:each, reverse: reverse, safe: safe) { size } unless block_given?
486
+ return enum_for(__method__, reverse: reverse, safe: safe) { size } unless block_given?
463
487
  if reverse
464
488
  traverse_all_desc(@root, safe: safe, &block)
465
489
  else
@@ -488,7 +512,7 @@ class RBTree
488
512
  # @return [Enumerator, RBTree] an Enumerator if no block is given, self otherwise
489
513
  # @see #each
490
514
  def reverse_each(safe: false, &block)
491
- return enum_for(:reverse_each, safe: safe) { size } unless block_given?
515
+ return enum_for(__method__, safe: safe) { size } unless block_given?
492
516
  each(reverse: true, safe: safe, &block)
493
517
  end
494
518
 
@@ -505,7 +529,7 @@ class RBTree
505
529
  # tree.lt(3, reverse: true).first # => [2, "two"]
506
530
  # tree.lt(3, safe: true) { |k, _| tree.delete(k) if k.even? } # safe to delete
507
531
  def lt(key, reverse: false, safe: false, &block)
508
- return enum_for(:lt, key, reverse: reverse, safe: safe) unless block_given?
532
+ return enum_for(__method__, key, reverse: reverse, safe: safe) unless block_given?
509
533
  if reverse
510
534
  traverse_lt_desc(@root, key, safe: safe, &block)
511
535
  else
@@ -526,7 +550,7 @@ class RBTree
526
550
  # tree.lte(3).to_a # => [[1, "one"], [2, "two"], [3, "three"]]
527
551
  # tree.lte(3, reverse: true).first # => [3, "three"]
528
552
  def lte(key, reverse: false, safe: false, &block)
529
- return enum_for(:lte, key, reverse: reverse, safe: safe) unless block_given?
553
+ return enum_for(__method__, key, reverse: reverse, safe: safe) unless block_given?
530
554
  if reverse
531
555
  traverse_lte_desc(@root, key, safe: safe, &block)
532
556
  else
@@ -547,7 +571,7 @@ class RBTree
547
571
  # tree.gt(2).to_a # => [[3, "three"], [4, "four"]]
548
572
  # tree.gt(2, reverse: true).first # => [4, "four"]
549
573
  def gt(key, reverse: false, safe: false, &block)
550
- return enum_for(:gt, key, reverse: reverse, safe: safe) unless block_given?
574
+ return enum_for(__method__, key, reverse: reverse, safe: safe) unless block_given?
551
575
  if reverse
552
576
  traverse_gt_desc(@root, key, safe: safe, &block)
553
577
  else
@@ -568,7 +592,7 @@ class RBTree
568
592
  # tree.gte(2).to_a # => [[2, "two"], [3, "three"], [4, "four"]]
569
593
  # tree.gte(2, reverse: true).first # => [4, "four"]
570
594
  def gte(key, reverse: false, safe: false, &block)
571
- return enum_for(:gte, key, reverse: reverse, safe: safe) unless block_given?
595
+ return enum_for(__method__, key, reverse: reverse, safe: safe) unless block_given?
572
596
  if reverse
573
597
  traverse_gte_desc(@root, key, safe: safe, &block)
574
598
  else
@@ -592,7 +616,7 @@ class RBTree
592
616
  # tree.between(2, 4).to_a # => [[2, "two"], [3, "three"], [4, "four"]]
593
617
  # tree.between(2, 4, reverse: true).first # => [4, "four"]
594
618
  def between(min, max, include_min: true, include_max: true, reverse: false, safe: false, &block)
595
- return enum_for(:between, min, max, include_min: include_min, include_max: include_max, reverse: reverse, safe: safe) unless block_given?
619
+ return enum_for(__method__, min, max, include_min: include_min, include_max: include_max, reverse: reverse, safe: safe) unless block_given?
596
620
  if reverse
597
621
  traverse_between_desc(@root, min, max, include_min, include_max, safe: safe, &block)
598
622
  else
@@ -1194,6 +1218,8 @@ class RBTree
1194
1218
  # @param key [Object] the reference key
1195
1219
  # @return [Node] the successor node, or @nil_node if none exists
1196
1220
  def find_successor_node(key)
1221
+ # If key is smaller than min_key, return min_node
1222
+ return @min_node if min_key && key < min_key
1197
1223
  # Check if key exists using O(1) hash lookup
1198
1224
  if (node = @hash_index[key])
1199
1225
  # Key exists: find successor in subtree or ancestors
@@ -1496,6 +1522,7 @@ class MultiRBTree < RBTree
1496
1522
  # tree.get(1) # => "first"
1497
1523
  # tree.get(1, last: true) # => "second"
1498
1524
  def value(key, last: false) = @hash_index[key]&.value&.send(last ? :last : :first)
1525
+ alias :get :value
1499
1526
 
1500
1527
  # Retrieves the first value associated with the given key.
1501
1528
  #
@@ -1521,7 +1548,7 @@ class MultiRBTree < RBTree
1521
1548
  # tree.insert(1, 'second')
1522
1549
  # tree.values(1).to_a # => ["first", "second"]
1523
1550
  def values(key, reverse: false)
1524
- return enum_for(:values, key) { value_count(key) } unless block_given?
1551
+ return enum_for(__method__, key) { value_count(key) } unless block_given?
1525
1552
  @hash_index[key]&.value&.send(reverse ? :reverse_each : :each) { |v| yield v }
1526
1553
  end
1527
1554
  alias :get_all :values
@@ -1622,6 +1649,7 @@ class MultiRBTree < RBTree
1622
1649
  delete_indexed_node(z.key)
1623
1650
  value
1624
1651
  end
1652
+ alias :delete :delete_key
1625
1653
 
1626
1654
  # Removes and returns the first key-value pair.
1627
1655
  #
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rbtree-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.3.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Masahito Suzuki