amorim-algorithms 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.markdown +193 -0
  3. data/Gemfile +9 -0
  4. data/Manifest +51 -0
  5. data/README.markdown +87 -0
  6. data/Rakefile +22 -0
  7. data/algorithms.gemspec +23 -0
  8. data/benchmarks/deque.rb +17 -0
  9. data/benchmarks/sorts.rb +34 -0
  10. data/benchmarks/treemaps.rb +51 -0
  11. data/ext/algorithms/string/extconf.rb +4 -0
  12. data/ext/algorithms/string/string.c +68 -0
  13. data/ext/containers/bst/bst.c +247 -0
  14. data/ext/containers/bst/extconf.rb +4 -0
  15. data/ext/containers/deque/deque.c +247 -0
  16. data/ext/containers/deque/extconf.rb +4 -0
  17. data/ext/containers/rbtree_map/extconf.rb +4 -0
  18. data/ext/containers/rbtree_map/rbtree.c +498 -0
  19. data/ext/containers/splaytree_map/extconf.rb +4 -0
  20. data/ext/containers/splaytree_map/splaytree.c +419 -0
  21. data/lib/algorithms.rb +66 -0
  22. data/lib/algorithms/search.rb +84 -0
  23. data/lib/algorithms/sort.rb +368 -0
  24. data/lib/algorithms/string.rb +9 -0
  25. data/lib/containers/deque.rb +171 -0
  26. data/lib/containers/heap.rb +499 -0
  27. data/lib/containers/kd_tree.rb +110 -0
  28. data/lib/containers/priority_queue.rb +113 -0
  29. data/lib/containers/queue.rb +68 -0
  30. data/lib/containers/rb_tree_map.rb +398 -0
  31. data/lib/containers/splay_tree_map.rb +269 -0
  32. data/lib/containers/stack.rb +67 -0
  33. data/lib/containers/suffix_array.rb +68 -0
  34. data/lib/containers/trie.rb +182 -0
  35. data/spec/bst_gc_mark_spec.rb +25 -0
  36. data/spec/bst_spec.rb +25 -0
  37. data/spec/deque_gc_mark_spec.rb +18 -0
  38. data/spec/deque_spec.rb +108 -0
  39. data/spec/heap_spec.rb +131 -0
  40. data/spec/kd_expected_out.txt +10000 -0
  41. data/spec/kd_test_in.txt +10000 -0
  42. data/spec/kd_tree_spec.rb +34 -0
  43. data/spec/map_gc_mark_spec.rb +29 -0
  44. data/spec/priority_queue_spec.rb +75 -0
  45. data/spec/queue_spec.rb +61 -0
  46. data/spec/rb_tree_map_spec.rb +123 -0
  47. data/spec/search_spec.rb +28 -0
  48. data/spec/sort_spec.rb +29 -0
  49. data/spec/splay_tree_map_spec.rb +106 -0
  50. data/spec/stack_spec.rb +60 -0
  51. data/spec/string_spec.rb +15 -0
  52. data/spec/suffix_array_spec.rb +40 -0
  53. data/spec/trie_spec.rb +59 -0
  54. metadata +108 -0
@@ -0,0 +1,110 @@
1
+ =begin rdoc
2
+
3
+ A kd-tree is a binary tree that allows one to store points (of any space dimension: 2D, 3D, etc).
4
+ The structure of the resulting tree makes it so that large portions of the tree are pruned
5
+ during queries.
6
+
7
+ One very good use of the tree is to allow nearest neighbor searching. Let's say you have a number
8
+ of points in 2D space, and you want to find the nearest 2 points from a specific point:
9
+
10
+ First, put the points into the tree:
11
+
12
+ kdtree = Containers::KDTree.new( {0 => [4, 3], 1 => [3, 4], 2 => [-1, 2], 3 => [6, 4],
13
+ 4 => [3, -5], 5 => [-2, -5] })
14
+
15
+ Then, query on the tree:
16
+
17
+ puts kd.find_nearest([0, 0], 2) => [[5, 2], [9, 1]]
18
+
19
+ The result is an array of [distance, id] pairs. There seems to be a bug in this version.
20
+
21
+ Note that the point queried on does not have to exist in the tree. However, if it does exist,
22
+ it will be returned.
23
+
24
+ =end
25
+
26
+ class Containers::KDTree
27
+ Node = Struct.new(:id, :coords, :left, :right)
28
+
29
+ # Points is a hash of id => [coord, coord] pairs.
30
+ def initialize(points)
31
+ raise "must pass in a hash" unless points.kind_of?(Hash)
32
+ @dimensions = points[ points.keys.first ].size
33
+ @root = build_tree(points.to_a)
34
+ @nearest = []
35
+ end
36
+
37
+ # Find k closest points to given coordinates
38
+ def find_nearest(target, k_nearest)
39
+ @nearest = []
40
+ nearest(@root, target, k_nearest, 0)
41
+ end
42
+
43
+ # points is an array
44
+ def build_tree(points, depth=0)
45
+ return if points.empty?
46
+
47
+ axis = depth % @dimensions
48
+
49
+ points.sort! { |a, b| a.last[axis] <=> b.last[axis] }
50
+ median = points.size / 2
51
+
52
+ node = Node.new(points[median].first, points[median].last, nil, nil)
53
+ node.left = build_tree(points[0...median], depth+1)
54
+ node.right = build_tree(points[median+1..-1], depth+1)
55
+ node
56
+ end
57
+ private :build_tree
58
+
59
+ # Euclidian distanced, squared, between a node and target coords
60
+ def distance2(node, target)
61
+ return nil if node.nil? or target.nil?
62
+ c = (node.coords[0] - target[0])
63
+ d = (node.coords[1] - target[1])
64
+ c * c + d * d
65
+ end
66
+ private :distance2
67
+
68
+ # Update array of nearest elements if necessary
69
+ def check_nearest(nearest, node, target, k_nearest)
70
+ d = distance2(node, target)
71
+ if nearest.size < k_nearest || d < nearest.last[0]
72
+ nearest.pop if nearest.size >= k_nearest
73
+ nearest << [d, node.id]
74
+ nearest.sort! { |a, b| a[0] <=> b[0] }
75
+ end
76
+ nearest
77
+ end
78
+ private :check_nearest
79
+
80
+ # Recursively find nearest coordinates, going down the appropriate branch as needed
81
+ def nearest(node, target, k_nearest, depth)
82
+ axis = depth % @dimensions
83
+
84
+ if node.left.nil? && node.right.nil? # Leaf node
85
+ @nearest = check_nearest(@nearest, node, target, k_nearest)
86
+ return
87
+ end
88
+
89
+ # Go down the nearest split
90
+ if node.right.nil? || (node.left && target[axis] <= node.coords[axis])
91
+ nearer = node.left
92
+ further = node.right
93
+ else
94
+ nearer = node.right
95
+ further = node.left
96
+ end
97
+ nearest(nearer, target, k_nearest, depth+1)
98
+
99
+ # See if we have to check other side
100
+ if further
101
+ if @nearest.size < k_nearest || (target[axis] - node.coords[axis])**2 < @nearest.last[0]
102
+ nearest(further, target, k_nearest, depth+1)
103
+ end
104
+ end
105
+
106
+ @nearest = check_nearest(@nearest, node, target, k_nearest)
107
+ end
108
+ private :nearest
109
+
110
+ end
@@ -0,0 +1,113 @@
1
+ require 'containers/heap'
2
+
3
+ =begin rdoc
4
+ A Priority Queue is a data structure that behaves like a queue except that elements have an
5
+ associated priority. The #next and #pop methods return the item with the next highest priority.
6
+
7
+ Priority Queues are often used in graph problems, such as Dijkstra's Algorithm for shortest
8
+ path, and the A* search algorithm for shortest path.
9
+
10
+ This container is implemented using the Fibonacci heap included in the Collections library.
11
+ =end
12
+ class Containers::PriorityQueue
13
+ include Enumerable
14
+
15
+ # Create a new, empty PriorityQueue
16
+ def initialize(&block)
17
+ # We default to a priority queue that returns the largest value
18
+ block ||= lambda { |x, y| (x <=> y) == 1 }
19
+ @heap = Containers::Heap.new(&block)
20
+ end
21
+
22
+ # Returns the number of elements in the queue.
23
+ #
24
+ # q = Containers::PriorityQueue.new
25
+ # q.size #=> 0
26
+ # q.push("Alaska", 1)
27
+ # q.size #=> 1
28
+ def size
29
+ @heap.size
30
+ end
31
+ alias_method :length, :size
32
+
33
+ # Add an object to the queue with associated priority.
34
+ #
35
+ # q = Containers::PriorityQueue.new
36
+ # q.push("Alaska", 1)
37
+ # q.pop #=> "Alaska"
38
+ def push(object, priority)
39
+ @heap.push(priority, object)
40
+ end
41
+
42
+ # Clears all the items in the queue.
43
+ def clear
44
+ @heap.clear
45
+ end
46
+
47
+ # Returns true if the queue is empty, false otherwise.
48
+ def empty?
49
+ @heap.empty?
50
+ end
51
+
52
+ # call-seq:
53
+ # has_priority? priority -> boolean
54
+ #
55
+ # Return true if the priority is in the queue, false otherwise.
56
+ #
57
+ # q = PriorityQueue.new
58
+ # q.push("Alaska", 1)
59
+ #
60
+ # q.has_priority?(1) #=> true
61
+ # q.has_priority?(2) #=> false
62
+ def has_priority?(priority)
63
+ @heap.has_key?(priority)
64
+ end
65
+
66
+ # call-seq:
67
+ # next -> object
68
+ #
69
+ # Return the object with the next highest priority, but does not remove it
70
+ #
71
+ # q = Containers::PriorityQueue.new
72
+ # q.push("Alaska", 50)
73
+ # q.push("Delaware", 30)
74
+ # q.push("Georgia", 35)
75
+ # q.next #=> "Alaska"
76
+ def next
77
+ @heap.next
78
+ end
79
+
80
+ # call-seq:
81
+ # pop -> object
82
+ #
83
+ # Return the object with the next highest priority and removes it from the queue
84
+ #
85
+ # q = Containers::PriorityQueue.new
86
+ # q.push("Alaska", 50)
87
+ # q.push("Delaware", 30)
88
+ # q.push("Georgia", 35)
89
+ # q.pop #=> "Alaska"
90
+ # q.size #=> 2
91
+ def pop
92
+ @heap.pop
93
+ end
94
+ alias_method :next!, :pop
95
+
96
+ # call-seq:
97
+ # delete(priority) -> object
98
+ # delete(priority) -> nil
99
+ #
100
+ # Delete an object with specified priority from the queue. If there are duplicates, an
101
+ # arbitrary object with that priority is deleted and returned. Returns nil if there are
102
+ # no objects with the priority.
103
+ #
104
+ # q = PriorityQueue.new
105
+ # q.push("Alaska", 50)
106
+ # q.push("Delaware", 30)
107
+ # q.delete(50) #=> "Alaska"
108
+ # q.delete(10) #=> nil
109
+ def delete(priority)
110
+ @heap.delete(priority)
111
+ end
112
+
113
+ end
@@ -0,0 +1,68 @@
1
+ require 'containers/deque'
2
+
3
+ =begin rdoc
4
+ A Queue is a container that keeps elements in a first-in first-out (FIFO) order. Because of its
5
+ properties, it is often used as a buffer.
6
+
7
+ This implementation uses a doubly-linked list, guaranteeing O(1) complexity for all operations.
8
+
9
+ =end
10
+ class Containers::Queue
11
+ include Enumerable
12
+ # Create a new queue. Takes an optional array argument to initialize the queue.
13
+ #
14
+ # q = Containers::Queue.new([1, 2, 3])
15
+ # q.pop #=> 1
16
+ # q.pop #=> 2
17
+ def initialize(ary=[])
18
+ @container = Containers::Deque.new(ary)
19
+ end
20
+
21
+ # Returns the next item from the queue but does not remove it.
22
+ #
23
+ # q = Containers::Queue.new([1, 2, 3])
24
+ # q.next #=> 1
25
+ # q.size #=> 3
26
+ def next
27
+ @container.front
28
+ end
29
+
30
+ # Adds an item to the queue.
31
+ #
32
+ # q = Containers::Queue.new([1])
33
+ # q.push(2)
34
+ # q.pop #=> 1
35
+ # q.pop #=> 2
36
+ def push(obj)
37
+ @container.push_back(obj)
38
+ end
39
+ alias_method :<<, :push
40
+
41
+ # Removes the next item from the queue and returns it.
42
+ #
43
+ # q = Containers::Queue.new([1, 2, 3])
44
+ # q.pop #=> 1
45
+ # q.size #=> 2
46
+ def pop
47
+ @container.pop_front
48
+ end
49
+
50
+ # Return the number of items in the queue.
51
+ #
52
+ # q = Containers::Queue.new([1, 2, 3])
53
+ # q.size #=> 3
54
+ def size
55
+ @container.size
56
+ end
57
+
58
+ # Returns true if the queue is empty, false otherwise.
59
+ def empty?
60
+ @container.empty?
61
+ end
62
+
63
+ # Iterate over the Queue in FIFO order.
64
+ def each(&block)
65
+ @container.each_forward(&block)
66
+ end
67
+
68
+ end
@@ -0,0 +1,398 @@
1
+ require 'containers/stack'
2
+ =begin rdoc
3
+ A RBTreeMap is a map that is stored in sorted order based on the order of its keys. This ordering is
4
+ determined by applying the function <=> to compare the keys. No duplicate values for keys are allowed,
5
+ so duplicate values are overwritten.
6
+
7
+ A major advantage of RBTreeMap over a Hash is the fact that keys are stored in order and can thus be
8
+ iterated over in order. This is useful for many datasets.
9
+
10
+ The implementation is adapted from Robert Sedgewick's Left Leaning Red-Black Tree implementation,
11
+ which can be found at http://www.cs.princeton.edu/~rs/talks/LLRB/Java/RedBlackBST.java
12
+
13
+ Containers::RBTreeMap automatically uses the faster C implementation if it was built
14
+ when the gem was installed. Alternatively, Containers::RubyRBTreeMap and Containers::CRBTreeMap can be
15
+ explicitly used as well; their functionality is identical.
16
+
17
+ Most methods have O(log n) complexity.
18
+
19
+ =end
20
+ class Containers::RubyRBTreeMap
21
+ include Enumerable
22
+
23
+ attr_accessor :height_black
24
+
25
+ # Create and initialize a new empty TreeMap.
26
+ def initialize
27
+ @root = nil
28
+ @height_black = 0
29
+ end
30
+
31
+ # Insert an item with an associated key into the TreeMap, and returns the item inserted
32
+ #
33
+ # Complexity: O(log n)
34
+ #
35
+ # map = Containers::TreeMap.new
36
+ # map.push("MA", "Massachusetts") #=> "Massachusetts"
37
+ # map.get("MA") #=> "Massachusetts"
38
+ def push(key, value)
39
+ @root = insert(@root, key, value)
40
+ @height_black += 1 if isred(@root)
41
+ @root.color = :black
42
+ value
43
+ end
44
+ alias_method :[]=, :push
45
+
46
+ # Return the number of items in the TreeMap.
47
+ #
48
+ # map = Containers::TreeMap.new
49
+ # map.push("MA", "Massachusetts")
50
+ # map.push("GA", "Georgia")
51
+ # map.size #=> 2
52
+ def size
53
+ @root and @root.size or 0
54
+ end
55
+
56
+ # Return the height of the tree structure in the TreeMap.
57
+ #
58
+ # Complexity: O(1)
59
+ #
60
+ # map = Containers::TreeMap.new
61
+ # map.push("MA", "Massachusetts")
62
+ # map.push("GA", "Georgia")
63
+ # map.height #=> 2
64
+ def height
65
+ @root and @root.height or 0
66
+ end
67
+
68
+ # Return true if key is found in the TreeMap, false otherwise
69
+ #
70
+ # Complexity: O(log n)
71
+ #
72
+ # map = Containers::TreeMap.new
73
+ # map.push("MA", "Massachusetts")
74
+ # map.push("GA", "Georgia")
75
+ # map.has_key?("GA") #=> true
76
+ # map.has_key?("DE") #=> false
77
+ def has_key?(key)
78
+ !get(key).nil?
79
+ end
80
+
81
+ # Return the item associated with the key, or nil if none found.
82
+ #
83
+ # Complexity: O(log n)
84
+ #
85
+ # map = Containers::TreeMap.new
86
+ # map.push("MA", "Massachusetts")
87
+ # map.push("GA", "Georgia")
88
+ # map.get("GA") #=> "Georgia"
89
+ def get(key)
90
+ get_recursive(@root, key)
91
+ end
92
+ alias_method :[], :get
93
+
94
+ # Return the smallest key in the map.
95
+ #
96
+ # Complexity: O(log n)
97
+ #
98
+ # map = Containers::TreeMap.new
99
+ # map.push("MA", "Massachusetts")
100
+ # map.push("GA", "Georgia")
101
+ # map.min_key #=> "GA"
102
+ def min_key
103
+ @root.nil? ? nil : min_recursive(@root)
104
+ end
105
+
106
+ # Return the largest key in the map.
107
+ #
108
+ # Complexity: O(log n)
109
+ #
110
+ # map = Containers::TreeMap.new
111
+ # map.push("MA", "Massachusetts")
112
+ # map.push("GA", "Georgia")
113
+ # map.max_key #=> "MA"
114
+ def max_key
115
+ @root.nil? ? nil : max_recursive(@root)
116
+ end
117
+
118
+ # Deletes the item and key if it's found, and returns the item. Returns nil
119
+ # if key is not present.
120
+ #
121
+ # !!! Warning !!! There is a currently a bug in the delete method that occurs rarely
122
+ # but often enough, especially in large datasets. It is currently under investigation.
123
+ #
124
+ # Complexity: O(log n)
125
+ #
126
+ # map = Containers::TreeMap.new
127
+ # map.push("MA", "Massachusetts")
128
+ # map.push("GA", "Georgia")
129
+ # map.min_key #=> "GA"
130
+ def delete(key)
131
+ result = nil
132
+ if @root
133
+ @root, result = delete_recursive(@root, key)
134
+ @root.color = :black if @root
135
+ end
136
+ result
137
+ end
138
+
139
+ # Returns true if the tree is empty, false otherwise
140
+ def empty?
141
+ @root.nil?
142
+ end
143
+
144
+ # Deletes the item with the smallest key and returns the item. Returns nil
145
+ # if key is not present.
146
+ #
147
+ # Complexity: O(log n)
148
+ #
149
+ # map = Containers::TreeMap.new
150
+ # map.push("MA", "Massachusetts")
151
+ # map.push("GA", "Georgia")
152
+ # map.delete_min #=> "Massachusetts"
153
+ # map.size #=> 1
154
+ def delete_min
155
+ result = nil
156
+ if @root
157
+ @root, result = delete_min_recursive(@root)
158
+ @root.color = :black if @root
159
+ end
160
+ result
161
+ end
162
+
163
+ # Deletes the item with the smallest key and returns the item. Returns nil
164
+ # if key is not present.
165
+ #
166
+ # Complexity: O(log n)
167
+ #
168
+ # map = Containers::TreeMap.new
169
+ # map.push("MA", "Massachusetts")
170
+ # map.push("GA", "Georgia")
171
+ # map.delete_max #=> "Georgia"
172
+ # map.size #=> 1
173
+ def delete_max
174
+ result = nil
175
+ if @root
176
+ @root, result = delete_max_recursive(@root)
177
+ @root.color = :black if @root
178
+ end
179
+ result
180
+ end
181
+
182
+ # Iterates over the TreeMap from smallest to largest element. Iterative approach.
183
+ def each
184
+ return nil unless @root
185
+ stack = Containers::Stack.new
186
+ cursor = @root
187
+ loop do
188
+ if cursor
189
+ stack.push(cursor)
190
+ cursor = cursor.left
191
+ else
192
+ unless stack.empty?
193
+ cursor = stack.pop
194
+ yield(cursor.key, cursor.value)
195
+ cursor = cursor.right
196
+ else
197
+ break
198
+ end
199
+ end
200
+ end
201
+ end
202
+
203
+ class Node # :nodoc: all
204
+ attr_accessor :color, :key, :value, :left, :right, :size, :height
205
+ def initialize(key, value)
206
+ @key = key
207
+ @value = value
208
+ @color = :red
209
+ @left = nil
210
+ @right = nil
211
+ @size = 1
212
+ @height = 1
213
+ end
214
+
215
+ def red?
216
+ @color == :red
217
+ end
218
+
219
+ def colorflip
220
+ @color = @color == :red ? :black : :red
221
+ @left.color = @left.color == :red ? :black : :red
222
+ @right.color = @right.color == :red ? :black : :red
223
+ end
224
+
225
+ def update_size
226
+ @size = (@left ? @left.size : 0) + (@right ? @right.size : 0) + 1
227
+ left_height = (@left ? @left.height : 0)
228
+ right_height = (@right ? @right.height : 0)
229
+ if left_height > right_height
230
+ @height = left_height + 1
231
+ else
232
+ @height = right_height + 1
233
+ end
234
+ self
235
+ end
236
+
237
+ def rotate_left
238
+ r = @right
239
+ r_key, r_value, r_color = r.key, r.value, r.color
240
+ b = r.left
241
+ r.left = @left
242
+ @left = r
243
+ @right = r.right
244
+ r.right = b
245
+ r.color, r.key, r.value = :red, @key, @value
246
+ @key, @value = r_key, r_value
247
+ r.update_size
248
+ update_size
249
+ end
250
+
251
+ def rotate_right
252
+ l = @left
253
+ l_key, l_value, l_color = l.key, l.value, l.color
254
+ b = l.right
255
+ l.right = @right
256
+ @right = l
257
+ @left = l.left
258
+ l.left = b
259
+ l.color, l.key, l.value = :red, @key, @value
260
+ @key, @value = l_key, l_value
261
+ l.update_size
262
+ update_size
263
+ end
264
+
265
+ def move_red_left
266
+ colorflip
267
+ if (@right.left && @right.left.red?)
268
+ @right.rotate_right
269
+ rotate_left
270
+ colorflip
271
+ end
272
+ self
273
+ end
274
+
275
+ def move_red_right
276
+ colorflip
277
+ if (@left.left && @left.left.red?)
278
+ rotate_right
279
+ colorflip
280
+ end
281
+ self
282
+ end
283
+
284
+ def fixup
285
+ rotate_left if @right && @right.red?
286
+ rotate_right if (@left && @left.red?) && (@left.left && @left.left.red?)
287
+ colorflip if (@left && @left.red?) && (@right && @right.red?)
288
+
289
+ update_size
290
+ end
291
+ end
292
+
293
+ def delete_recursive(node, key)
294
+ if (key <=> node.key) == -1
295
+ node.move_red_left if ( !isred(node.left) && !isred(node.left.left) )
296
+ node.left, result = delete_recursive(node.left, key)
297
+ else
298
+ node.rotate_right if isred(node.left)
299
+ if ( ( (key <=> node.key) == 0) && node.right.nil? )
300
+ return nil, node.value
301
+ end
302
+ if ( !isred(node.right) && !isred(node.right.left) )
303
+ node.move_red_right
304
+ end
305
+ if (key <=> node.key) == 0
306
+ result = node.value
307
+ node.value = get_recursive(node.right, min_recursive(node.right))
308
+ node.key = min_recursive(node.right)
309
+ node.right = delete_min_recursive(node.right).first
310
+ else
311
+ node.right, result = delete_recursive(node.right, key)
312
+ end
313
+ end
314
+ return node.fixup, result
315
+ end
316
+ private :delete_recursive
317
+
318
+ def delete_min_recursive(node)
319
+ if node.left.nil?
320
+ return nil, node.value
321
+ end
322
+ if ( !isred(node.left) && !isred(node.left.left) )
323
+ node.move_red_left
324
+ end
325
+ node.left, result = delete_min_recursive(node.left)
326
+
327
+ return node.fixup, result
328
+ end
329
+ private :delete_min_recursive
330
+
331
+ def delete_max_recursive(node)
332
+ if (isred(node.left))
333
+ node = node.rotate_right
334
+ end
335
+ return nil, node.value if node.right.nil?
336
+ if ( !isred(node.right) && !isred(node.right.left) )
337
+ node.move_red_right
338
+ end
339
+ node.right, result = delete_max_recursive(node.right)
340
+
341
+ return node.fixup, result
342
+ end
343
+ private :delete_max_recursive
344
+
345
+ def get_recursive(node, key)
346
+ return nil if node.nil?
347
+ case key <=> node.key
348
+ when 0 then return node.value
349
+ when -1 then return get_recursive(node.left, key)
350
+ when 1 then return get_recursive(node.right, key)
351
+ end
352
+ end
353
+ private :get_recursive
354
+
355
+ def min_recursive(node)
356
+ return node.key if node.left.nil?
357
+
358
+ min_recursive(node.left)
359
+ end
360
+ private :min_recursive
361
+
362
+ def max_recursive(node)
363
+ return node.key if node.right.nil?
364
+
365
+ max_recursive(node.right)
366
+ end
367
+ private :max_recursive
368
+
369
+ def insert(node, key, value)
370
+ return Node.new(key, value) unless node
371
+
372
+ case key <=> node.key
373
+ when 0 then node.value = value
374
+ when -1 then node.left = insert(node.left, key, value)
375
+ when 1 then node.right = insert(node.right, key, value)
376
+ end
377
+
378
+ node.rotate_left if (node.right && node.right.red?)
379
+ node.rotate_right if (node.left && node.left.red? && node.left.left && node.left.left.red?)
380
+ node.colorflip if (node.left && node.left.red? && node.right && node.right.red?)
381
+ node.update_size
382
+ end
383
+ private :insert
384
+
385
+ def isred(node)
386
+ return false if node.nil?
387
+
388
+ node.color == :red
389
+ end
390
+ private :isred
391
+ end
392
+
393
+ begin
394
+ require 'CRBTreeMap'
395
+ Containers::RBTreeMap = Containers::CRBTreeMap
396
+ rescue LoadError # C Version could not be found, try ruby version
397
+ Containers::RBTreeMap = Containers::RubyRBTreeMap
398
+ end