motion-map 0.0.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.
- checksums.yaml +15 -0
- data/.gitignore +2 -0
- data/README.md +51 -0
- data/Rakefile +15 -0
- data/app/a.rb +16 -0
- data/app/app_delegate.rb +11 -0
- data/lib/motion-map.rb +9 -0
- data/lib/motion-map/map.rb +1028 -0
- data/lib/motion-map/version.rb +3 -0
- data/motion-map.gemspec +17 -0
- data/spec/helpers/map_helper.rb +13 -0
- data/spec/map/map_spec.rb +521 -0
- metadata +70 -0
data/motion-map.gemspec
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/motion-map/version.rb', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.name = 'motion-map'
|
6
|
+
gem.version = MotionMap::VERSION
|
7
|
+
gem.authors = ['Fernand Galiana']
|
8
|
+
gem.email = ['fernand.galiana@gmail.com']
|
9
|
+
gem.summary = %{Port of the most excellent Map gem to RubyMotion}
|
10
|
+
gem.description = %{Ported Ara Howard's Map gem to RubyMotion}
|
11
|
+
gem.homepage = 'https://github.com/derailed/motion-map'
|
12
|
+
gem.files = `git ls-files`.split($\)
|
13
|
+
gem.test_files = gem.files.grep(%r{^spec/})
|
14
|
+
gem.require_paths = ['lib']
|
15
|
+
|
16
|
+
gem.add_development_dependency 'rspec'
|
17
|
+
end
|
@@ -0,0 +1,521 @@
|
|
1
|
+
describe Map do
|
2
|
+
extend MapHelper
|
3
|
+
|
4
|
+
describe 'constructor' do
|
5
|
+
it 'supports bare constructor' do
|
6
|
+
map = Map.new
|
7
|
+
map .count.should == 0
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'can take a hash' do
|
11
|
+
map = Map.new( {} )
|
12
|
+
map.count.should == 0
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'can take an empty array' do
|
16
|
+
array = []
|
17
|
+
map = Map.new( array )
|
18
|
+
map.count.should == 0
|
19
|
+
map = Map.new( *array )
|
20
|
+
map.count.should == 0
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'deals with nil correctly' do
|
24
|
+
map = Map.new( nil )
|
25
|
+
map.count.should == 0
|
26
|
+
map = Map.new( false )
|
27
|
+
map.count.should == 0
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'accepts an even sized array' do
|
31
|
+
arrays = [
|
32
|
+
[ %w( k v ), %w( key val ) ],
|
33
|
+
[ %w( k v ), %w( key val ), %w( a b ) ],
|
34
|
+
[ %w( k v ), %w( key val ), %w( a b ), %w( x y ) ]
|
35
|
+
]
|
36
|
+
expectations = [1, 2, 2]
|
37
|
+
i = 0
|
38
|
+
arrays.each do |array|
|
39
|
+
map = Map.new(array)
|
40
|
+
map.count.should == expectations[i]
|
41
|
+
map = Map.new(*array)
|
42
|
+
map.count.should == expectations[i]
|
43
|
+
i += 1
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'can deal with [] as new' do
|
48
|
+
list = [
|
49
|
+
[],
|
50
|
+
[{}],
|
51
|
+
[[:key, :val]],
|
52
|
+
[:key, :val]
|
53
|
+
]
|
54
|
+
list.each do |args|
|
55
|
+
map = Map[*args]
|
56
|
+
map.class.should == Map
|
57
|
+
Map.new(*args).should == map
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe 'iterators' do
|
63
|
+
it 'yields pairs in order' do
|
64
|
+
map = new_int_map
|
65
|
+
i = 0
|
66
|
+
map.each do |key, val|
|
67
|
+
key.should == i.to_s
|
68
|
+
val.should == i
|
69
|
+
i += 1
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'preserves ordering' do
|
74
|
+
map = new_int_map(n=2048)
|
75
|
+
values = Array.new(n) {|i| i}
|
76
|
+
keys = values.map{|value| value.to_s}
|
77
|
+
map.keys.size.should == n
|
78
|
+
map.keys.should == keys
|
79
|
+
map.values.should == values
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe 'accessors' do
|
84
|
+
it 'Maps string/symbol indifferent for simple look-ups' do
|
85
|
+
map = Map.new
|
86
|
+
map[:k] = :v
|
87
|
+
map['a'] = 'b'
|
88
|
+
map[:k].should == :v
|
89
|
+
map[:k.to_s].should == :v
|
90
|
+
map[:a].should == 'b'
|
91
|
+
map[:a.to_s].should == 'b'
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'Maps string/symbol indifferent for recursive look-ups' do
|
95
|
+
map = Map(:a => {:b => {:c => 42}})
|
96
|
+
|
97
|
+
map[:a].should == {:b => {:c => 42}}
|
98
|
+
map[:a][:b][:c].should == 42
|
99
|
+
map['a'][:b][:c].should == 42
|
100
|
+
map['a']['b'][:c].should == 42
|
101
|
+
map['a']['b']['c'].should == 42
|
102
|
+
map[:a]['b'][:c].should == 42
|
103
|
+
map[:a]['b']['c'].should == 42
|
104
|
+
map[:a][:b]['c'].should == 42
|
105
|
+
map['a'][:b]['c'].should == 42
|
106
|
+
|
107
|
+
map = Map[:a => [{:b => 42}]]
|
108
|
+
map['a'].class.should == Array
|
109
|
+
map['a'][0].class.should == Map
|
110
|
+
map['a'][0]['b'].should == 42
|
111
|
+
|
112
|
+
map = Map(:a => [ {:b => 42}, [{:c => 'forty-two'}] ])
|
113
|
+
map['a'].class.should == Array
|
114
|
+
map['a'][0].class.should == Map
|
115
|
+
map['a'][1].class.should == Array
|
116
|
+
map['a'][0]['b'].should == 42
|
117
|
+
map['a'][1][0]['c'].should == 'forty-two'
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe 'shift' do
|
122
|
+
it 'supports shift like a good ordered container' do
|
123
|
+
map = Map.new
|
124
|
+
10.times do |i|
|
125
|
+
key, val = i.to_s, i
|
126
|
+
map.unshift(key, val)
|
127
|
+
map[key].should == val
|
128
|
+
map.keys.first.to_s.should == key.to_s
|
129
|
+
map.values.first.to_s.should == val.to_s
|
130
|
+
end
|
131
|
+
|
132
|
+
map = Map.new
|
133
|
+
args = []
|
134
|
+
10.times do |i|
|
135
|
+
key, val = i.to_s, i
|
136
|
+
args.unshift([key, val])
|
137
|
+
end
|
138
|
+
map.unshift(*args)
|
139
|
+
10.times do |i|
|
140
|
+
key, val = i.to_s, i
|
141
|
+
map[key].should == val
|
142
|
+
map.keys[i].to_s.should == key.to_s
|
143
|
+
map.values[i].to_s.should == val.to_s
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
describe 'comparator' do
|
149
|
+
it 'supports match operator, which can make testing hash equality simpler!' do
|
150
|
+
map = new_int_map
|
151
|
+
hash = new_int_hash
|
152
|
+
map.should =~ hash
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'supports inheritence without cycles' do
|
156
|
+
c = Class.new(Map){}
|
157
|
+
o = c.new
|
158
|
+
Map.should === o
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'captures equality correctly' do
|
162
|
+
a = Map.new
|
163
|
+
b = Map.new
|
164
|
+
a.should == b
|
165
|
+
a.should != 42
|
166
|
+
b[:k] = :v
|
167
|
+
a.should != b
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
describe 'keys as methods' do
|
172
|
+
it 'works with simple usage' do
|
173
|
+
a = Map.new( k: :v)
|
174
|
+
a.k.should == :v
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'works with complexusage' do
|
178
|
+
a = Map.new( k: {a: {b: 10}} )
|
179
|
+
a.k.a.b.should == 10
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
describe 'subclassing' do
|
184
|
+
it 'insures subclassing and clobbering initialize does not kill nested coersion' do
|
185
|
+
c = Class.new(Map){ def initialize(arg) end }
|
186
|
+
o = c.new(42)
|
187
|
+
o.class.should == c
|
188
|
+
o.update(:k => {:a => :b})
|
189
|
+
o.size.should == 1
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'ensures that subclassing does not kill class level coersion' do
|
193
|
+
c = Class.new(Map){ }
|
194
|
+
o = c.for(Map.new)
|
195
|
+
o.class.should == c
|
196
|
+
|
197
|
+
d = Class.new(c)
|
198
|
+
o = d.for(Map.new)
|
199
|
+
o.class.should == d
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
describe '#to_list' do
|
204
|
+
it 'insures Maps can be converted to lists with numeric indexes' do
|
205
|
+
m = Map[0, :a, 1, :b, 2, :c]
|
206
|
+
m.to_list.should == [:a, :b, :c]
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
describe 'method_missing' do
|
211
|
+
it 'ensures method_missing hacks allow setting values, but not getting them until they are set' do
|
212
|
+
m = Map.new
|
213
|
+
(m.missing rescue $!).class.should == NoMethodError
|
214
|
+
m.missing = :val
|
215
|
+
m[:missing].should == :val
|
216
|
+
m.missing.should == :val
|
217
|
+
end
|
218
|
+
|
219
|
+
it 'ensures method_missing hacks have sane respond_to? semantics' do
|
220
|
+
m = Map.new
|
221
|
+
m.respond_to?(:missing).should == false
|
222
|
+
m.respond_to?(:missing=).should == true
|
223
|
+
m.missing = :val
|
224
|
+
m.respond_to?(:missing).should == true
|
225
|
+
m.respond_to?(:missing=).should == true
|
226
|
+
end
|
227
|
+
|
228
|
+
it 'ensures method missing with a block delegatets to fetch' do
|
229
|
+
m = Map.new
|
230
|
+
m.missing{ :val }.should == :val
|
231
|
+
m.has_key?(:key).should == false
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
describe 'compound keys' do
|
236
|
+
it 'Maps support compound key/val setting' do
|
237
|
+
m = Map.new
|
238
|
+
m.set(:a, :b, :c, 42)
|
239
|
+
m.get(:a, :b, :c).should == 42
|
240
|
+
|
241
|
+
m = Map.new
|
242
|
+
m.set([:a, :b, :c], 42)
|
243
|
+
m.get(:a, :b, :c) == 42
|
244
|
+
|
245
|
+
m = Map.new
|
246
|
+
m.set([:a, :b, :c] => 42)
|
247
|
+
m.get(:a, :b, :c) == 42
|
248
|
+
|
249
|
+
m = Map.new
|
250
|
+
m.set([:x, :y, :z] => 42.0, [:A, 2] => 'forty-two')
|
251
|
+
m[:A].class.should == Array
|
252
|
+
m[:A].size.should == 3
|
253
|
+
m[:A][2].should == 'forty-two'
|
254
|
+
m[:x][:y].class.should == Map
|
255
|
+
m[:x][:y][:z].should == 42.0
|
256
|
+
|
257
|
+
Map.new.tap{|m| m.set}.should =~ {}
|
258
|
+
Map.new.tap{|m| m.set({})} =~ {}
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
describe '#get' do
|
263
|
+
it 'supports providing a default value in a block' do
|
264
|
+
m = Map.new
|
265
|
+
m.set(:a, :b, :c, 42)
|
266
|
+
m.set(:z, 1)
|
267
|
+
|
268
|
+
m.get(:x){1}.should == 1
|
269
|
+
m.get(:z){2}.should == 1
|
270
|
+
m.get(:a, :b, :d){1}.should == 1
|
271
|
+
m.get(:a, :b, :c){1}.should == 42
|
272
|
+
m.get(:a, :b){1}.should == Map({:c => 42})
|
273
|
+
m.get(:a, :aa){1}.should == 1
|
274
|
+
end
|
275
|
+
|
276
|
+
it 'ensures setting a sub-container does not eff up the container values' do
|
277
|
+
m = Map.new
|
278
|
+
m.set(:array => [0,1,2])
|
279
|
+
m.get(:array, 0).should == 0
|
280
|
+
m.get(:array, 1).should == 1
|
281
|
+
m.get(:array, 2).should == 2
|
282
|
+
|
283
|
+
m.set(:array, 2, 42)
|
284
|
+
m.get(:array, 0).should == 0
|
285
|
+
m.get(:array, 1).should == 1
|
286
|
+
m.get(:array, 2).should == 42
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
describe 'merging' do
|
291
|
+
it 'ensures #apply selectively merges non-nil values' do
|
292
|
+
m = Map.new(:array => [0, 1], :hash => {:a => false, :b => nil, :c => 42})
|
293
|
+
defaults = Map.new(:array => [nil, nil, 2], :hash => {:b => true})
|
294
|
+
|
295
|
+
m.apply(defaults)
|
296
|
+
m[:array].should == [0,1,2]
|
297
|
+
m[:hash].should =~ {:a => false, :b => true, :c => 42}
|
298
|
+
|
299
|
+
m = Map.new
|
300
|
+
m.apply :key => [{:key => :val}]
|
301
|
+
m[:key].class.should == Array
|
302
|
+
m[:key][0].class.should == Map
|
303
|
+
end
|
304
|
+
|
305
|
+
it 'ensures #add overlays the leaves of one hash onto another without nuking branches' do
|
306
|
+
m = Map.new
|
307
|
+
|
308
|
+
m.add(
|
309
|
+
:comments => [
|
310
|
+
{ :body => 'a' },
|
311
|
+
{ :body => 'b' },
|
312
|
+
],
|
313
|
+
|
314
|
+
[:comments, 0] => {'title' => 'teh title', 'description' => 'description'},
|
315
|
+
[:comments, 1] => {'description' => 'description'},
|
316
|
+
)
|
317
|
+
m.should =~
|
318
|
+
{"comments"=>
|
319
|
+
[{"body"=>"a", "title"=>"teh title", "description"=>"description"},
|
320
|
+
{"body"=>"b", "description"=>"description"}]}
|
321
|
+
|
322
|
+
m = Map.new
|
323
|
+
m.add(
|
324
|
+
[:a, :b, :c] => 42,
|
325
|
+
[:a, :b] => {:d => 42.0}
|
326
|
+
)
|
327
|
+
m.should =~ {"a"=>{"b"=>{"c"=>42, "d"=>42.0}}}
|
328
|
+
|
329
|
+
Map.new.tap{|m| m.add}.should =~ {}
|
330
|
+
Map.new.tap{|m| m.add({})}.should =~ {}
|
331
|
+
end
|
332
|
+
|
333
|
+
it 'ensures that Map.combine is teh sweet' do
|
334
|
+
{
|
335
|
+
[{:a => {:b => 42}}, {:a => {:c => 42.0}}] => {"a"=>{"b"=>42, "c"=>42.0}},
|
336
|
+
[{:a => {:b => 42}}, {:a => {:c => 42.0, :d => [1]}}] => {"a"=>{"b"=>42, "d"=>[1], "c"=>42.0}},
|
337
|
+
[{:a => {:b => 42}}, {:a => {:c => 42.0, :d => {0=>1}}}] => {"a"=>{"b"=>42, "d"=>{0=>1}, "c"=>42.0}}
|
338
|
+
}.each do |args, expected|
|
339
|
+
Map.combine(*args).should =~ expected
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
describe 'traversal' do
|
345
|
+
it 'supports depth_first_each' do
|
346
|
+
m = Map.new
|
347
|
+
prefix = %w[ a b c ]
|
348
|
+
keys = []
|
349
|
+
n = 0.42
|
350
|
+
|
351
|
+
10.times do |i|
|
352
|
+
key = prefix + [i]
|
353
|
+
val = n
|
354
|
+
keys.push(key)
|
355
|
+
m.set(key => val)
|
356
|
+
n *= 10
|
357
|
+
end
|
358
|
+
m.get(:a).class.should == Map
|
359
|
+
m.get(:a, :b).class.should == Map
|
360
|
+
m.get(:a, :b, :c).class.should == Array
|
361
|
+
|
362
|
+
n = 0.42
|
363
|
+
m.depth_first_each do |key, val|
|
364
|
+
key.should == keys.shift
|
365
|
+
val.should == n
|
366
|
+
n *= 10
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
it 'ensures #each_pair works on arrays' do
|
371
|
+
each = []
|
372
|
+
array = %w( a b c )
|
373
|
+
Map.each_pair(array){|k,v| each.push(k,v)}
|
374
|
+
each.should == ['a', 'b', 'c', nil]
|
375
|
+
end
|
376
|
+
|
377
|
+
it 'supports breath_first_each' do
|
378
|
+
m = Map[
|
379
|
+
'hash' , {'x' => 'y'},
|
380
|
+
'nested hash' , {'nested' => {'a' => 'b'}},
|
381
|
+
'array' , [0, 1, 2],
|
382
|
+
'nested array' , [[3], [4], [5]],
|
383
|
+
'string' , '42'
|
384
|
+
]
|
385
|
+
|
386
|
+
accum = []
|
387
|
+
m.breadth_first_each(Map){|k, v| accum.push([k, v])}
|
388
|
+
expected =
|
389
|
+
[
|
390
|
+
[["hash"], {"x"=>"y"}],
|
391
|
+
[["nested hash"], {"nested"=>{"a"=>"b"}}],
|
392
|
+
[["array"], [0, 1, 2]],
|
393
|
+
[["nested array"], [[3], [4], [5]]],
|
394
|
+
[["string"], "42"],
|
395
|
+
|
396
|
+
[["hash", "x"], "y"],
|
397
|
+
[["nested hash", "nested"], {"a"=>"b"}],
|
398
|
+
[["array", 0], 0],
|
399
|
+
[["array", 1], 1],
|
400
|
+
[["array", 2], 2],
|
401
|
+
[["nested array", 0], [3]],
|
402
|
+
[["nested array", 1], [4]],
|
403
|
+
[["nested array", 2], [5]],
|
404
|
+
|
405
|
+
[["nested hash", "nested", "a"], "b"],
|
406
|
+
[["nested array", 0, 0], 3],
|
407
|
+
[["nested array", 1, 0], 4],
|
408
|
+
[["nested array", 2, 0], 5]
|
409
|
+
]
|
410
|
+
accum.should == expected
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
describe '#contains' do
|
415
|
+
it 'handles needle-in-a-haystack like #contains? method' do
|
416
|
+
haystack = Map[
|
417
|
+
'hash' , {'x' => 'y'},
|
418
|
+
'nested hash' , {'nested' => {'a' => 'b'}},
|
419
|
+
'array' , [0, 1, 2],
|
420
|
+
'nested array' , [[3], [4], [5]],
|
421
|
+
'string' , '42'
|
422
|
+
]
|
423
|
+
|
424
|
+
needles = [
|
425
|
+
{'x' => 'y'},
|
426
|
+
{'nested' => {'a' => 'b'}},
|
427
|
+
{'a' => 'b'},
|
428
|
+
[0,1,2],
|
429
|
+
[[3], [4], [5]],
|
430
|
+
[3],
|
431
|
+
[4],
|
432
|
+
[5],
|
433
|
+
'42',
|
434
|
+
0,1,2,
|
435
|
+
3,4,5
|
436
|
+
]
|
437
|
+
|
438
|
+
needles.each do |needle|
|
439
|
+
haystack.contains?(needle).should == true
|
440
|
+
end
|
441
|
+
end
|
442
|
+
end
|
443
|
+
|
444
|
+
it 'ensures #blank? method that is sane' do
|
445
|
+
m = Map.new(:a => 0, :b => ' ', :c => '', :d => {}, :e => [], :f => false)
|
446
|
+
m.each do |key, val|
|
447
|
+
m.blank?(key).should == true
|
448
|
+
end
|
449
|
+
|
450
|
+
m = Map.new(:a => 1, :b => '_', :d => {:k=>:v}, :e => [42], :f => true)
|
451
|
+
m.each do |key, val|
|
452
|
+
m.blank?(key).should == false
|
453
|
+
end
|
454
|
+
Map.new.blank?.should == true
|
455
|
+
end
|
456
|
+
|
457
|
+
it 'ensures self referential Maps do not make #inspect puke' do
|
458
|
+
a = Map.new
|
459
|
+
b = Map.new
|
460
|
+
|
461
|
+
b[:a] = a
|
462
|
+
a[:b] = b
|
463
|
+
|
464
|
+
begin
|
465
|
+
a.inspect
|
466
|
+
b.inspect
|
467
|
+
true.should == true
|
468
|
+
rescue => boom
|
469
|
+
fail boom
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
it 'has a clever little rm operator' do
|
474
|
+
m = Map.new
|
475
|
+
m.set :a, :b, 42
|
476
|
+
m.set :x, :y, 42
|
477
|
+
m.set :x, :z, 42
|
478
|
+
m.set :array, [0,1,2]
|
479
|
+
|
480
|
+
m.rm(:x, :y)
|
481
|
+
m.get(:x).should =~ {:z => 42}
|
482
|
+
|
483
|
+
m.rm(:a, :b)
|
484
|
+
m.get(:a).should =~ {}
|
485
|
+
|
486
|
+
m.rm(:array, 0)
|
487
|
+
m.get(:array).should == [1,2]
|
488
|
+
m.rm(:array, 1)
|
489
|
+
m.get(:array).should == [1]
|
490
|
+
m.rm(:array, 0)
|
491
|
+
m.get(:array).should == []
|
492
|
+
|
493
|
+
m.rm(:array)
|
494
|
+
m.get(:array).should == nil
|
495
|
+
|
496
|
+
m.rm(:a)
|
497
|
+
m.get(:a).should == nil
|
498
|
+
|
499
|
+
m.rm(:x)
|
500
|
+
m.get(:x).should == nil
|
501
|
+
end
|
502
|
+
|
503
|
+
it 'Maps a clever little question method' do
|
504
|
+
m = Map.new
|
505
|
+
m.set(:a, :b, :c, 42)
|
506
|
+
m.set([:x, :y, :z] => 42.0, [:A, 2] => 'forty-two')
|
507
|
+
|
508
|
+
m.b?.should == false
|
509
|
+
m.a?.should == true
|
510
|
+
m.a.b?.should == true
|
511
|
+
m.a.b.c?.should == true
|
512
|
+
m.a.b.d?.should == false
|
513
|
+
|
514
|
+
m.x?.should == true
|
515
|
+
m.x.y?.should == true
|
516
|
+
m.x.y.z?.should == true
|
517
|
+
m.y?.should == false
|
518
|
+
|
519
|
+
m.A?.should == true
|
520
|
+
end
|
521
|
+
end
|