rbtree-ruby 0.2.2 → 0.2.3

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: 13b088e9f0f8ab5cf80a77b8f9b8e1af73b94b33bdff90115fe575def2c54b11
4
- data.tar.gz: 51636a7c22c4ccc70d2ac14bafc58a39bbccac7599a704506c280d8d39345117
3
+ metadata.gz: f3c97bfa59aaaa7bbc03f272fdc5e383b258f529134f1d2a79fd3758d1d53b02
4
+ data.tar.gz: fa9c8df14da95bf4224164d720c90981c1c68ef96e5cb544007327b9a1564b11
5
5
  SHA512:
6
- metadata.gz: e543801d0b22de4ee8654f7b8164121c2107ccda64bc16f47d034e98526703e4aa444e03cecd225bddb9aa9fc5547813bcd1bc73cd4a4048df98eee5d41a2fc6
7
- data.tar.gz: 6d6986550fe2293e971558e65119f230c3eb26e8f43f8af77fe8bf666d5df337c6bf23e36a4e4fd7e8aee86287b6f58579e48a637e66d27a6e80ab66b0010ed4
6
+ metadata.gz: e856b4d10d326c3d3261d21044db760b41447dd01e1eb22bff4dfe1ac07e71f709792b75a9ee48ddc3195f06993fd86e8434511b3f2f0eaafbcf0827da05aa1e
7
+ data.tar.gz: 10e8c1af8787b201c73a64557ba86304867b601daae13ad816771b87e52a81e30bc94dbeda5accc1e224a55d397ae28e0325918d61d8d4db40ee09cdd83876fa
data/CHANGELOG.md CHANGED
@@ -5,6 +5,16 @@ 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.2.3] - 2026-01-14
9
+
10
+ ### Added
11
+ - **Bulk Insert**: `insert` now supports bulk insertion from Hash, Array of pairs, Enumerator, or Block.
12
+ - `tree.insert({1 => 'one', 2 => 'two'})`
13
+ - `tree.insert([[1, 'one'], [2, 'two']])`
14
+ - `tree.insert { source_data }`
15
+ - **Flexible Initialization**: `RBTree.new` and `MultiRBTree.new` now accept bulk data and an `overwrite:` option.
16
+ - `RBTree.new([[1, 'a'], [1, 'b']], overwrite: false)`
17
+
8
18
  ## [0.2.2] - 2026-01-14
9
19
 
10
20
  ### Changed
data/README.ja.md CHANGED
@@ -2,8 +2,7 @@
2
2
 
3
3
  🌍 *[English](README.md) | [日本語](README.ja.md)*
4
4
 
5
- Red-Black Tree(赤黒木)データ構造のピュアRuby実装です。挿入、削除、検索操作がO(log n)の時間計算量で実行できる、
6
- 効率的な順序付きキーバリューストレージを提供します。
5
+ Red-Black Tree(赤黒木)データ構造のピュアRuby実装です。挿入、削除、検索操作がO(log n)の時間計算量で実行できる、効率的な順序付きキーバリューストレージを提供します。
7
6
 
8
7
  ## 特徴
9
8
 
@@ -43,13 +42,18 @@ require 'rbtree'
43
42
  # 空のツリーを作成
44
43
  tree = RBTree.new
45
44
 
46
- # データで初期化
45
+ # データで初期化(バルク挿入)
47
46
  tree = RBTree.new({3 => 'three', 1 => 'one', 2 => 'two'})
48
47
  tree = RBTree[[5, 'five'], [4, 'four']]
48
+ tree = RBTree.new do # ブロック初期化
49
+ data_source.each { |data| [data.time, data.content] }
50
+ end
49
51
 
50
52
  # 値の挿入と取得
51
53
  tree.insert(10, 'ten')
52
54
  tree[20] = 'twenty'
55
+ # バルク挿入
56
+ tree.insert({30 => 'thirty', 40 => 'forty'})
53
57
  puts tree[10] # => "ten"
54
58
 
55
59
  # ソート順でイテレーション
@@ -138,6 +142,8 @@ tree.nearest(8) # => [10, "ten"]
138
142
 
139
143
  ### 前後キー検索
140
144
 
145
+ ツリーの中で次のキーまたは前のキーを検索します。
146
+
141
147
  ```ruby
142
148
  tree = RBTree.new({1 => 'one', 3 => 'three', 5 => 'five', 7 => 'seven'})
143
149
 
@@ -202,8 +208,8 @@ tree.max(last: true) # => [2, "b"] (最大キーの最後の値)
202
208
 
203
209
  - `insert(key, value)` - O(log n)
204
210
  - `delete(key)` - O(log n)
205
- - `get(key)` / `[]` - **O(1)** (ハイブリッドハッシュインデックス)
206
- - `has_key?` - **O(1)** (ハイブリッドハッシュインデックス)
211
+ - `get(key)` / `[]` - **O(1)** (内部ハッシュインデックスによる超高速アクセス)
212
+ - `has_key?` - **O(1)** (内部ハッシュインデックスによる超高速チェック)
207
213
  - `min` - **O(1)**
208
214
  - `max` - O(log n)
209
215
  - `shift` / `pop` - O(log n)
@@ -227,7 +233,7 @@ RBTreeは内部的な**メモリプール**を使用してノードオブジェ
227
233
  | **範囲クエリ** | **O(log n + k)** | O(n) フィルター | **〜540倍高速** | 部分木へ直接ジャンプ vs 全件スキャン |
228
234
  | **最小値抽出** | **O(log n)** | O(n) 検索 | **〜160倍高速** | 連続的なリバランス vs 全件スキャン |
229
235
  | **ソート済みイテレーション** | **O(n)** | O(n log n) | **無料** | 常にソート済み vs 明示的な`sort` |
230
- | **キー検索** | **O(1)** | O(1) | **同等** | ハイブリッドハッシュインデックス |
236
+ | **キー検索** | **O(1)** | O(1) | **同等** | **ハイブリッドハッシュインデックスにより、Hashと同等のO(1)検索速度を実現** |
231
237
 
232
238
  ### RBTreeを使うべき場面
233
239
 
data/README.md CHANGED
@@ -2,8 +2,7 @@
2
2
 
3
3
  🌍 *[English](README.md) | [日本語](README.ja.md)*
4
4
 
5
- A pure Ruby implementation of the Red-Black Tree data structure, providing efficient ordered
6
- key-value storage with O(log n) time complexity for insertion, deletion, and lookup operations.
5
+ A pure Ruby implementation of the Red-Black Tree data structure, providing efficient ordered key-value storage with O(log n) time complexity for insertion, deletion, and lookup operations.
7
6
 
8
7
  ## Features
9
8
 
@@ -43,13 +42,18 @@ require 'rbtree'
43
42
  # Create an empty tree
44
43
  tree = RBTree.new
45
44
 
46
- # Or initialize with data
45
+ # Or initialize with data (Bulk Insert)
47
46
  tree = RBTree.new({3 => 'three', 1 => 'one', 2 => 'two'})
48
47
  tree = RBTree[[5, 'five'], [4, 'four']]
48
+ tree = RBTree.new do # Block initialization
49
+ data_source.each { |data| [data.time, data.content] }
50
+ end
49
51
 
50
52
  # Insert and retrieve values
51
53
  tree.insert(10, 'ten')
52
54
  tree[20] = 'twenty'
55
+ # Bulk insert
56
+ tree.insert({30 => 'thirty', 40 => 'forty'})
53
57
  puts tree[10] # => "ten"
54
58
 
55
59
  # Iterate in sorted order
@@ -229,7 +233,7 @@ For ordered and spatial operations, RBTree is not just faster—it is in a compl
229
233
  | **Range Queries** | **O(log n + k)** | O(n) filter | **~540x faster** | Direct subtree jump vs full scan |
230
234
  | **Min Extraction** | **O(log n)** | O(n) search | **~160x faster** | Continuous rebalancing vs full scan |
231
235
  | **Sorted Iteration** | **O(n)** | O(n log n) | **FREE** | Always sorted vs explicit `sort` |
232
- | **Key Lookup** | **O(1)** | O(1) | **Equal** | Optimized Hybrid Hash Index |
236
+ | **Key Lookup** | **O(1)** | O(1) | **Equal** | **Hybrid Hash Index provides O(1) access like standard Hash** |
233
237
 
234
238
  ### When to Use RBTree
235
239
 
@@ -2,5 +2,5 @@
2
2
 
3
3
  class RBTree
4
4
  # The version of the rbtree-ruby gem
5
- VERSION = "0.2.2"
5
+ VERSION = "0.2.3"
6
6
  end
data/lib/rbtree.rb CHANGED
@@ -72,11 +72,15 @@ class RBTree
72
72
 
73
73
  # Initializes a new RBTree.
74
74
  #
75
- # The tree can be initialized empty or populated with initial data from a Hash or Array.
75
+ # The tree can be initialized empty or populated with initial data from a Hash, Array, or Enumerator.
76
+ # A block can also be provided to supply the initial data.
76
77
  #
77
78
  # @param args [Hash, Array, nil] optional initial data
79
+ # @param overwrite [Boolean] whether to overwrite existing keys (default: true)
80
+ # @yieldreturn [Object] optional initial data
78
81
  # - If a Hash is provided, each key-value pair is inserted into the tree
79
82
  # - If an Array is provided, it should contain [key, value] pairs
83
+ # - If a block is provided, it is yielded to get the source data
80
84
  # - If no arguments are provided, an empty tree is created
81
85
  # @raise [ArgumentError] if arguments are invalid
82
86
  # @example Create an empty tree
@@ -85,7 +89,9 @@ class RBTree
85
89
  # tree = RBTree.new({1 => 'one', 2 => 'two'})
86
90
  # @example Create from an array
87
91
  # tree = RBTree.new([[1, 'one'], [2, 'two']])
88
- def initialize(*args)
92
+ # @example Create with overwrite: false
93
+ # tree = RBTree.new([[1, 'one'], [1, 'uno']], overwrite: false)
94
+ def initialize(*args, overwrite: true, &block)
89
95
  @nil_node = Node.new
90
96
  @nil_node.color = Node::BLACK
91
97
  @nil_node.left = @nil_node
@@ -96,19 +102,8 @@ class RBTree
96
102
  @node_pool = [] # Memory pool for recycling nodes
97
103
  @size = 0
98
104
 
99
- if args.any?
100
- source = args.size == 1 ? args.first : args
101
- case source
102
- when Hash
103
- source.each { |k, v| insert(k, v) }
104
- when Array
105
- source.each do |arg|
106
- key, value = arg
107
- insert(key, value)
108
- end
109
- else
110
- raise ArgumentError, "Invalid arguments"
111
- end
105
+ if args.size > 0 || block_given?
106
+ insert(*args, overwrite: overwrite, &block)
112
107
  end
113
108
  end
114
109
 
@@ -211,63 +206,67 @@ class RBTree
211
206
  n == @nil_node ? nil : n.pair
212
207
  end
213
208
 
214
- # Inserts or updates a key-value pair in the tree.
209
+ # Inserts one or more key-value pairs into the tree.
210
+ #
211
+ # This method supports both single entry insertion and bulk insertion.
212
+ #
213
+ # Single insertion:
214
+ # insert(key, value, overwrite: true)
215
+ #
216
+ # Bulk insertion:
217
+ # insert(hash, overwrite: true)
218
+ # insert(array_of_pairs, overwrite: true)
219
+ # insert(enumerator, overwrite: true)
220
+ # insert { data_source }
215
221
  #
216
222
  # If the key already exists and overwrite is true (default), the value is updated.
217
223
  # If overwrite is false and the key exists, the operation returns nil without modification.
218
224
  #
219
- # @param key [Object] the key to insert (must implement <=>)
220
- # @param value [Object] the value to associate with the key
225
+ # @param args [Object] key (and value) or source object
221
226
  # @param overwrite [Boolean] whether to overwrite existing keys (default: true)
222
- # @return [Boolean, nil] true if inserted/updated, nil if key exists and overwrite is false
223
- # @example
224
- # tree = RBTree.new
225
- # tree.insert(1, 'one') # => true
226
- # tree.insert(1, 'ONE') # => true (overwrites)
227
- # tree.insert(1, 'uno', overwrite: false) # => nil (no change)
228
- # tree[2] = 'two' # using alias
229
- def insert(key, value, overwrite: true)
230
- if (node = @hash_index[key])
231
- return nil unless overwrite
232
- node.value = value
233
- return true
234
- end
235
- y = @nil_node
236
- x = @root
237
- while x != @nil_node
238
- y = x
239
- cmp = key <=> x.key
240
- if cmp == 0
241
- return nil unless overwrite
242
- x.value = value
243
- return true
244
- elsif cmp < 0
245
- x = x.left
227
+ # @yieldreturn [Object] data source for bulk insertion
228
+ # @return [Boolean, nil] true if inserted/updated, nil if key exists and overwrite is false (for single insert)
229
+ # @example Single insert
230
+ # tree.insert(1, 'one')
231
+ # @example Bulk insert from Hash
232
+ # tree.insert({1 => 'one', 2 => 'two'})
233
+ # @example Bulk insert from Array
234
+ # tree.insert([[1, 'one'], [2, 'two']])
235
+ def insert(*args, overwrite: true, &block)
236
+ if args.size == 2
237
+ key, value = args
238
+ insert_entry(key, value, overwrite: overwrite)
239
+ else
240
+ source = nil
241
+ if args.empty? && block_given?
242
+ source = yield
243
+ elsif args.size == 1
244
+ source = args[0]
245
+ elsif args.empty?
246
+ return # No-op
246
247
  else
247
- x = x.right
248
+ raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 0..2)"
249
+ end
250
+
251
+ return if source.nil?
252
+
253
+ unless source.respond_to?(:each)
254
+ raise ArgumentError, "Source must be iterable"
255
+ end
256
+
257
+ source.each do |*pair|
258
+ key, value = nil, nil
259
+ if pair.size == 1 && pair[0].is_a?(Array)
260
+ key, value = pair[0]
261
+ raise ArgumentError, "Invalid pair size: #{pair[0].size} (expected 2)" unless pair[0].size == 2
262
+ elsif pair.size == 2
263
+ key, value = pair
264
+ else
265
+ raise ArgumentError, "Invalid pair format: #{pair.inspect}"
266
+ end
267
+ insert_entry(key, value, overwrite: overwrite)
248
268
  end
249
269
  end
250
- z = allocate_node(key, value, Node::RED, @nil_node, @nil_node, @nil_node)
251
- z.parent = y
252
- if y == @nil_node
253
- @root = z
254
- elsif (key <=> y.key) < 0
255
- y.left = z
256
- else
257
- y.right = z
258
- end
259
- z.left = @nil_node
260
- z.right = @nil_node
261
- z.color = Node::RED
262
- insert_fixup(z)
263
- @size += 1
264
-
265
- if @min_node == @nil_node || (key <=> @min_node.key) < 0
266
- @min_node = z
267
- end
268
-
269
- @hash_index[key] = z # Add to hash index
270
- true
271
270
  end
272
271
  alias_method :[]=, :insert
273
272
 
@@ -518,8 +517,58 @@ class RBTree
518
517
  true
519
518
  end
520
519
 
521
- # @!visibility private
522
- private
520
+ # @!visibility protected
521
+ protected
522
+
523
+ # Inserts a single key-value pair.
524
+ #
525
+ # @param key [Object] the key to insert
526
+ # @param value [Object] the value to associate with the key
527
+ # @param overwrite [Boolean] whether to overwrite existing keys (default: true)
528
+ # @return [Boolean, nil] true if inserted/updated, nil if key exists and overwrite is false
529
+ def insert_entry(key, value, overwrite: true)
530
+ if (node = @hash_index[key])
531
+ return nil unless overwrite
532
+ node.value = value
533
+ return true
534
+ end
535
+ y = @nil_node
536
+ x = @root
537
+ while x != @nil_node
538
+ y = x
539
+ cmp = key <=> x.key
540
+ if cmp == 0
541
+ return nil unless overwrite
542
+ x.value = value
543
+ return true
544
+ elsif cmp < 0
545
+ x = x.left
546
+ else
547
+ x = x.right
548
+ end
549
+ end
550
+ z = allocate_node(key, value, Node::RED, @nil_node, @nil_node, @nil_node)
551
+ z.parent = y
552
+ if y == @nil_node
553
+ @root = z
554
+ elsif (key <=> y.key) < 0
555
+ y.left = z
556
+ else
557
+ y.right = z
558
+ end
559
+ z.left = @nil_node
560
+ z.right = @nil_node
561
+ z.color = Node::RED
562
+ insert_fixup(z)
563
+ @size += 1
564
+
565
+ if @min_node == @nil_node || (key <=> @min_node.key) < 0
566
+ @min_node = z
567
+ end
568
+
569
+ @hash_index[key] = z # Add to hash index
570
+ true
571
+ end
523
572
 
524
573
  # Traverses the tree in ascending order (in-order traversal).
525
574
  #
@@ -1377,12 +1426,13 @@ class MultiRBTree < RBTree
1377
1426
  #
1378
1427
  # @param key [Object] the key (must implement <=>)
1379
1428
  # @param value [Object] the value to insert
1429
+ # @param overwrite [Boolean] ignored for MultiRBTree which always appends
1380
1430
  # @return [Boolean] always returns true
1381
1431
  # @example
1382
1432
  # tree = MultiRBTree.new
1383
1433
  # tree.insert(1, 'first')
1384
1434
  # tree.insert(1, 'second') # adds another value for key 1
1385
- def insert(key, value)
1435
+ def insert_entry(key, value, **)
1386
1436
  if (node = @hash_index[key])
1387
1437
  node.value << value
1388
1438
  @size += 1
@@ -1527,7 +1577,8 @@ class MultiRBTree < RBTree
1527
1577
  @hash_index[key]&.value&.each { |v| yield v }
1528
1578
  end
1529
1579
 
1530
- private
1580
+ # @!visibility protected
1581
+ protected
1531
1582
 
1532
1583
  def traverse_range_asc(...)
1533
1584
  super { |k, vals| vals.each { |v| yield k, v } }
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.2.2
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Masahito Suzuki