algorithms 0.5.0 → 1.1.0

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.
data/spec/deque_spec.rb CHANGED
@@ -3,31 +3,31 @@ require 'algorithms'
3
3
 
4
4
  shared_examples "(empty deque)" do
5
5
  it "should return nil when popping objects" do
6
- @deque.pop_front.should be_nil
7
- @deque.pop_back.should be_nil
6
+ expect(@deque.pop_front).to be_nil
7
+ expect(@deque.pop_back).to be_nil
8
8
  end
9
9
 
10
10
  it "should return a size of 1 when sent #push_front" do
11
11
  @deque.push_front(1)
12
- @deque.size.should eql(1)
12
+ expect(@deque.size).to eql(1)
13
13
  end
14
14
 
15
15
  it "should return a size of 1 when sent #push_back" do
16
16
  @deque.push_back(1)
17
- @deque.size.should eql(1)
17
+ expect(@deque.size).to eql(1)
18
18
  end
19
19
 
20
20
  it "should return nil when sent #front and #back" do
21
- @deque.front.should be_nil
22
- @deque.back.should be_nil
21
+ expect(@deque.front).to be_nil
22
+ expect(@deque.back).to be_nil
23
23
  end
24
24
 
25
25
  it "should be empty" do
26
- @deque.should be_empty
26
+ expect(@deque).to be_empty
27
27
  end
28
28
 
29
29
  it "should raise ArgumentError if passed more than one argument" do
30
- lambda { @deque.class.send("new", Time.now, []) }.should raise_error
30
+ expect { @deque.class.send("new", Time.now, []) }.to raise_error(ArgumentError)
31
31
  end
32
32
  end
33
33
 
@@ -38,40 +38,40 @@ shared_examples "(non-empty deque)" do
38
38
  end
39
39
 
40
40
  it "should return last pushed object with pop_back" do
41
- @deque.pop_back.should eql("10")
42
- @deque.pop_back.should eql(10)
41
+ expect(@deque.pop_back).to eql("10")
42
+ expect(@deque.pop_back).to eql(10)
43
43
  end
44
44
 
45
45
  it "should return first pushed object with pop_front" do
46
- @deque.pop_front.should eql(10)
47
- @deque.pop_front.should eql("10")
46
+ expect(@deque.pop_front).to eql(10)
47
+ expect(@deque.pop_front).to eql("10")
48
48
  end
49
49
 
50
50
  it "should return a size greater than 0" do
51
- @deque.size.should eql(2)
51
+ expect(@deque.size).to eql(2)
52
52
  end
53
53
 
54
54
  it "should not be empty" do
55
- @deque.should_not be_empty
55
+ expect(@deque).not_to be_empty
56
56
  end
57
57
 
58
58
  it "should iterate in LIFO order with #each_backward" do
59
59
  arr = []
60
60
  @deque.each_backward { |obj| arr << obj }
61
- arr.should eql(["10", 10])
61
+ expect(arr).to eql(["10", 10])
62
62
  end
63
63
 
64
64
  it "should iterate in FIFO order with #each_forward" do
65
65
  arr = []
66
66
  @deque.each_forward { |obj| arr << obj }
67
- arr.should eql([10, "10"])
67
+ expect(arr).to eql([10, "10"])
68
68
  end
69
69
 
70
70
  it "should return nil after everything's popped" do
71
71
  @deque.pop_back
72
72
  @deque.pop_back
73
- @deque.pop_back.should be_nil
74
- @deque.front.should be_nil
73
+ expect(@deque.pop_back).to be_nil
74
+ expect(@deque.front).to be_nil
75
75
  end
76
76
  end
77
77
 
data/spec/heap_spec.rb CHANGED
@@ -1,35 +1,221 @@
1
1
  $: << File.join(File.expand_path(File.dirname(__FILE__)), '..', 'lib')
2
2
  require 'algorithms'
3
3
 
4
+ include Comparable
5
+
6
+ class Book
7
+ attr_reader :title, :year_published
8
+
9
+ def initialize(title, year_published)
10
+ @title = title
11
+ @year_published = year_published
12
+ end
13
+
14
+ def <=>(other)
15
+ return nil unless other.is_a?(Book)
16
+
17
+ # 1. Compare by year first
18
+ year_comparison = self.year_published <=> other.year_published
19
+
20
+ # 2. If years are different, return that comparison result
21
+ return year_comparison unless year_comparison == 0
22
+
23
+ # 3. If years are the same (tie), compare by title alphabetically
24
+ # (Ensure title comparison returns -1, 0, or 1)
25
+ self.title <=> other.title
26
+ end
27
+
28
+
29
+ def to_s
30
+ "\"#{title}\" (#{year_published})" # Simplified for test output
31
+ end
32
+
33
+ # Add an equality method for clearer test failures if needed,
34
+ # though <=> returning 0 handles equality for sorting/heap purposes.
35
+ def ==(other)
36
+ other.is_a?(Book) &&
37
+ self.title == other.title &&
38
+ self.year_published == other.year_published
39
+ end
40
+ alias eql? ==
41
+
42
+ def hash
43
+ [title, year_published].hash
44
+ end
45
+
46
+ end
47
+
48
+ describe ::Containers::Heap do
49
+
50
+ # --- Test Data Setup ---
51
+ let!(:book_1932) { Book.new("Brave New World", 1932) }
52
+ let!(:book_1949) { Book.new("1984", 1949) }
53
+ let!(:book_1951a) { Book.new("Foundation", 1951) }
54
+ # Add another book from the same year to test handling of equal priority items
55
+ let!(:book_1951b) { Book.new("The Illustrated Man", 1951) }
56
+ let!(:book_1965) { Book.new("Dune", 1965) }
57
+ let!(:book_1989) { Book.new("Hyperion", 1989) }
58
+
59
+ # Use shuffle to ensure tests don't depend on insertion order
60
+ let(:books) { [book_1965, book_1951a, book_1989, book_1949, book_1932, book_1951b].shuffle }
61
+
62
+ let(:expected_min_order) { books.sort }
63
+
64
+ let(:expected_max_order) { books.sort.reverse }
65
+
66
+ context "Min-Heap (using default <=> via Comparable)" do
67
+ # Initialize heap with books; it should use Book#<=> by default
68
+ let(:min_heap) { ::Containers::Heap.new(books) }
69
+
70
+ it "initializes with the correct size" do
71
+ expect(min_heap.size).to eq(books.size)
72
+ expect(min_heap.empty?).to be false
73
+ end
74
+
75
+ it "gets the next minimum element (earliest year) without removing it" do
76
+ # next should return the element with the smallest year_published
77
+ expect(min_heap.next).to eq(book_1932)
78
+ # next should not change the size
79
+ expect(min_heap.size).to eq(books.size)
80
+ end
81
+
82
+ it "pops elements in ascending order of year_published" do
83
+ popped_books = []
84
+ while (book = min_heap.pop)
85
+ popped_books << book
86
+ end
87
+
88
+ # Verify the popped order matches the expected sorted order
89
+ expect(popped_books).to eq(expected_min_order)
90
+
91
+ # Verify the heap is now empty
92
+ expect(min_heap.size).to eq(0)
93
+ expect(min_heap.empty?).to be true
94
+ expect(min_heap.pop).to be_nil # Popping an empty heap
95
+ expect(min_heap.next).to be_nil # Getting next from an empty heap
96
+ end
97
+
98
+ it "correctly updates size after popping" do
99
+ expect(min_heap.size).to eq(books.size)
100
+ min_heap.pop
101
+ expect(min_heap.size).to eq(books.size - 1)
102
+ end
103
+ end
104
+
105
+
106
+ context "Max-Heap (using a custom comparison block)" do
107
+ # Initialize heap with books and a block for max-heap behavior
108
+ # The block returns true if x should have higher priority (be "larger") than y
109
+ let(:max_heap) { ::Containers::Heap.new(books) { |x, y| x > y } } # Use > from Comparable
110
+
111
+ it "initializes with the correct size" do
112
+ expect(max_heap.size).to eq(books.size)
113
+ expect(max_heap.empty?).to be false
114
+ end
115
+
116
+ it "gets the next maximum element (latest year) without removing it" do
117
+ # next should return the element with the largest year_published
118
+ expect(max_heap.next).to eq(book_1989)
119
+ # next should not change the size
120
+ expect(max_heap.size).to eq(books.size)
121
+ end
122
+
123
+ it "pops elements in descending order of year_published" do
124
+ popped_books = []
125
+ while (book = max_heap.pop)
126
+ popped_books << book
127
+ end
128
+
129
+ # Verify the popped order matches the expected reversed sorted order
130
+ expect(popped_books).to eq(expected_max_order)
131
+
132
+ # Verify the heap is now empty
133
+ expect(max_heap.size).to eq(0)
134
+ expect(max_heap.empty?).to be true
135
+ expect(max_heap.pop).to be_nil # Popping an empty heap
136
+ expect(max_heap.next).to be_nil # Getting next from an empty heap
137
+ end
138
+
139
+ it "correctly updates size after popping" do
140
+ expect(max_heap.size).to eq(books.size)
141
+ max_heap.pop
142
+ expect(max_heap.size).to eq(books.size - 1)
143
+ end
144
+ end
145
+
146
+ context "Edge cases" do
147
+ it "handles an empty initialization" do
148
+ heap = ::Containers::Heap.new([])
149
+ expect(heap.size).to eq(0)
150
+ expect(heap.empty?).to be true
151
+ expect(heap.pop).to be_nil
152
+ expect(heap.next).to be_nil
153
+ end
154
+
155
+ it "handles initialization with one element" do
156
+ heap = ::Containers::Heap.new([book_1965])
157
+ expect(heap.size).to eq(1)
158
+ expect(heap.next).to eq(book_1965)
159
+ expect(heap.pop).to eq(book_1965)
160
+ expect(heap.empty?).to be true
161
+ end
162
+ end
163
+
164
+ end
165
+
4
166
  describe Containers::Heap do
5
167
  before(:each) do
6
168
  @heap = Containers::MaxHeap.new
7
169
  end
170
+
171
+ it "should run without error when given Objects and a non-ordering comparator" do
172
+ # Create and store the initial distinct objects
173
+ initial_objects = 10.times.map { Object.new }
174
+ initial_object_ids = initial_objects.map(&:object_id) # Store IDs for comparison
175
+
176
+ min_heap = ::Containers::Heap.new(initial_objects) { |x, y| (x <=> y) == -1 }
177
+
178
+ expect(min_heap.size).to eq(10)
179
+
180
+ popped_elements = []
181
+ expect {
182
+ while val = min_heap.pop do
183
+ popped_elements << val
184
+ end
185
+ }.not_to raise_error
186
+
187
+ expect(min_heap.empty?).to be true
188
+ expect(popped_elements.size).to eq(10)
189
+
190
+ # Assert that exactly the same objects were returned, regardless of order.
191
+ # Comparing by object_id is the most reliable way for distinct Objects.
192
+ expect(popped_elements.map(&:object_id)).to contain_exactly(*initial_object_ids)
193
+ end
8
194
 
9
195
  it "should not let you merge with non-heaps" do
10
- lambda { @heap.merge!(nil) }.should raise_error
11
- lambda { @heap.merge!([]) }.should raise_error
196
+ expect { @heap.merge!(nil) }.to raise_error(ArgumentError)
197
+ expect { @heap.merge!([]) }.to raise_error(ArgumentError)
12
198
  end
13
199
 
14
200
  describe "(empty)" do
15
201
 
16
202
  it "should return nil when getting the maximum" do
17
- @heap.max!.should be_nil
203
+ expect(@heap.max!).to be_nil
18
204
  end
19
205
 
20
206
  it "should let you insert and remove one item" do
21
- @heap.size.should eql(0)
207
+ expect(@heap.size).to eql(0)
22
208
 
23
209
  @heap.push(1)
24
- @heap.size.should eql(1)
210
+ expect(@heap.size).to eql(1)
25
211
 
26
- @heap.max!.should eql(1)
27
- @heap.size.should eql(0)
212
+ expect(@heap.max!).to eql(1)
213
+ expect(@heap.size).to eql(0)
28
214
  end
29
215
 
30
216
  it "should let you initialize with an array" do
31
217
  @heap = Containers::MaxHeap.new([1,2,3])
32
- @heap.size.should eql(3)
218
+ expect(@heap.size).to eql(3)
33
219
  end
34
220
 
35
221
  end
@@ -38,25 +224,25 @@ describe Containers::Heap do
38
224
  before(:each) do
39
225
  @random_array = []
40
226
  @num_items = 100
41
- @num_items.times { |x| @random_array << rand(@num_items) }
227
+ @num_items.times { @random_array << rand(@num_items) }
42
228
  @heap = Containers::MaxHeap.new(@random_array)
43
229
  end
44
230
 
45
231
  it "should display the correct size" do
46
- @heap.size.should eql(@num_items)
232
+ expect(@heap.size).to eql(@num_items)
47
233
  end
48
234
 
49
235
  it "should have a next value" do
50
- @heap.next.should be_true
51
- @heap.next_key.should be_true
236
+ expect(@heap.next).to be_truthy
237
+ expect(@heap.next_key).to be_truthy
52
238
  end
53
239
 
54
240
  it "should delete random keys" do
55
- @heap.delete(@random_array[0]).should eql(@random_array[0])
56
- @heap.delete(@random_array[1]).should eql(@random_array[1])
241
+ expect(@heap.delete(@random_array[0])).to eql(@random_array[0])
242
+ expect(@heap.delete(@random_array[1])).to eql(@random_array[1])
57
243
  ordered = []
58
244
  ordered << @heap.max! until @heap.empty?
59
- ordered.should eql( @random_array[2..-1].sort.reverse )
245
+ expect(ordered).to eql( @random_array[2..-1].sort.reverse )
60
246
  end
61
247
 
62
248
  it "should delete all keys" do
@@ -64,15 +250,15 @@ describe Containers::Heap do
64
250
  @random_array.size.times do |t|
65
251
  ordered << @heap.delete(@random_array[t])
66
252
  end
67
- @heap.should be_empty
68
- ordered.should eql( @random_array )
253
+ expect(@heap).to be_empty
254
+ expect(ordered).to eql( @random_array )
69
255
  end
70
256
 
71
257
  it "should be in max->min order" do
72
258
  ordered = []
73
259
  ordered << @heap.max! until @heap.empty?
74
260
 
75
- ordered.should eql(@random_array.sort.reverse)
261
+ expect(ordered).to eql(@random_array.sort.reverse)
76
262
  end
77
263
 
78
264
  it "should change certain keys" do
@@ -84,12 +270,12 @@ describe Containers::Heap do
84
270
  heap.change_key(8, 0)
85
271
  ordered = []
86
272
  ordered << heap.min! until heap.empty?
87
- ordered.should eql( [8,3,4,5,6,7,9,10,101,100] )
273
+ expect(ordered).to eql( [8,3,4,5,6,7,9,10,101,100] )
88
274
  end
89
275
 
90
276
  it "should not delete keys it doesn't have" do
91
- @heap.delete(:nonexisting).should be_nil
92
- @heap.size.should eql(@num_items)
277
+ expect(@heap.delete(:nonexisting)).to be_nil
278
+ expect(@heap.size).to eql(@num_items)
93
279
  end
94
280
 
95
281
  it "should delete certain keys" do
@@ -101,19 +287,19 @@ describe Containers::Heap do
101
287
  heap.delete(100)
102
288
  ordered = []
103
289
  ordered << heap.min! until heap.empty?
104
- ordered.should eql( [3,4,6,7,8,9,10,101] )
290
+ expect(ordered).to eql( [3,4,6,7,8,9,10,101] )
105
291
  end
106
292
 
107
293
  it "should let you merge with another heap" do
108
294
  numbers = [1,2,3,4,5,6,7,8]
109
295
  otherheap = Containers::MaxHeap.new(numbers)
110
- otherheap.size.should eql(8)
296
+ expect(otherheap.size).to eql(8)
111
297
  @heap.merge!(otherheap)
112
298
 
113
299
  ordered = []
114
300
  ordered << @heap.max! until @heap.empty?
115
301
 
116
- ordered.should eql( (@random_array + numbers).sort.reverse)
302
+ expect(ordered).to eql( (@random_array + numbers).sort.reverse)
117
303
  end
118
304
 
119
305
  describe "min-heap" do
@@ -122,7 +308,7 @@ describe Containers::Heap do
122
308
  ordered = []
123
309
  ordered << @heap.min! until @heap.empty?
124
310
 
125
- ordered.should eql(@random_array.sort)
311
+ expect(ordered).to eql(@random_array.sort)
126
312
  end
127
313
  end
128
314
 
data/spec/kd_tree_spec.rb CHANGED
@@ -6,7 +6,7 @@ describe Containers::KDTree do
6
6
  kdtree = Containers::KDTree.new( {0 => [4, 3], 1 => [3, 0], 2 => [-1, 2], 3 => [6, 4],
7
7
  4 => [3, -5], 5 => [-2, -5] })
8
8
  closest_2 = kdtree.find_nearest([0, 0], 2)
9
- closest_2.should eql([[5, 2], [9, 1]])
9
+ expect(closest_2).to eql([[5, 2], [9, 1]])
10
10
  end
11
11
 
12
12
  it "should work for real-life example from facebook puzzle" do
@@ -28,7 +28,7 @@ describe Containers::KDTree do
28
28
  }
29
29
 
30
30
  expected = File.read(File.join(File.dirname(__FILE__), 'kd_expected_out.txt'))
31
- expected.should eql(out)
31
+ expect(expected).to eql(out)
32
32
  end
33
33
 
34
34
  end
@@ -1,27 +1,29 @@
1
1
  $: << File.join(File.expand_path(File.dirname(__FILE__)), '..', 'lib')
2
2
  require 'algorithms'
3
3
 
4
- describe "map gc mark test" do
5
- it "should mark ruby object references" do
6
- anon_key_class = Class.new do
7
- attr :value
8
- def initialize(x); @value = x; end
9
- def <=>(other); value <=> other.value; end
4
+ if !(defined? RUBY_ENGINE && RUBY_ENGINE == 'jruby')
5
+ describe "map gc mark test" do
6
+ it "should mark ruby object references" do
7
+ anon_key_class = Class.new do
8
+ attr :value
9
+ def initialize(x); @value = x; end
10
+ def <=>(other); value <=> other.value; end
11
+ end
12
+ anon_val_class = Class.new
13
+ @rbtree = Containers::RBTreeMap.new
14
+ @splaytree = Containers::SplayTreeMap.new
15
+ 100.times { |x|
16
+ @rbtree[anon_key_class.new(x)] = anon_val_class.new
17
+ @splaytree[anon_key_class.new(x)] = anon_val_class.new
18
+ }
19
+ # Mark and sweep
20
+ ObjectSpace.garbage_collect
21
+ # Check if any instances were swept
22
+ count = 0
23
+ ObjectSpace.each_object(anon_key_class) { |x| count += 1 }
24
+ expect(count).to eql(200)
25
+ ObjectSpace.each_object(anon_val_class) { |x| count += 1 }
26
+ expect(count).to eql(400)
10
27
  end
11
- anon_val_class = Class.new
12
- @rbtree = Containers::CRBTreeMap.new
13
- @splaytree = Containers::CSplayTreeMap.new
14
- 100.times { |x|
15
- @rbtree[anon_key_class.new(x)] = anon_val_class.new
16
- @splaytree[anon_key_class.new(x)] = anon_val_class.new
17
- }
18
- # Mark and sweep
19
- ObjectSpace.garbage_collect
20
- # Check if any instances were swept
21
- count = 0
22
- ObjectSpace.each_object(anon_key_class) { |x| count += 1 }
23
- count.should eql(200)
24
- ObjectSpace.each_object(anon_val_class) { |x| count += 1 }
25
- count.should eql(400)
26
28
  end
27
29
  end
@@ -8,32 +8,32 @@ describe Containers::PriorityQueue do
8
8
 
9
9
  describe "(empty priority queue)" do
10
10
  it "should return 0 for size and be empty" do
11
- @q.size.should eql(0)
12
- @q.should be_empty
11
+ expect(@q.size).to eql(0)
12
+ expect(@q).to be_empty
13
13
  end
14
14
 
15
15
  it "should not return anything" do
16
- @q.next.should be_nil
17
- @q.pop.should be_nil
18
- @q.delete(1).should be_nil
19
- @q.has_priority?(1).should be_false
16
+ expect(@q.next).to be_nil
17
+ expect(@q.pop).to be_nil
18
+ expect(@q.delete(1)).to be_nil
19
+ expect(@q.has_priority?(1)).to be false
20
20
  end
21
21
 
22
22
  it "should give the correct size when adding items" do
23
23
  20.times do |i|
24
- @q.size.should eql(i)
24
+ expect(@q.size).to eql(i)
25
25
  @q.push(i, i)
26
26
  end
27
27
  10.times do |i|
28
- @q.size.should eql(20-i)
28
+ expect(@q.size).to eql(20-i)
29
29
  @q.pop
30
30
  end
31
31
  10.times do |i|
32
- @q.size.should eql(i+10)
32
+ expect(@q.size).to eql(i+10)
33
33
  @q.push(i, i)
34
34
  end
35
35
  @q.delete(5)
36
- @q.size.should eql(19)
36
+ expect(@q.size).to eql(19)
37
37
  end
38
38
  end
39
39
 
@@ -45,31 +45,31 @@ describe Containers::PriorityQueue do
45
45
  end
46
46
 
47
47
  it "should next/pop the highest priority" do
48
- @q.next.should eql("Alaska")
49
- @q.size.should eql(3)
50
- @q.pop.should eql("Alaska")
51
- @q.size.should eql(2)
48
+ expect(@q.next).to eql("Alaska")
49
+ expect(@q.size).to eql(3)
50
+ expect(@q.pop).to eql("Alaska")
51
+ expect(@q.size).to eql(2)
52
52
  end
53
53
 
54
54
  it "should not be empty" do
55
- @q.should_not be_empty
55
+ expect(@q).not_to be_empty
56
56
  end
57
57
 
58
58
  it "should has_priority? priorities it has" do
59
- @q.has_priority?(50).should be_true
60
- @q.has_priority?(10).should be_false
59
+ expect(@q.has_priority?(50)).to be true
60
+ expect(@q.has_priority?(10)).to be false
61
61
  end
62
62
 
63
63
  it "should return nil after popping everything" do
64
64
  3.times do
65
65
  @q.pop
66
66
  end
67
- @q.pop.should be_nil
67
+ expect(@q.pop).to be_nil
68
68
  end
69
69
 
70
70
  it "should delete things it has and not things it doesn't" do
71
- @q.delete(50).should eql("Alaska")
72
- @q.delete(10).should eql(nil)
71
+ expect(@q.delete(50)).to eql("Alaska")
72
+ expect(@q.delete(10)).to eql(nil)
73
73
  end
74
74
  end
75
75
  end
data/spec/queue_spec.rb CHANGED
@@ -8,20 +8,20 @@ describe "empty queue" do
8
8
 
9
9
 
10
10
  it "should return nil when sent #pop" do
11
- @queue.pop.should be_nil
11
+ expect(@queue.pop).to be_nil
12
12
  end
13
13
 
14
14
  it "should return a size of 1 when sent #push" do
15
15
  @queue.push(1)
16
- @queue.size.should eql(1)
16
+ expect(@queue.size).to eql(1)
17
17
  end
18
18
 
19
19
  it "should return nil when sent #next" do
20
- @queue.next.should be_nil
20
+ expect(@queue.next).to be_nil
21
21
  end
22
22
 
23
23
  it "should return empty?" do
24
- @queue.empty?.should be_true
24
+ expect(@queue.empty?).to be true
25
25
  end
26
26
  end
27
27
 
@@ -33,29 +33,29 @@ describe "non-empty queue" do
33
33
  end
34
34
 
35
35
  it "should return first pushed object" do
36
- @queue.pop.should eql(10)
36
+ expect(@queue.pop).to eql(10)
37
37
  end
38
38
 
39
39
  it "should return the size" do
40
- @queue.size.should eql(2)
40
+ expect(@queue.size).to eql(2)
41
41
  end
42
42
 
43
43
  it "should not return empty?" do
44
- @queue.empty?.should be_false
44
+ expect(@queue.empty?).to be false
45
45
  end
46
46
 
47
47
  it "should iterate in FIFO order" do
48
48
  arr = []
49
49
  @queue.each { |obj| arr << obj }
50
- arr.should eql([10, "10"])
50
+ expect(arr).to eql([10, "10"])
51
51
  end
52
52
 
53
53
  it "should return nil after all gets" do
54
54
  2.times do
55
55
  @queue.pop
56
56
  end
57
- @queue.pop.should be_nil
58
- @queue.next.should be_nil
57
+ expect(@queue.pop).to be_nil
58
+ expect(@queue.next).to be_nil
59
59
  end
60
60
 
61
61
  end