grosser-algorithms 0.4.0

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