amorim-algorithms 0.6.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.
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,9 @@
1
+ =begin rdoc
2
+ This module implements string algorithms. Documentation is provided for each algorithm.
3
+
4
+ =end
5
+
6
+ begin
7
+ require 'CString'
8
+ rescue LoadError
9
+ end
@@ -0,0 +1,171 @@
1
+ =begin rdoc
2
+ A Deque is a container that allows items to be added and removed from both the front and back,
3
+ acting as a combination of a Stack and Queue.
4
+
5
+ This implementation uses a doubly-linked list, guaranteeing O(1) complexity for all operations.
6
+ =end
7
+ class Containers::RubyDeque
8
+ include Enumerable
9
+
10
+ Node = Struct.new(:left, :right, :obj)
11
+
12
+ # Create a new Deque. Takes an optional array argument to initialize the Deque.
13
+ #
14
+ # d = Containers::Deque.new([1, 2, 3])
15
+ # d.front #=> 1
16
+ # d.back #=> 3
17
+ def initialize(ary=[])
18
+ @front = nil
19
+ @back = nil
20
+ @size = 0
21
+ ary.to_a.each { |obj| push_back(obj) }
22
+ end
23
+
24
+ # Returns true if the Deque is empty, false otherwise.
25
+ def empty?
26
+ @size == 0
27
+ end
28
+
29
+ # Removes all the objects in the Deque.
30
+ def clear
31
+ @front = @back = nil
32
+ @size = 0
33
+ end
34
+
35
+ # Return the number of items in the Deque.
36
+ #
37
+ # d = Containers::Deque.new([1, 2, 3])
38
+ # d.size #=> 3
39
+ def size
40
+ @size
41
+ end
42
+ alias_method :length, :size
43
+
44
+ # Returns the object at the front of the Deque but does not remove it.
45
+ #
46
+ # d = Containers::Deque.new
47
+ # d.push_front(1)
48
+ # d.push_front(2)
49
+ # d.front #=> 2
50
+ def front
51
+ @front && @front.obj
52
+ end
53
+
54
+ # Returns the object at the back of the Deque but does not remove it.
55
+ #
56
+ # d = Containers::Deque.new
57
+ # d.push_front(1)
58
+ # d.push_front(2)
59
+ # d.back #=> 1
60
+ def back
61
+ @back && @back.obj
62
+ end
63
+
64
+ # Adds an object at the front of the Deque.
65
+ #
66
+ # d = Containers::Deque.new([1, 2, 3])
67
+ # d.push_front(0)
68
+ # d.pop_front #=> 0
69
+ def push_front(obj)
70
+ node = Node.new(nil, nil, obj)
71
+ if @front
72
+ node.right = @front
73
+ @front.left = node
74
+ @front = node
75
+ else
76
+ @front = @back = node
77
+ end
78
+ @size += 1
79
+ obj
80
+ end
81
+
82
+ # Adds an object at the back of the Deque.
83
+ #
84
+ # d = Containers::Deque.new([1, 2, 3])
85
+ # d.push_back(4)
86
+ # d.pop_back #=> 4
87
+ def push_back(obj)
88
+ node = Node.new(nil, nil, obj)
89
+ if @back
90
+ node.left = @back
91
+ @back.right = node
92
+ @back = node
93
+ else
94
+ @front = @back = node
95
+ end
96
+ @size += 1
97
+ obj
98
+ end
99
+
100
+ # Returns the object at the front of the Deque and removes it.
101
+ #
102
+ # d = Containers::Deque.new
103
+ # d.push_front(1)
104
+ # d.push_front(2)
105
+ # d.pop_front #=> 2
106
+ # d.size #=> 1
107
+ def pop_front
108
+ return nil unless @front
109
+ node = @front
110
+ if @size == 1
111
+ clear
112
+ return node.obj
113
+ else
114
+ @front.right.left = nil
115
+ @front = @front.right
116
+ end
117
+ @size -= 1
118
+ node.obj
119
+ end
120
+
121
+ # Returns the object at the back of the Deque and removes it.
122
+ #
123
+ # d = Containers::Deque.new
124
+ # d.push_front(1)
125
+ # d.push_front(2)
126
+ # d.pop_back #=> 1
127
+ # d.size #=> 1
128
+ def pop_back
129
+ return nil unless @back
130
+ node = @back
131
+ if @size == 1
132
+ clear
133
+ return node.obj
134
+ else
135
+ @back.left.right = nil
136
+ @back = @back.left
137
+ end
138
+ @size -= 1
139
+ node.obj
140
+ end
141
+
142
+ # Iterate over the Deque in FIFO order.
143
+ def each_forward
144
+ return unless @front
145
+ node = @front
146
+ while node
147
+ yield node.obj
148
+ node = node.right
149
+ end
150
+ end
151
+ alias_method :each, :each_forward
152
+
153
+ # Iterate over the Deque in LIFO order.
154
+ def each_backward
155
+ return unless @back
156
+ node = @back
157
+ while node
158
+ yield node.obj
159
+ node = node.left
160
+ end
161
+ end
162
+ alias_method :reverse_each, :each_backward
163
+
164
+ end
165
+
166
+ begin
167
+ require 'CDeque'
168
+ Containers::Deque = Containers::CDeque
169
+ rescue LoadError # C Version could not be found, try ruby version
170
+ Containers::Deque = Containers::RubyDeque
171
+ end
@@ -0,0 +1,499 @@
1
+ =begin rdoc
2
+ A Heap is a container that satisfies the heap property that nodes are always smaller in
3
+ value than their parent node.
4
+
5
+ The Containers::Heap class is flexible and upon initialization, takes an optional block
6
+ that determines how the items are ordered. Two versions that are included are the
7
+ Containers::MaxHeap and Containers::MinHeap that return the largest and smallest items on
8
+ each invocation, respectively.
9
+
10
+ This library implements a Fibonacci heap, which allows O(1) complexity for most methods.
11
+ =end
12
+ class Containers::Heap
13
+ include Enumerable
14
+
15
+ # call-seq:
16
+ # size -> int
17
+ #
18
+ # Return the number of elements in the heap.
19
+ def size
20
+ @size
21
+ end
22
+ alias_method :length, :size
23
+
24
+ # call-seq:
25
+ # Heap.new(optional_array) { |x, y| optional_comparison_fn } -> new_heap
26
+ #
27
+ # If an optional array is passed, the entries in the array are inserted into the heap with
28
+ # equal key and value fields. Also, an optional block can be passed to define the function
29
+ # that maintains heap property. For example, a min-heap can be created with:
30
+ #
31
+ # minheap = Heap.new { |x, y| (x <=> y) == -1 }
32
+ # minheap.push(6)
33
+ # minheap.push(10)
34
+ # minheap.pop #=> 6
35
+ #
36
+ # Thus, smaller elements will be parent nodes. The heap defaults to a min-heap if no block
37
+ # is given.
38
+ def initialize(ary=[], &block)
39
+ @compare_fn = block || lambda { |x, y| (x <=> y) == -1 }
40
+ @next = nil
41
+ @size = 0
42
+ @stored = {}
43
+
44
+ ary.each { |n| push(n) } unless ary.empty?
45
+ end
46
+
47
+ # call-seq:
48
+ # push(key, value) -> value
49
+ # push(value) -> value
50
+ #
51
+ # Inserts an item with a given key into the heap. If only one parameter is given,
52
+ # the key is set to the value.
53
+ #
54
+ # Complexity: O(1)
55
+ #
56
+ # heap = MinHeap.new
57
+ # heap.push(1, "Cat")
58
+ # heap.push(2)
59
+ # heap.pop #=> "Cat"
60
+ # heap.pop #=> 2
61
+ def push(key, value=key)
62
+ raise ArgumentError, "Heap keys must not be nil." unless key
63
+ node = Node.new(key, value)
64
+ # Add new node to the left of the @next node
65
+ if @next
66
+ node.right = @next
67
+ node.left = @next.left
68
+ node.left.right = node
69
+ @next.left = node
70
+ if @compare_fn[key, @next.key]
71
+ @next = node
72
+ end
73
+ else
74
+ @next = node
75
+ end
76
+ @size += 1
77
+
78
+ arr = []
79
+ w = @next.right
80
+ until w == @next do
81
+ arr << w.value
82
+ w = w.right
83
+ end
84
+ arr << @next.value
85
+ @stored[key] ||= []
86
+ @stored[key] << node
87
+ value
88
+ end
89
+ alias_method :<<, :push
90
+
91
+ # call-seq:
92
+ # has_key?(key) -> true or false
93
+ #
94
+ # Returns true if heap contains the key.
95
+ #
96
+ # Complexity: O(1)
97
+ #
98
+ # minheap = MinHeap.new([1, 2])
99
+ # minheap.has_key?(2) #=> true
100
+ # minheap.has_key?(4) #=> false
101
+ def has_key?(key)
102
+ @stored[key] && !@stored[key].empty? ? true : false
103
+ end
104
+
105
+ # call-seq:
106
+ # next -> value
107
+ # next -> nil
108
+ #
109
+ # Returns the value of the next item in heap order, but does not remove it.
110
+ #
111
+ # Complexity: O(1)
112
+ #
113
+ # minheap = MinHeap.new([1, 2])
114
+ # minheap.next #=> 1
115
+ # minheap.size #=> 2
116
+ def next
117
+ @next && @next.value
118
+ end
119
+
120
+ # call-seq:
121
+ # next_key -> key
122
+ # next_key -> nil
123
+ #
124
+ # Returns the key associated with the next item in heap order, but does not remove the value.
125
+ #
126
+ # Complexity: O(1)
127
+ #
128
+ # minheap = MinHeap.new
129
+ # minheap.push(1, :a)
130
+ # minheap.next_key #=> 1
131
+ #
132
+ def next_key
133
+ @next && @next.key
134
+ end
135
+
136
+ # call-seq:
137
+ # clear -> nil
138
+ #
139
+ # Removes all elements from the heap, destructively.
140
+ #
141
+ # Complexity: O(1)
142
+ #
143
+ def clear
144
+ @next = nil
145
+ @size = 0
146
+ @stored = {}
147
+ nil
148
+ end
149
+
150
+ # call-seq:
151
+ # empty? -> true or false
152
+ #
153
+ # Returns true if the heap is empty, false otherwise.
154
+ def empty?
155
+ @next.nil?
156
+ end
157
+
158
+ # call-seq:
159
+ # merge!(otherheap) -> merged_heap
160
+ #
161
+ # Does a shallow merge of all the nodes in the other heap.
162
+ #
163
+ # Complexity: O(1)
164
+ #
165
+ # heap = MinHeap.new([5, 6, 7, 8])
166
+ # otherheap = MinHeap.new([1, 2, 3, 4])
167
+ # heap.merge!(otherheap)
168
+ # heap.size #=> 8
169
+ # heap.pop #=> 1
170
+ def merge!(otherheap)
171
+ raise ArgumentError, "Trying to merge a heap with something not a heap" unless otherheap.kind_of? Containers::Heap
172
+ other_root = otherheap.instance_variable_get("@next")
173
+ if other_root
174
+ @stored = @stored.merge(otherheap.instance_variable_get("@stored")) { |key, a, b| (a << b).flatten }
175
+ # Insert othernode's @next node to the left of current @next
176
+ @next.left.right = other_root
177
+ ol = other_root.left
178
+ other_root.left = @next.left
179
+ ol.right = @next
180
+ @next.left = ol
181
+
182
+ @next = other_root if @compare_fn[other_root.key, @next.key]
183
+ end
184
+ @size += otherheap.size
185
+ end
186
+
187
+ # call-seq:
188
+ # pop -> value
189
+ # pop -> nil
190
+ #
191
+ # Returns the value of the next item in heap order and removes it from the heap.
192
+ #
193
+ # Complexity: O(1)
194
+ #
195
+ # minheap = MinHeap.new([1, 2])
196
+ # minheap.pop #=> 1
197
+ # minheap.size #=> 1
198
+ def pop
199
+ return nil unless @next
200
+ popped = @next
201
+ if @size == 1
202
+ clear
203
+ return popped.value
204
+ end
205
+ # Merge the popped's children into root node
206
+ if @next.child
207
+ @next.child.parent = nil
208
+
209
+ # get rid of parent
210
+ sibling = @next.child.right
211
+ until sibling == @next.child
212
+ sibling.parent = nil
213
+ sibling = sibling.right
214
+ end
215
+
216
+ # Merge the children into the root. If @next is the only root node, make its child the @next node
217
+ if @next.right == @next
218
+ @next = @next.child
219
+ else
220
+ next_left, next_right = @next.left, @next.right
221
+ current_child = @next.child
222
+ @next.right.left = current_child
223
+ @next.left.right = current_child.right
224
+ current_child.right.left = next_left
225
+ current_child.right = next_right
226
+ @next = @next.right
227
+ end
228
+ else
229
+ @next.left.right = @next.right
230
+ @next.right.left = @next.left
231
+ @next = @next.right
232
+ end
233
+ consolidate
234
+
235
+ @size -= 1
236
+
237
+ popped.value
238
+ end
239
+ alias_method :next!, :pop
240
+
241
+ # call-seq:
242
+ # change_key(key, new_key) -> [new_key, value]
243
+ # change_key(key, new_key) -> nil
244
+ #
245
+ # Changes the key from one to another. Doing so must not violate the heap property or
246
+ # an exception will be raised. If the key is found, an array containing the new key and
247
+ # value pair is returned, otherwise nil is returned.
248
+ #
249
+ # In the case of duplicate keys, an arbitrary key is changed. This will be investigated
250
+ # more in the future.
251
+ #
252
+ # Complexity: amortized O(1)
253
+ #
254
+ # minheap = MinHeap.new([1, 2])
255
+ # minheap.change_key(2, 3) #=> raise error since we can't increase the value in a min-heap
256
+ # minheap.change_key(2, 0) #=> [0, 2]
257
+ # minheap.pop #=> 2
258
+ # minheap.pop #=> 1
259
+ def change_key(key, new_key, delete=false)
260
+ return if @stored[key].nil? || @stored[key].empty? || (key == new_key)
261
+
262
+ # Must maintain heap property
263
+ raise "Changing this key would not maintain heap property!" unless (delete || @compare_fn[new_key, key])
264
+ node = @stored[key].shift
265
+ if node
266
+ node.key = new_key
267
+ @stored[new_key] ||= []
268
+ @stored[new_key] << node
269
+ parent = node.parent
270
+ if parent
271
+ # if heap property is violated
272
+ if delete || @compare_fn[new_key, parent.key]
273
+ cut(node, parent)
274
+ cascading_cut(parent)
275
+ end
276
+ end
277
+ if delete || @compare_fn[node.key, @next.key]
278
+ @next = node
279
+ end
280
+ return [node.key, node.value]
281
+ end
282
+ nil
283
+ end
284
+
285
+ # call-seq:
286
+ # delete(key) -> value
287
+ # delete(key) -> nil
288
+ #
289
+ # Deletes the item with associated key and returns it. nil is returned if the key
290
+ # is not found. In the case of nodes with duplicate keys, an arbitrary one is deleted.
291
+ #
292
+ # Complexity: amortized O(log n)
293
+ #
294
+ # minheap = MinHeap.new([1, 2])
295
+ # minheap.delete(1) #=> 1
296
+ # minheap.size #=> 1
297
+ def delete(key)
298
+ pop if change_key(key, nil, true)
299
+ end
300
+
301
+ # Node class used internally
302
+ class Node # :nodoc:
303
+ attr_accessor :parent, :child, :left, :right, :key, :value, :degree, :marked
304
+
305
+ def initialize(key, value)
306
+ @key = key
307
+ @value = value
308
+ @degree = 0
309
+ @marked = false
310
+ @right = self
311
+ @left = self
312
+ end
313
+
314
+ def marked?
315
+ @marked == true
316
+ end
317
+
318
+ end
319
+
320
+ # make node a child of a parent node
321
+ def link_nodes(child, parent)
322
+ # link the child's siblings
323
+ child.left.right = child.right
324
+ child.right.left = child.left
325
+
326
+ child.parent = parent
327
+
328
+ # if parent doesn't have children, make new child its only child
329
+ if parent.child.nil?
330
+ parent.child = child.right = child.left = child
331
+ else # otherwise insert new child into parent's children list
332
+ current_child = parent.child
333
+ child.left = current_child
334
+ child.right = current_child.right
335
+ current_child.right.left = child
336
+ current_child.right = child
337
+ end
338
+ parent.degree += 1
339
+ child.marked = false
340
+ end
341
+ private :link_nodes
342
+
343
+ # Makes sure the structure does not contain nodes in the root list with equal degrees
344
+ def consolidate
345
+ roots = []
346
+ root = @next
347
+ min = root
348
+ # find the nodes in the list
349
+ loop do
350
+ roots << root
351
+ root = root.right
352
+ break if root == @next
353
+ end
354
+ degrees = []
355
+ roots.each do |root|
356
+ min = root if @compare_fn[root.key, min.key]
357
+ # check if we need to merge
358
+ if degrees[root.degree].nil? # no other node with the same degree
359
+ degrees[root.degree] = root
360
+ next
361
+ else # there is another node with the same degree, consolidate them
362
+ degree = root.degree
363
+ until degrees[degree].nil? do
364
+ other_root_with_degree = degrees[degree]
365
+ if @compare_fn[root.key, other_root_with_degree.key] # determine which node is the parent, which one is the child
366
+ smaller, larger = root, other_root_with_degree
367
+ else
368
+ smaller, larger = other_root_with_degree, root
369
+ end
370
+ link_nodes(larger, smaller)
371
+ degrees[degree] = nil
372
+ root = smaller
373
+ degree += 1
374
+ end
375
+ degrees[degree] = root
376
+ min = root if min.key == root.key # this fixes a bug with duplicate keys not being in the right order
377
+ end
378
+ end
379
+ @next = min
380
+ end
381
+ private :consolidate
382
+
383
+ def cascading_cut(node)
384
+ p = node.parent
385
+ if p
386
+ if node.marked?
387
+ cut(node, p)
388
+ cascading_cut(p)
389
+ else
390
+ node.marked = true
391
+ end
392
+ end
393
+ end
394
+ private :cascading_cut
395
+
396
+ # remove x from y's children and add x to the root list
397
+ def cut(x, y)
398
+ x.left.right = x.right
399
+ x.right.left = x.left
400
+ y.degree -= 1
401
+ if (y.degree == 0)
402
+ y.child = nil
403
+ elsif (y.child == x)
404
+ y.child = x.right
405
+ end
406
+ x.right = @next
407
+ x.left = @next.left
408
+ @next.left = x
409
+ x.left.right = x
410
+ x.parent = nil
411
+ x.marked = false
412
+ end
413
+ private :cut
414
+
415
+ end
416
+
417
+ # A MaxHeap is a heap where the items are returned in descending order of key value.
418
+ class Containers::MaxHeap < Containers::Heap
419
+
420
+ # call-seq:
421
+ # MaxHeap.new(ary) -> new_heap
422
+ #
423
+ # Creates a new MaxHeap with an optional array parameter of items to insert into the heap.
424
+ # A MaxHeap is created by calling Heap.new { |x, y| (x <=> y) == 1 }, so this is a convenience class.
425
+ #
426
+ # maxheap = MaxHeap.new([1, 2, 3, 4])
427
+ # maxheap.pop #=> 4
428
+ # maxheap.pop #=> 3
429
+ def initialize(ary=[])
430
+ super(ary) { |x, y| (x <=> y) == 1 }
431
+ end
432
+
433
+ # call-seq:
434
+ # max -> value
435
+ # max -> nil
436
+ #
437
+ # Returns the item with the largest key, but does not remove it from the heap.
438
+ #
439
+ # maxheap = MaxHeap.new([1, 2, 3, 4])
440
+ # maxheap.max #=> 4
441
+ def max
442
+ self.next
443
+ end
444
+
445
+ # call-seq:
446
+ # max! -> value
447
+ # max! -> nil
448
+ #
449
+ # Returns the item with the largest key and removes it from the heap.
450
+ #
451
+ # maxheap = MaxHeap.new([1, 2, 3, 4])
452
+ # maxheap.max! #=> 4
453
+ # maxheap.size #=> 3
454
+ def max!
455
+ self.pop
456
+ end
457
+ end
458
+
459
+ # A MinHeap is a heap where the items are returned in ascending order of key value.
460
+ class Containers::MinHeap < Containers::Heap
461
+
462
+ # call-seq:
463
+ # MinHeap.new(ary) -> new_heap
464
+ #
465
+ # Creates a new MinHeap with an optional array parameter of items to insert into the heap.
466
+ # A MinHeap is created by calling Heap.new { |x, y| (x <=> y) == -1 }, so this is a convenience class.
467
+ #
468
+ # minheap = MinHeap.new([1, 2, 3, 4])
469
+ # minheap.pop #=> 1
470
+ # minheap.pop #=> 2
471
+ def initialize(ary=[])
472
+ super(ary) { |x, y| (x <=> y) == -1 }
473
+ end
474
+
475
+ # call-seq:
476
+ # min -> value
477
+ # min -> nil
478
+ #
479
+ # Returns the item with the smallest key, but does not remove it from the heap.
480
+ #
481
+ # minheap = MinHeap.new([1, 2, 3, 4])
482
+ # minheap.min #=> 1
483
+ def min
484
+ self.next
485
+ end
486
+
487
+ # call-seq:
488
+ # min! -> value
489
+ # min! -> nil
490
+ #
491
+ # Returns the item with the smallest key and removes it from the heap.
492
+ #
493
+ # minheap = MinHeap.new([1, 2, 3, 4])
494
+ # minheap.min! #=> 1
495
+ # minheap.size #=> 3
496
+ def min!
497
+ self.pop
498
+ end
499
+ end