algorithms 0.3.0-jruby

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.
Files changed (44) hide show
  1. data/History.txt +172 -0
  2. data/Manifest +43 -0
  3. data/README.markdown +93 -0
  4. data/Rakefile +31 -0
  5. data/algorithms.gemspec +33 -0
  6. data/benchmarks/deque.rb +17 -0
  7. data/benchmarks/sorts.rb +34 -0
  8. data/benchmarks/treemaps.rb +51 -0
  9. data/ext/containers/deque/deque.c +247 -0
  10. data/ext/containers/deque/extconf.rb +4 -0
  11. data/ext/containers/rbtree_map/extconf.rb +4 -0
  12. data/ext/containers/rbtree_map/rbtree.c +498 -0
  13. data/ext/containers/splaytree_map/extconf.rb +4 -0
  14. data/ext/containers/splaytree_map/splaytree.c +419 -0
  15. data/lib/algorithms.rb +68 -0
  16. data/lib/algorithms/search.rb +84 -0
  17. data/lib/algorithms/sort.rb +238 -0
  18. data/lib/containers/deque.rb +171 -0
  19. data/lib/containers/heap.rb +486 -0
  20. data/lib/containers/kd_tree.rb +110 -0
  21. data/lib/containers/priority_queue.rb +113 -0
  22. data/lib/containers/queue.rb +68 -0
  23. data/lib/containers/rb_tree_map.rb +398 -0
  24. data/lib/containers/splay_tree_map.rb +269 -0
  25. data/lib/containers/stack.rb +67 -0
  26. data/lib/containers/suffix_array.rb +68 -0
  27. data/lib/containers/trie.rb +182 -0
  28. data/spec/deque_gc_mark_spec.rb +18 -0
  29. data/spec/deque_spec.rb +108 -0
  30. data/spec/heap_spec.rb +126 -0
  31. data/spec/kd_expected_out.txt +10000 -0
  32. data/spec/kd_test_in.txt +10000 -0
  33. data/spec/kd_tree_spec.rb +34 -0
  34. data/spec/map_gc_mark_spec.rb +27 -0
  35. data/spec/priority_queue_spec.rb +75 -0
  36. data/spec/queue_spec.rb +61 -0
  37. data/spec/rb_tree_map_spec.rb +123 -0
  38. data/spec/search_spec.rb +28 -0
  39. data/spec/sort_spec.rb +28 -0
  40. data/spec/splay_tree_map_spec.rb +106 -0
  41. data/spec/stack_spec.rb +60 -0
  42. data/spec/suffix_array_spec.rb +40 -0
  43. data/spec/trie_spec.rb +59 -0
  44. metadata +122 -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