veb_tree 0.1.0 → 0.1.1
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 +6 -1
- data/README.md +266 -35
- data/ext/veb_tree/extconf.rb +21 -17
- data/ext/veb_tree/veb_tree_ext.cpp +46 -1
- data/lib/veb_tree/version.rb +1 -1
- metadata +1 -2
- data/lib/veb_tree/pure_ruby.rb +0 -277
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b518aea4b85123bdda532a06f561bcd24af252f724f64ded4e4089451f9273f7
|
4
|
+
data.tar.gz: 3527c4ebc1ebc586b90fabc57c3bcefc0650df20c53b399c21613229cd3a2fec
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 85d0512562fb3c530b8d01ee6ca96ee1354ab7254ae6ed539d9e8530cc8f00533ce20279c4bd0082bcdaaf4f94536a20c2aa3250f5d190816c6da2fb5315ffa4
|
7
|
+
data.tar.gz: 67962e7fa31d7c63ce8d4144a822e2ed642f041f1d689ce65fda357bf8cb28705eab62d81921e8bf80ac123de3df3e9409ab2697bf98b6f10e395ba03764f3ef
|
data/CHANGELOG.md
CHANGED
@@ -5,7 +5,12 @@ 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.1.
|
8
|
+
## [0.1.1] - 2025-10-03
|
9
|
+
|
10
|
+
### Added
|
11
|
+
- Fix compatibility issues for ruby 2.7 and macos
|
12
|
+
|
13
|
+
## [0.1.0] - 2025-10-03
|
9
14
|
|
10
15
|
### Added
|
11
16
|
- Initial release of VebTree gem
|
data/README.md
CHANGED
@@ -1,32 +1,39 @@
|
|
1
1
|
# VebTree - Van Emde Boas Tree
|
2
2
|
|
3
|
-
|
3
|
+
[](https://github.com/abhinvv1/Van-Emde-Boas-tree/actions)
|
4
|
+
[](https://badge.fury.io/rb/veb_tree)
|
4
5
|
|
5
|
-
|
6
|
+
A high-performance **Van Emde Boas (vEB) tree** implementation for Ruby with a C++17 core.
|
7
|
+
Provides **O(log log U)** time complexity for integer set operations, making it exponentially faster than balanced BSTs for many workloads.
|
6
8
|
|
7
|
-
|
8
|
-
- **Native Performance**: Core algorithm implemented in C++17
|
9
|
-
- **Simple API**: Clean, idiomatic Ruby interface
|
10
|
-
- **Memory Efficient**: Lazy cluster allocation
|
11
|
-
- **Battle Tested**: Comprehensive test suite
|
9
|
+
---
|
12
10
|
|
13
|
-
##
|
11
|
+
## ✨ Features
|
14
12
|
|
15
|
-
|
13
|
+
- ⚡ **Blazing Fast** — `O(log log U)` operations for insert, delete, search, successor, and predecessor
|
14
|
+
- 🖥 **Native Performance** — core implemented in **C++17**
|
15
|
+
- 🧑💻 **Simple API** — clean, idiomatic Ruby interface
|
16
|
+
- 🧩 **Memory Efficient** — lazy cluster allocation to minimize space usage
|
17
|
+
- ✅ **Battle Tested** — comprehensive test suite included
|
18
|
+
|
19
|
+
---
|
20
|
+
|
21
|
+
## 📦 Installation
|
16
22
|
|
17
|
-
|
18
|
-
-
|
19
|
-
|
20
|
-
- **
|
21
|
-
- **
|
23
|
+
### Requirements
|
24
|
+
- **Ruby**: 2.7 or higher
|
25
|
+
- **C++17** compatible compiler:
|
26
|
+
- **Linux**: GCC 7+ or Clang 5+
|
27
|
+
- **macOS**: Xcode Command Line Tools
|
28
|
+
- **Windows**: MinGW-w64 or MSVC 2017+
|
22
29
|
|
23
30
|
### Install via RubyGems
|
24
31
|
```bash
|
25
32
|
gem install veb_tree
|
26
33
|
```
|
27
34
|
|
28
|
-
|
29
|
-
```
|
35
|
+
### Install from Source
|
36
|
+
```bash
|
30
37
|
git clone https://github.com/yourusername/veb_tree.git
|
31
38
|
cd veb_tree
|
32
39
|
bundle install
|
@@ -36,11 +43,14 @@ gem build veb_tree.gemspec
|
|
36
43
|
gem install veb_tree-*.gem
|
37
44
|
```
|
38
45
|
|
39
|
-
|
40
|
-
|
46
|
+
---
|
47
|
+
|
48
|
+
## 🚀 Quick Start
|
49
|
+
|
50
|
+
```ruby
|
41
51
|
require 'veb_tree'
|
42
52
|
|
43
|
-
# Create a tree with universe size (
|
53
|
+
# Create a tree with universe size (rounded to next power of 2)
|
44
54
|
tree = VebTree::Tree.new(1000) # Actual size: 1024
|
45
55
|
|
46
56
|
# Insert elements
|
@@ -49,21 +59,21 @@ tree.insert(100)
|
|
49
59
|
tree.insert(7)
|
50
60
|
tree.insert(500)
|
51
61
|
|
52
|
-
#
|
62
|
+
# Membership check
|
53
63
|
tree.include?(42) # => true
|
54
64
|
tree.include?(99) # => false
|
55
65
|
|
56
|
-
# Min/Max
|
66
|
+
# Min/Max
|
57
67
|
tree.min # => 7
|
58
68
|
tree.max # => 500
|
59
69
|
|
60
|
-
# Successor/Predecessor
|
70
|
+
# Successor / Predecessor
|
61
71
|
tree.successor(42) # => 100
|
62
72
|
tree.predecessor(100) # => 42
|
63
73
|
|
64
|
-
# Size
|
65
|
-
tree.size
|
66
|
-
tree.empty?
|
74
|
+
# Size & emptiness
|
75
|
+
tree.size # => 4
|
76
|
+
tree.empty? # => false
|
67
77
|
|
68
78
|
# Iterate in sorted order
|
69
79
|
tree.each { |key| puts key }
|
@@ -74,23 +84,244 @@ tree.to_a # => [7, 42, 100, 500]
|
|
74
84
|
|
75
85
|
# Delete elements
|
76
86
|
tree.delete(42) # => true
|
77
|
-
tree.delete(42) # => false (
|
87
|
+
tree.delete(42) # => false (already removed)
|
78
88
|
|
79
89
|
# Clear all elements
|
80
90
|
tree.clear
|
81
91
|
```
|
82
92
|
|
83
|
-
|
84
|
-
```VebTree::Tree.new(universe_size)```
|
93
|
+
---
|
85
94
|
|
86
|
-
|
87
|
-
- universe_size (Integer): Maximum value that can be stored (exclusive). Will be
|
88
|
-
- rounded up to the next power of 2.
|
89
|
-
- Returns: New VebTree::Tree instance
|
90
|
-
- Raises: ArgumentError if universe_size is not positive
|
95
|
+
## 📖 API Reference
|
91
96
|
|
92
|
-
|
97
|
+
### Constructor
|
98
|
+
```ruby
|
99
|
+
VebTree::Tree.new(universe_size)
|
93
100
|
```
|
94
|
-
|
101
|
+
|
102
|
+
- **universe_size (Integer)** — maximum value that can be stored (exclusive).
|
103
|
+
Automatically rounded up to the next power of 2.
|
104
|
+
- **Returns**: `VebTree::Tree` instance
|
105
|
+
- **Raises**: `ArgumentError` if universe_size ≤ 0
|
106
|
+
|
107
|
+
**Example:**
|
108
|
+
```ruby
|
109
|
+
tree = VebTree::Tree.new(100)
|
95
110
|
tree.universe_size # => 128
|
96
111
|
```
|
112
|
+
|
113
|
+
---
|
114
|
+
|
115
|
+
### Core Operations
|
116
|
+
|
117
|
+
#### Insert
|
118
|
+
```ruby
|
119
|
+
tree.insert(key) → Boolean
|
120
|
+
```
|
121
|
+
- Inserts a key.
|
122
|
+
- **Time**: O(log log U)
|
123
|
+
- Returns `true` if inserted, `false` if already present.
|
124
|
+
|
125
|
+
#### Delete
|
126
|
+
```ruby
|
127
|
+
tree.delete(key) → Boolean
|
128
|
+
```
|
129
|
+
- Removes a key.
|
130
|
+
- **Time**: O(log log U)
|
131
|
+
- Returns `true` if deleted, `false` if not found.
|
132
|
+
|
133
|
+
#### Membership
|
134
|
+
```ruby
|
135
|
+
tree.include?(key) → Boolean
|
136
|
+
tree.member?(key) # alias
|
137
|
+
```
|
138
|
+
- Checks if key exists.
|
139
|
+
- **Time**: O(log log U)
|
140
|
+
|
141
|
+
#### Min / Max
|
142
|
+
```ruby
|
143
|
+
tree.min → Integer or nil
|
144
|
+
tree.max → Integer or nil
|
145
|
+
```
|
146
|
+
- Returns smallest or largest key.
|
147
|
+
- **Time**: O(1)
|
148
|
+
|
149
|
+
#### Successor / Predecessor
|
150
|
+
```ruby
|
151
|
+
tree.successor(key) → Integer or nil
|
152
|
+
tree.predecessor(key) → Integer or nil
|
153
|
+
```
|
154
|
+
- Finds next higher or next lower key.
|
155
|
+
- **Time**: O(log log U)
|
156
|
+
|
157
|
+
---
|
158
|
+
|
159
|
+
### Utility Methods
|
160
|
+
|
161
|
+
- `tree.size` → number of elements (**O(1)**)
|
162
|
+
- `tree.empty?` → true/false (**O(1)**)
|
163
|
+
- `tree.universe_size` → current universe size
|
164
|
+
- `tree.clear` → removes all elements
|
165
|
+
|
166
|
+
---
|
167
|
+
|
168
|
+
### Enumeration
|
169
|
+
|
170
|
+
The tree includes `Enumerable`, so all Ruby iteration helpers work:
|
171
|
+
|
172
|
+
```ruby
|
173
|
+
tree.each { |key| puts key }
|
174
|
+
tree.to_a # => [7, 42, 100, 500]
|
175
|
+
tree.map { |x| x * 2 } # => [14, 84, 200, 1000]
|
176
|
+
tree.select { |x| x > 50 } # => [100, 500]
|
177
|
+
tree.count # => 4
|
178
|
+
```
|
179
|
+
|
180
|
+
---
|
181
|
+
|
182
|
+
## 📊 Performance
|
183
|
+
|
184
|
+
| Operation | vEB Tree | Balanced BST |
|
185
|
+
|-------------|----------|--------------|
|
186
|
+
| Insert | O(log log U) | O(log n) |
|
187
|
+
| Delete | O(log log U) | O(log n) |
|
188
|
+
| Search | O(log log U) | O(log n) |
|
189
|
+
| Successor | O(log log U) | O(log n) |
|
190
|
+
| Predecessor | O(log log U) | O(log n) |
|
191
|
+
| Min/Max | O(1) | O(log n) |
|
192
|
+
|
193
|
+
- **U** = universe size (max key)
|
194
|
+
- **n** = number of stored elements
|
195
|
+
|
196
|
+
vEB trees are best for **bounded integer sets** with frequent `successor/predecessor/min/max` queries.
|
197
|
+
|
198
|
+
⚠️ Avoid when:
|
199
|
+
- Universe size is **huge (> 2^24)**
|
200
|
+
- Need arbitrary objects (only integers supported)
|
201
|
+
- Extremely memory constrained
|
202
|
+
|
203
|
+
---
|
204
|
+
|
205
|
+
## 💾 Space Complexity
|
206
|
+
|
207
|
+
- **Theoretical**: O(U)
|
208
|
+
- **Optimized** with lazy allocation: only used clusters consume memory
|
209
|
+
|
210
|
+
**Practical Usage:**
|
211
|
+
- Universe `2^16` (65K): ~hundreds of KB
|
212
|
+
- Universe `2^20` (1M): ~few MB
|
213
|
+
- Universe `2^24` (16M): ~tens of MB
|
214
|
+
|
215
|
+
---
|
216
|
+
|
217
|
+
## ⚠️ Thread Safety
|
218
|
+
|
219
|
+
This implementation is **NOT thread-safe**.
|
220
|
+
For concurrency, wrap operations with a `Mutex`:
|
221
|
+
|
222
|
+
```ruby
|
223
|
+
require 'thread'
|
224
|
+
|
225
|
+
tree = VebTree::Tree.new(1000)
|
226
|
+
mutex = Mutex.new
|
227
|
+
|
228
|
+
mutex.synchronize do
|
229
|
+
tree.insert(42)
|
230
|
+
end
|
231
|
+
```
|
232
|
+
|
233
|
+
---
|
234
|
+
|
235
|
+
## 🛑 Error Handling
|
236
|
+
|
237
|
+
```ruby
|
238
|
+
tree = VebTree::Tree.new(0) # ArgumentError: Universe size must be > 0
|
239
|
+
tree.insert(-1) # ArgumentError: Key must be non-negative
|
240
|
+
tree.insert(200) # ArgumentError: Key exceeds universe size
|
241
|
+
|
242
|
+
tree.include?(999) # => false
|
243
|
+
tree.successor(999) # => nil
|
244
|
+
```
|
245
|
+
|
246
|
+
---
|
247
|
+
|
248
|
+
## 🧪 Examples
|
249
|
+
|
250
|
+
### Range Query Simulation
|
251
|
+
```ruby
|
252
|
+
tree = VebTree::Tree.new(10000)
|
253
|
+
|
254
|
+
100.times { tree.insert(rand(10000)) }
|
255
|
+
|
256
|
+
current = tree.successor(999) # first ≥ 1000
|
257
|
+
result = []
|
258
|
+
while current && current <= 2000
|
259
|
+
result << current
|
260
|
+
current = tree.successor(current)
|
261
|
+
end
|
262
|
+
```
|
263
|
+
|
264
|
+
### K-th Smallest Element
|
265
|
+
```ruby
|
266
|
+
def kth_smallest(tree, k)
|
267
|
+
current = tree.min
|
268
|
+
(k - 1).times do
|
269
|
+
return nil unless current
|
270
|
+
current = tree.successor(current)
|
271
|
+
end
|
272
|
+
current
|
273
|
+
end
|
274
|
+
|
275
|
+
tree = VebTree::Tree.new(1000)
|
276
|
+
[5, 10, 3, 50].each { |x| tree.insert(x) }
|
277
|
+
|
278
|
+
kth_smallest(tree, 2) # => 5
|
279
|
+
```
|
280
|
+
|
281
|
+
---
|
282
|
+
|
283
|
+
## 🔧 Development
|
284
|
+
|
285
|
+
```bash
|
286
|
+
# Clone repo
|
287
|
+
git clone https://github.com/yourusername/veb_tree.git
|
288
|
+
cd veb_tree
|
289
|
+
|
290
|
+
# Install dependencies
|
291
|
+
bundle install
|
292
|
+
|
293
|
+
# Compile extension
|
294
|
+
rake compile
|
295
|
+
|
296
|
+
# Run tests
|
297
|
+
rake test
|
298
|
+
|
299
|
+
# Clean build
|
300
|
+
rake clean
|
301
|
+
```
|
302
|
+
|
303
|
+
---
|
304
|
+
|
305
|
+
## 🤝 Contributing
|
306
|
+
|
307
|
+
Bug reports and pull requests are welcome at:
|
308
|
+
👉 [https://github.com/abhinvv1/Van-Emde-Boas-tree](https://github.com/abhinvv1/Van-Emde-Boas-tree)
|
309
|
+
|
310
|
+
---
|
311
|
+
|
312
|
+
## 📜 License
|
313
|
+
|
314
|
+
This gem is available as open source under the **MIT License**.
|
315
|
+
|
316
|
+
---
|
317
|
+
|
318
|
+
## 📚 References & Credits
|
319
|
+
|
320
|
+
- Based on the **Van Emde Boas tree** described by *Peter van Emde Boas (1975)*
|
321
|
+
- *Cormen, T. H., et al. (2009). Introduction to Algorithms (3rd ed.), Chapter 20*
|
322
|
+
|
323
|
+
---
|
324
|
+
|
325
|
+
## 🗒️ Changelog
|
326
|
+
|
327
|
+
See [CHANGELOG.md](./CHANGELOG.md) for version history.
|
data/ext/veb_tree/extconf.rb
CHANGED
@@ -7,34 +7,38 @@ unless find_executable("g++") || find_executable("clang++")
|
|
7
7
|
abort "C++ compiler not found. Please install a C++ compiler."
|
8
8
|
end
|
9
9
|
|
10
|
-
#
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
$CXXFLAGS << " -
|
18
|
-
|
19
|
-
# Debug flags if requested
|
20
|
-
if ENV["DEBUG"] == "1"
|
21
|
-
$CXXFLAGS << " -g -O0 -DDEBUG"
|
10
|
+
# Determine the C++ compiler
|
11
|
+
if RUBY_PLATFORM =~ /darwin/
|
12
|
+
RbConfig::MAKEFILE_CONFIG["CXX"] = "clang++"
|
13
|
+
RbConfig::MAKEFILE_CONFIG["LDSHAREDXX"] = "clang++ -dynamic -bundle"
|
14
|
+
|
15
|
+
if RUBY_VERSION < "3.0"
|
16
|
+
$CXXFLAGS << " -D_LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION"
|
17
|
+
$CXXFLAGS << " -Wno-error=implicit-function-declaration"
|
18
|
+
end
|
22
19
|
else
|
23
|
-
|
20
|
+
RbConfig::MAKEFILE_CONFIG["CXX"] = ENV["CXX"] || "g++"
|
24
21
|
end
|
25
22
|
|
23
|
+
# C++17 standard
|
24
|
+
$CXXFLAGS << " -std=c++17 -Wall -Wextra -O2"
|
25
|
+
|
26
26
|
# Platform-specific settings
|
27
27
|
case RUBY_PLATFORM
|
28
28
|
when /darwin/
|
29
|
-
# macOS specific flags
|
30
29
|
$CXXFLAGS << " -stdlib=libc++"
|
30
|
+
$LDFLAGS << " -stdlib=libc++"
|
31
31
|
when /linux/
|
32
|
-
# Linux specific flags
|
33
32
|
$LDFLAGS << " -lstdc++"
|
34
33
|
when /mingw|mswin/
|
35
|
-
# Windows specific flags
|
36
34
|
$CXXFLAGS << " -static-libgcc -static-libstdc++"
|
37
35
|
end
|
38
36
|
|
39
|
-
#
|
37
|
+
# Debug flags if requested
|
38
|
+
if ENV["DEBUG"] == "1"
|
39
|
+
$CXXFLAGS << " -g -O0 -DDEBUG"
|
40
|
+
else
|
41
|
+
$CXXFLAGS << " -DNDEBUG"
|
42
|
+
end
|
43
|
+
|
40
44
|
create_makefile("veb_tree/veb_tree")
|
@@ -1,6 +1,30 @@
|
|
1
|
-
#include
|
1
|
+
#include <cstdint>
|
2
|
+
#include <memory>
|
3
|
+
#include <stdexcept>
|
4
|
+
#include <cmath>
|
5
|
+
#include <vector>
|
6
|
+
#include <limits>
|
2
7
|
#include <algorithm>
|
3
8
|
|
9
|
+
#include <ruby.h>
|
10
|
+
|
11
|
+
#if defined(__APPLE__) && defined(RUBY_API_VERSION_CODE)
|
12
|
+
#if RUBY_API_VERSION_CODE < 30000
|
13
|
+
// Ruby 2.7's missing.h pollutes the global namespace
|
14
|
+
#ifdef finite
|
15
|
+
#undef finite
|
16
|
+
#endif
|
17
|
+
#ifdef isnan
|
18
|
+
#undef isnan
|
19
|
+
#endif
|
20
|
+
#ifdef isinf
|
21
|
+
#undef isinf
|
22
|
+
#endif
|
23
|
+
#endif
|
24
|
+
#endif
|
25
|
+
|
26
|
+
#include "veb_tree_ext.h"
|
27
|
+
|
4
28
|
namespace VebTree {
|
5
29
|
|
6
30
|
// ============================================================================
|
@@ -326,6 +350,9 @@ std::vector<uint64_t> VEBTree::to_vector() const {
|
|
326
350
|
// Ruby Wrapper Implementation
|
327
351
|
// ============================================================================
|
328
352
|
|
353
|
+
// Ruby 2.x vs 3.x compatibility
|
354
|
+
#if RUBY_API_VERSION_CODE >= 30000
|
355
|
+
// Ruby 3.0+
|
329
356
|
static const rb_data_type_t veb_tree_type = {
|
330
357
|
"VebTree::Tree",
|
331
358
|
{
|
@@ -338,6 +365,24 @@ static const rb_data_type_t veb_tree_type = {
|
|
338
365
|
nullptr, // data
|
339
366
|
RUBY_TYPED_FREE_IMMEDIATELY
|
340
367
|
};
|
368
|
+
#else
|
369
|
+
// Ruby 2.7
|
370
|
+
static void veb_tree_free(void* ptr) {
|
371
|
+
delete static_cast<VEBTree*>(ptr);
|
372
|
+
}
|
373
|
+
|
374
|
+
static const rb_data_type_t veb_tree_type = {
|
375
|
+
"VebTree::Tree",
|
376
|
+
{
|
377
|
+
nullptr, // dmark
|
378
|
+
veb_tree_free, // dfree
|
379
|
+
nullptr, // dsize
|
380
|
+
},
|
381
|
+
nullptr, // parent
|
382
|
+
nullptr, // data
|
383
|
+
RUBY_TYPED_FREE_IMMEDIATELY
|
384
|
+
};
|
385
|
+
#endif
|
341
386
|
|
342
387
|
VEBTree* TreeWrapper::get_tree(VALUE self) {
|
343
388
|
VEBTree* tree;
|
data/lib/veb_tree/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: veb_tree
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- pixelcaliber
|
@@ -58,7 +58,6 @@ files:
|
|
58
58
|
- ext/veb_tree/veb_tree_ext.cpp
|
59
59
|
- ext/veb_tree/veb_tree_ext.h
|
60
60
|
- lib/veb_tree.rb
|
61
|
-
- lib/veb_tree/pure_ruby.rb
|
62
61
|
- lib/veb_tree/version.rb
|
63
62
|
homepage: https://github.com/abhinvv1/Van-Emde-Boas-tree
|
64
63
|
licenses:
|
data/lib/veb_tree/pure_ruby.rb
DELETED
@@ -1,277 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'set'
|
4
|
-
|
5
|
-
module VebTree
|
6
|
-
class PureRuby
|
7
|
-
attr_reader :universe_size, :size
|
8
|
-
|
9
|
-
NIL_VALUE = -1
|
10
|
-
|
11
|
-
def initialize(universe_size)
|
12
|
-
raise ArgumentError, "Universe size must be positive" if universe_size <= 0
|
13
|
-
|
14
|
-
@universe_size = next_power_of_2(universe_size)
|
15
|
-
warn "Universe size #{universe_size} rounded up to #{@universe_size}" if @universe_size != universe_size
|
16
|
-
|
17
|
-
@size = 0
|
18
|
-
@min = NIL_VALUE
|
19
|
-
@max = NIL_VALUE
|
20
|
-
|
21
|
-
# Base case
|
22
|
-
if @universe_size <= 2
|
23
|
-
@base_case = true
|
24
|
-
return
|
25
|
-
end
|
26
|
-
|
27
|
-
@base_case = false
|
28
|
-
|
29
|
-
# Calculate sqrt
|
30
|
-
log_u = Math.log2(@universe_size).to_i
|
31
|
-
@sqrt_size = 1 << (log_u / 2)
|
32
|
-
@num_clusters = @universe_size / @sqrt_size
|
33
|
-
|
34
|
-
# Lazy allocation
|
35
|
-
@clusters = Array.new(@num_clusters)
|
36
|
-
@summary = nil
|
37
|
-
end
|
38
|
-
|
39
|
-
def insert(key)
|
40
|
-
validate_key(key)
|
41
|
-
return false if include?(key)
|
42
|
-
|
43
|
-
# Empty tree
|
44
|
-
if @min == NIL_VALUE
|
45
|
-
@min = @max = key
|
46
|
-
@size += 1
|
47
|
-
return true
|
48
|
-
end
|
49
|
-
|
50
|
-
# Base case
|
51
|
-
if @base_case
|
52
|
-
@min = key if key < @min
|
53
|
-
@max = key if key > @max
|
54
|
-
@size += 1
|
55
|
-
return true
|
56
|
-
end
|
57
|
-
|
58
|
-
# Ensure key is not min
|
59
|
-
if key < @min
|
60
|
-
key, @min = @min, key
|
61
|
-
end
|
62
|
-
|
63
|
-
@max = key if key > @max
|
64
|
-
|
65
|
-
# Recursive insert
|
66
|
-
h = high(key)
|
67
|
-
l = low(key)
|
68
|
-
|
69
|
-
# Lazy create cluster
|
70
|
-
@clusters[h] ||= PureRuby.new(@sqrt_size)
|
71
|
-
|
72
|
-
# If cluster was empty, update summary
|
73
|
-
if @clusters[h].min == NIL_VALUE
|
74
|
-
@summary ||= PureRuby.new(@num_clusters)
|
75
|
-
@summary.insert(h)
|
76
|
-
@clusters[h].instance_variable_set(:@min, l)
|
77
|
-
@clusters[h].instance_variable_set(:@max, l)
|
78
|
-
@clusters[h].instance_variable_set(:@size, 1)
|
79
|
-
else
|
80
|
-
@clusters[h].insert(l)
|
81
|
-
end
|
82
|
-
|
83
|
-
@size += 1
|
84
|
-
true
|
85
|
-
end
|
86
|
-
|
87
|
-
def delete(key)
|
88
|
-
return false unless include?(key)
|
89
|
-
|
90
|
-
# Base case
|
91
|
-
if @base_case
|
92
|
-
if key == @min && key == @max
|
93
|
-
@min = @max = NIL_VALUE
|
94
|
-
elsif key == @min
|
95
|
-
@min = @max
|
96
|
-
else
|
97
|
-
@max = @min
|
98
|
-
end
|
99
|
-
@size -= 1
|
100
|
-
return true
|
101
|
-
end
|
102
|
-
|
103
|
-
# Only one element
|
104
|
-
if @size == 1
|
105
|
-
@min = @max = NIL_VALUE
|
106
|
-
@size = 0
|
107
|
-
return true
|
108
|
-
end
|
109
|
-
|
110
|
-
# Replace min with successor if deleting min
|
111
|
-
if key == @min
|
112
|
-
first_cluster = @summary.min
|
113
|
-
key = index(first_cluster, @clusters[first_cluster].min)
|
114
|
-
@min = key
|
115
|
-
end
|
116
|
-
|
117
|
-
# Recursive delete
|
118
|
-
h = high(key)
|
119
|
-
l = low(key)
|
120
|
-
|
121
|
-
@clusters[h].delete(l) if @clusters[h]
|
122
|
-
|
123
|
-
# If cluster is empty, remove from summary
|
124
|
-
if @clusters[h] && @clusters[h].min == NIL_VALUE
|
125
|
-
@summary.delete(h)
|
126
|
-
@clusters[h] = nil
|
127
|
-
|
128
|
-
# Update max if necessary
|
129
|
-
if key == @max
|
130
|
-
summary_max = @summary.max
|
131
|
-
if summary_max == NIL_VALUE
|
132
|
-
@max = @min
|
133
|
-
else
|
134
|
-
@max = index(summary_max, @clusters[summary_max].max)
|
135
|
-
end
|
136
|
-
end
|
137
|
-
elsif key == @max && @clusters[h]
|
138
|
-
@max = index(h, @clusters[h].max)
|
139
|
-
end
|
140
|
-
|
141
|
-
@size -= 1
|
142
|
-
true
|
143
|
-
end
|
144
|
-
|
145
|
-
def include?(key)
|
146
|
-
return false if key < 0 || key >= @universe_size
|
147
|
-
return true if key == @min || key == @max
|
148
|
-
return false if @base_case
|
149
|
-
|
150
|
-
h = high(key)
|
151
|
-
@clusters[h] && @clusters[h].include?(low(key))
|
152
|
-
end
|
153
|
-
alias member? include?
|
154
|
-
|
155
|
-
def min
|
156
|
-
@min == NIL_VALUE ? nil : @min
|
157
|
-
end
|
158
|
-
|
159
|
-
def max
|
160
|
-
@max == NIL_VALUE ? nil : @max
|
161
|
-
end
|
162
|
-
|
163
|
-
def successor(key)
|
164
|
-
return nil if @min == NIL_VALUE
|
165
|
-
|
166
|
-
# Base case
|
167
|
-
if @base_case
|
168
|
-
return @min if key < @min
|
169
|
-
return @max if key < @max
|
170
|
-
return nil
|
171
|
-
end
|
172
|
-
|
173
|
-
return @min if key < @min
|
174
|
-
|
175
|
-
h = high(key)
|
176
|
-
l = low(key)
|
177
|
-
|
178
|
-
# Check same cluster
|
179
|
-
if @clusters[h] && l < @clusters[h].max
|
180
|
-
offset = @clusters[h].successor(l)
|
181
|
-
return index(h, offset)
|
182
|
-
end
|
183
|
-
|
184
|
-
# Next cluster
|
185
|
-
succ_cluster = @summary.successor(h)
|
186
|
-
return nil if succ_cluster == NIL_VALUE
|
187
|
-
|
188
|
-
offset = @clusters[succ_cluster].min
|
189
|
-
index(succ_cluster, offset)
|
190
|
-
end
|
191
|
-
|
192
|
-
def predecessor(key)
|
193
|
-
return nil if @max == NIL_VALUE
|
194
|
-
|
195
|
-
# Base case
|
196
|
-
if @base_case
|
197
|
-
return @max if key > @max
|
198
|
-
return @min if key > @min
|
199
|
-
return nil
|
200
|
-
end
|
201
|
-
|
202
|
-
return @max if key > @max
|
203
|
-
|
204
|
-
h = high(key)
|
205
|
-
l = low(key)
|
206
|
-
|
207
|
-
# Check same cluster
|
208
|
-
if @clusters[h] && l > @clusters[h].min
|
209
|
-
offset = @clusters[h].predecessor(l)
|
210
|
-
return index(h, offset)
|
211
|
-
end
|
212
|
-
|
213
|
-
# Previous cluster
|
214
|
-
pred_cluster = @summary.predecessor(h)
|
215
|
-
return @min if pred_cluster == NIL_VALUE && key > @min
|
216
|
-
return nil if pred_cluster == NIL_VALUE
|
217
|
-
|
218
|
-
offset = @clusters[pred_cluster].max
|
219
|
-
index(pred_cluster, offset)
|
220
|
-
end
|
221
|
-
|
222
|
-
def empty?
|
223
|
-
@size == 0
|
224
|
-
end
|
225
|
-
|
226
|
-
def clear
|
227
|
-
@min = @max = NIL_VALUE
|
228
|
-
@size = 0
|
229
|
-
unless @base_case
|
230
|
-
@summary&.clear
|
231
|
-
@clusters.fill(nil)
|
232
|
-
end
|
233
|
-
self
|
234
|
-
end
|
235
|
-
|
236
|
-
def each
|
237
|
-
return enum_for(:each) unless block_given?
|
238
|
-
|
239
|
-
current = @min
|
240
|
-
while current && current != NIL_VALUE
|
241
|
-
yield current
|
242
|
-
break if current == @max
|
243
|
-
current = successor(current)
|
244
|
-
end
|
245
|
-
|
246
|
-
self
|
247
|
-
end
|
248
|
-
|
249
|
-
def to_a
|
250
|
-
each.to_a
|
251
|
-
end
|
252
|
-
|
253
|
-
private
|
254
|
-
|
255
|
-
def high(x)
|
256
|
-
x / @sqrt_size
|
257
|
-
end
|
258
|
-
|
259
|
-
def low(x)
|
260
|
-
x % @sqrt_size
|
261
|
-
end
|
262
|
-
|
263
|
-
def index(high, low)
|
264
|
-
high * @sqrt_size + low
|
265
|
-
end
|
266
|
-
|
267
|
-
def validate_key(key)
|
268
|
-
raise ArgumentError, "Key must be non-negative" if key < 0
|
269
|
-
raise ArgumentError, "Key #{key} exceeds universe size #{@universe_size}" if key >= @universe_size
|
270
|
-
end
|
271
|
-
|
272
|
-
def next_power_of_2(n)
|
273
|
-
return 1 if n <= 1
|
274
|
-
2 ** (Math.log2(n).ceil)
|
275
|
-
end
|
276
|
-
end
|
277
|
-
end
|