fast_excel 0.2.5 → 0.2.6
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 +5 -5
- data/.gitignore +2 -1
- data/.travis.yml +12 -12
- data/CHANGELOG.md +4 -0
- data/Gemfile +2 -1
- data/Gemfile.lock +21 -16
- data/Makefile +2 -0
- data/README.md +30 -3
- data/Rakefile +8 -0
- data/benchmarks/1k_rows.rb +17 -4
- data/benchmarks/20k_rows.rb +4 -0
- data/benchmarks/auto_width.rb +37 -0
- data/benchmarks/init.rb +14 -2
- data/benchmarks/memory.rb +8 -0
- data/benchmarks/profiler.rb +27 -0
- data/benchmarks/write_value.rb +62 -0
- data/examples/example_auto_width.rb +26 -0
- data/ext/fast_excel/extconf.rb +3 -0
- data/ext/fast_excel/text_width_ext.c +460 -0
- data/fast_excel.gemspec +2 -3
- data/letters.html +114 -0
- data/lib/fast_excel.rb +79 -13
- data/lib/fast_excel/binding/format.rb +8 -1
- data/test/auto_width_test.rb +19 -0
- data/test/format_test.rb +8 -0
- data/test/text_width_test.rb +80 -0
- data/test/worksheet_test.rb +1 -1
- metadata +13 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 26cc6868779b440f2c848e60a68bce2272760255e1c4d4e887edae312d16eb76
|
|
4
|
+
data.tar.gz: 43e3d4288b2c2bb06685308a4c97b267a563d72c32c4902793dd6d3030cde890
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 49fa98254e1c6b147e0edd4262ddd87d5b75410b42b38843ae887bbb98ce9aae6bf6607bdebeec840bca73c98ebc5be1f451ed227d51a147fb6a1c6bd8bd0f67
|
|
7
|
+
data.tar.gz: 724f6b6e146816061c270899d798faa6ddcbe29e8ce9aba958bc23cb14c3ecdd0e0ce7de686b92b997957c73164a7f83c52f3283499a193eab96670bc9fdecec
|
data/.gitignore
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
.ruby-version
|
|
2
|
+
.idea/
|
|
2
3
|
try.c
|
|
3
4
|
try.rb
|
|
4
5
|
gen.rb
|
|
@@ -11,6 +12,6 @@ libxlsxwriter/dev
|
|
|
11
12
|
libxlsxwriter/docs
|
|
12
13
|
libxlsxwriter/examples
|
|
13
14
|
libxlsxwriter/test
|
|
14
|
-
|
|
15
|
+
ext/fast_excel/text_width_ext.o
|
|
15
16
|
ext/fast_excel/Makefile
|
|
16
17
|
ext/fast_excel/text_width_ext.bundle
|
data/.travis.yml
CHANGED
|
@@ -3,26 +3,26 @@ language: ruby
|
|
|
3
3
|
|
|
4
4
|
matrix:
|
|
5
5
|
include:
|
|
6
|
+
# - os: linux
|
|
7
|
+
# rvm: 2.2.10
|
|
8
|
+
# - os: osx
|
|
9
|
+
# rvm: 2.2.10
|
|
6
10
|
- os: linux
|
|
7
|
-
rvm: 2.
|
|
11
|
+
rvm: 2.3.8
|
|
8
12
|
- os: osx
|
|
9
|
-
rvm: 2.
|
|
13
|
+
rvm: 2.3.8
|
|
10
14
|
- os: linux
|
|
11
|
-
rvm: 2.
|
|
15
|
+
rvm: 2.4.5
|
|
12
16
|
- os: osx
|
|
13
|
-
rvm: 2.
|
|
17
|
+
rvm: 2.4.5
|
|
14
18
|
- os: linux
|
|
15
|
-
rvm: 2.3
|
|
19
|
+
rvm: 2.5.3
|
|
16
20
|
- os: osx
|
|
17
|
-
rvm: 2.3
|
|
21
|
+
rvm: 2.5.3
|
|
18
22
|
- os: linux
|
|
19
|
-
rvm: 2.
|
|
23
|
+
rvm: 2.6.0
|
|
20
24
|
- os: osx
|
|
21
|
-
rvm: 2.
|
|
22
|
-
- os: linux
|
|
23
|
-
rvm: 2.5.0
|
|
24
|
-
- os: osx
|
|
25
|
-
rvm: 2.5.0
|
|
25
|
+
rvm: 2.6.0
|
|
26
26
|
fast_finish: true
|
|
27
27
|
|
|
28
28
|
script:
|
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
|
@@ -5,7 +5,7 @@ gem 'ffi_gen', require: false
|
|
|
5
5
|
|
|
6
6
|
gem 'rake'
|
|
7
7
|
|
|
8
|
-
gem 'roo', '2.
|
|
8
|
+
gem 'roo', '2.8.1', git: 'https://github.com/roo-rb/roo.git', tag: 'v2.8.1'
|
|
9
9
|
|
|
10
10
|
gem 'minitest'
|
|
11
11
|
gem 'minitest-reporters'
|
|
@@ -13,5 +13,6 @@ gem 'minitest-reporters'
|
|
|
13
13
|
# For benchmakrs
|
|
14
14
|
gem 'axlsx', git: 'https://github.com/randym/axlsx.git'
|
|
15
15
|
gem 'write_xlsx'
|
|
16
|
+
gem 'xlsxtream'
|
|
16
17
|
gem 'benchmark-ips'
|
|
17
18
|
gem 'process_memory', git: 'https://github.com/paxa/process_memory', platforms: :ruby
|
data/Gemfile.lock
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
GIT
|
|
2
2
|
remote: https://github.com/paxa/process_memory
|
|
3
|
-
revision:
|
|
3
|
+
revision: 282c75336a4a6b92207cd1eaa57f20887b92601e
|
|
4
4
|
specs:
|
|
5
5
|
process_memory (0.1)
|
|
6
6
|
|
|
7
7
|
GIT
|
|
8
8
|
remote: https://github.com/randym/axlsx.git
|
|
9
|
-
revision:
|
|
9
|
+
revision: c593a08b2a929dac7aa8dc418b55e26b4c49dc34
|
|
10
10
|
specs:
|
|
11
11
|
axlsx (3.0.0.pre)
|
|
12
12
|
htmlentities (~> 4.3, >= 4.3.4)
|
|
@@ -16,9 +16,10 @@ GIT
|
|
|
16
16
|
|
|
17
17
|
GIT
|
|
18
18
|
remote: https://github.com/roo-rb/roo.git
|
|
19
|
-
revision:
|
|
19
|
+
revision: 5bbda9849ca6deb0ad8020c4476c1ab9ddfd824b
|
|
20
|
+
tag: v2.8.1
|
|
20
21
|
specs:
|
|
21
|
-
roo (2.
|
|
22
|
+
roo (2.8.1)
|
|
22
23
|
nokogiri (~> 1)
|
|
23
24
|
rubyzip (>= 1.2.1, < 2.0.0)
|
|
24
25
|
|
|
@@ -28,28 +29,31 @@ GEM
|
|
|
28
29
|
ansi (1.5.0)
|
|
29
30
|
benchmark-ips (2.7.2)
|
|
30
31
|
builder (3.2.3)
|
|
31
|
-
ffi (1.
|
|
32
|
+
ffi (1.10.0)
|
|
32
33
|
ffi_gen (1.2.0)
|
|
33
34
|
ffi (~> 1.0)
|
|
34
35
|
htmlentities (4.3.4)
|
|
35
|
-
mimemagic (0.3.
|
|
36
|
-
mini_portile2 (2.
|
|
36
|
+
mimemagic (0.3.3)
|
|
37
|
+
mini_portile2 (2.4.0)
|
|
37
38
|
minitest (5.11.3)
|
|
38
|
-
minitest-reporters (1.
|
|
39
|
+
minitest-reporters (1.3.6)
|
|
39
40
|
ansi
|
|
40
41
|
builder
|
|
41
42
|
minitest (>= 5.0)
|
|
42
43
|
ruby-progressbar
|
|
43
|
-
nokogiri (1.
|
|
44
|
-
mini_portile2 (~> 2.
|
|
45
|
-
rake (12.3.
|
|
46
|
-
ruby-progressbar (1.
|
|
47
|
-
rubyzip (1.2.
|
|
48
|
-
write_xlsx (0.85.
|
|
44
|
+
nokogiri (1.10.1)
|
|
45
|
+
mini_portile2 (~> 2.4.0)
|
|
46
|
+
rake (12.3.2)
|
|
47
|
+
ruby-progressbar (1.10.0)
|
|
48
|
+
rubyzip (1.2.2)
|
|
49
|
+
write_xlsx (0.85.5)
|
|
49
50
|
rubyzip (>= 1.0.0)
|
|
50
51
|
zip-zip
|
|
52
|
+
xlsxtream (2.1.0)
|
|
53
|
+
zip_tricks (~> 4.5)
|
|
51
54
|
zip-zip (0.3)
|
|
52
55
|
rubyzip (>= 1.0.0)
|
|
56
|
+
zip_tricks (4.7.1)
|
|
53
57
|
|
|
54
58
|
PLATFORMS
|
|
55
59
|
ruby
|
|
@@ -63,8 +67,9 @@ DEPENDENCIES
|
|
|
63
67
|
minitest-reporters
|
|
64
68
|
process_memory!
|
|
65
69
|
rake
|
|
66
|
-
roo (= 2.
|
|
70
|
+
roo (= 2.8.1)!
|
|
67
71
|
write_xlsx
|
|
72
|
+
xlsxtream
|
|
68
73
|
|
|
69
74
|
BUNDLED WITH
|
|
70
|
-
1.
|
|
75
|
+
1.17.2
|
data/Makefile
CHANGED
data/README.md
CHANGED
|
@@ -20,17 +20,19 @@ worksheet.set_column(1, 1, 20, price)
|
|
|
20
20
|
date_format = workbook.number_format("[$-409]m/d/yy h:mm AM/PM;@")
|
|
21
21
|
worksheet.set_column(2, 2, 20, date_format)
|
|
22
22
|
|
|
23
|
-
worksheet.
|
|
23
|
+
worksheet.append_row(["message", "price", "date"], bold)
|
|
24
24
|
|
|
25
25
|
for i in 1..1000
|
|
26
|
-
worksheet.
|
|
26
|
+
worksheet.append_row(["Hello", (rand * 10_000_000).round(2), Time.now])
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
worksheet.
|
|
29
|
+
worksheet.append_row(["Sum", FastExcel::Formula.new("SUM(B2:B1001)")], bold)
|
|
30
30
|
|
|
31
31
|
workbook.close
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
See [more examples](https://github.com/Paxa/fast_excel/tree/master/examples)
|
|
35
|
+
|
|
34
36
|
This repository and gem contain sources of [libxlsxwriter](https://github.com/jmcnamara/libxlsxwriter)
|
|
35
37
|
|
|
36
38
|
## Benchmarks
|
|
@@ -69,6 +71,29 @@ Or
|
|
|
69
71
|
gem install fast_excel
|
|
70
72
|
```
|
|
71
73
|
|
|
74
|
+
## Column Auto Width
|
|
75
|
+
|
|
76
|
+
Column authwidth only works for string values, because numbers may have custom formatting
|
|
77
|
+
|
|
78
|
+
Enabling column auto widths will slow down writing string values for about 15-25%
|
|
79
|
+
|
|
80
|
+
```ruby
|
|
81
|
+
require 'fast_excel'
|
|
82
|
+
|
|
83
|
+
workbook = FastExcel.open(constant_memory: true)
|
|
84
|
+
|
|
85
|
+
worksheet = workbook.add_worksheet
|
|
86
|
+
worksheet.auto_width = true
|
|
87
|
+
|
|
88
|
+
worksheet.append_row(["some text", "some longer text for example"])
|
|
89
|
+
|
|
90
|
+
content = workbook.read_string
|
|
91
|
+
File.open('./some_file.xlsx', 'wb') {|f| f.write(content) }
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+

|
|
95
|
+
|
|
96
|
+
|
|
72
97
|
## API
|
|
73
98
|
|
|
74
99
|
This gem is FFI binding for libxlsxwriter C library with some syntax sugar. All original functions is avaliable, for example:
|
|
@@ -93,3 +118,5 @@ Helper Methods:
|
|
|
93
118
|
* `worksheet.write_row(row_num, array_of_mixed_value, formats = nil)` - write values one by one, detecting type automatically. `formats` can be array, or Format object or nil
|
|
94
119
|
* `format.font_family` - alias for `format.font_name`
|
|
95
120
|
* `workbook.default_format.font_size` - set it to 0 if you want to use default font size (that what user set in Excel settings)
|
|
121
|
+
* `worksheet.write_row(num, values_array, formats = nil)` - Write row of values
|
|
122
|
+
* `worksheet.append_row(values_array, formats = nil)` - Append row of values ot the bottom of worksheet
|
data/Rakefile
CHANGED
data/benchmarks/1k_rows.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
require_relative 'init'
|
|
2
2
|
|
|
3
|
-
HEADERS = ["id", "name", "age", "date"]
|
|
3
|
+
HEADERS = ["id", "name", "age", "date"].freeze
|
|
4
4
|
|
|
5
5
|
DATA = []
|
|
6
6
|
1000.times do |n|
|
|
@@ -8,15 +8,15 @@ DATA = []
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
Benchmark.ips do |x|
|
|
11
|
-
x.config(time: 10, warmup:
|
|
11
|
+
x.config(time: 10, warmup: 40)
|
|
12
12
|
|
|
13
13
|
x.report("FastExcel") do
|
|
14
14
|
workbook = FastExcel.open(constant_memory: true)
|
|
15
15
|
worksheet = workbook.add_worksheet("benchmark")
|
|
16
16
|
|
|
17
17
|
worksheet.write_row(0, HEADERS)
|
|
18
|
-
DATA.
|
|
19
|
-
worksheet.
|
|
18
|
+
DATA.each do |row|
|
|
19
|
+
worksheet.append_row(row)
|
|
20
20
|
end
|
|
21
21
|
workbook.read_string
|
|
22
22
|
end
|
|
@@ -55,5 +55,18 @@ Benchmark.ips do |x|
|
|
|
55
55
|
File.delete(filename)
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
+
x.report("xlsxtream") do
|
|
59
|
+
filename = "#{Dir.mktmpdir}/xlsxtream.xlsx"
|
|
60
|
+
|
|
61
|
+
Xlsxtream::Workbook.open(filename) do |xlsx|
|
|
62
|
+
xlsx.write_worksheet do |sheet|
|
|
63
|
+
sheet << HEADERS
|
|
64
|
+
DATA.each do |row|
|
|
65
|
+
sheet << row
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
58
71
|
x.compare!
|
|
59
72
|
end
|
data/benchmarks/20k_rows.rb
CHANGED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require_relative 'init'
|
|
2
|
+
|
|
3
|
+
HEADERS = ["id", "name", "age", "date"]
|
|
4
|
+
|
|
5
|
+
DATA = []
|
|
6
|
+
1000.times do |n|
|
|
7
|
+
DATA << [n, "String string #{n}", (n * rand * 10).round, Time.at(n * 1000 + 1492922688)]
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
Benchmark.ips do |x|
|
|
11
|
+
x.config(time: 10, warmup: 2)
|
|
12
|
+
|
|
13
|
+
x.report("Normal") do
|
|
14
|
+
workbook = FastExcel.open(constant_memory: true)
|
|
15
|
+
worksheet = workbook.add_worksheet("benchmark")
|
|
16
|
+
|
|
17
|
+
worksheet.write_row(0, HEADERS)
|
|
18
|
+
DATA.each_with_index do |row, i|
|
|
19
|
+
worksheet.write_row(i + 1, row)
|
|
20
|
+
end
|
|
21
|
+
workbook.read_string
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
x.report("With auto_width") do
|
|
25
|
+
workbook = FastExcel.open(constant_memory: true)
|
|
26
|
+
worksheet = workbook.add_worksheet("benchmark")
|
|
27
|
+
worksheet.auto_width = true
|
|
28
|
+
|
|
29
|
+
worksheet.write_row(0, HEADERS)
|
|
30
|
+
DATA.each_with_index do |row, i|
|
|
31
|
+
worksheet.write_row(i + 1, row)
|
|
32
|
+
end
|
|
33
|
+
workbook.read_string
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
x.compare!
|
|
37
|
+
end
|
data/benchmarks/init.rb
CHANGED
|
@@ -8,10 +8,9 @@ require_relative '../lib/fast_excel'
|
|
|
8
8
|
require "benchmark/ips"
|
|
9
9
|
require 'axlsx'
|
|
10
10
|
require 'write_xlsx'
|
|
11
|
+
require 'xlsxtream'
|
|
11
12
|
require 'process_memory'
|
|
12
13
|
|
|
13
|
-
require_relative 'init'
|
|
14
|
-
|
|
15
14
|
def write_fast_excel_20k
|
|
16
15
|
workbook = FastExcel.open(constant_memory: true)
|
|
17
16
|
worksheet = workbook.add_worksheet("benchmark")
|
|
@@ -57,3 +56,16 @@ def write_axlsx_20k
|
|
|
57
56
|
File.delete(filename)
|
|
58
57
|
end
|
|
59
58
|
end
|
|
59
|
+
|
|
60
|
+
def write_xlsxtream_20k
|
|
61
|
+
filename = "#{Dir.mktmpdir}/xlsxtream.xlsx"
|
|
62
|
+
|
|
63
|
+
Xlsxtream::Workbook.open(filename) do |xlsx|
|
|
64
|
+
xlsx.write_worksheet do |sheet|
|
|
65
|
+
sheet << HEADERS
|
|
66
|
+
DATA.each do |row|
|
|
67
|
+
sheet << row
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
data/benchmarks/memory.rb
CHANGED
|
@@ -11,6 +11,7 @@ puts "warm up..."
|
|
|
11
11
|
write_fast_excel_20k
|
|
12
12
|
write_axlsx_20k
|
|
13
13
|
write_xlsx_20k
|
|
14
|
+
write_xlsxtream_20k
|
|
14
15
|
|
|
15
16
|
DATA.clear
|
|
16
17
|
50_000.times do |n|
|
|
@@ -47,3 +48,10 @@ sleep 5
|
|
|
47
48
|
measure_memory("write_xlsx") do
|
|
48
49
|
write_xlsx_20k
|
|
49
50
|
end
|
|
51
|
+
|
|
52
|
+
GC.start
|
|
53
|
+
sleep 5
|
|
54
|
+
|
|
55
|
+
measure_memory("xlsxtream") do
|
|
56
|
+
write_xlsxtream_20k
|
|
57
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require_relative '../lib/fast_excel'
|
|
2
|
+
require 'ruby-prof'
|
|
3
|
+
|
|
4
|
+
DATA = []
|
|
5
|
+
1000.times do |n|
|
|
6
|
+
DATA << [n, "String string #{n}", (n * rand * 10).round, Time.at(n * 1000 + 1492922688)]
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
RubyProf.start
|
|
10
|
+
|
|
11
|
+
100.times do
|
|
12
|
+
workbook = FastExcel.open(constant_memory: true)
|
|
13
|
+
worksheet = workbook.add_worksheet("benchmark")
|
|
14
|
+
worksheet.auto_width = true
|
|
15
|
+
|
|
16
|
+
DATA.each_with_index do |row, i|
|
|
17
|
+
worksheet.write_row(i + 1, row)
|
|
18
|
+
end
|
|
19
|
+
workbook.read_string
|
|
20
|
+
|
|
21
|
+
print '.'
|
|
22
|
+
end
|
|
23
|
+
result = RubyProf.stop
|
|
24
|
+
|
|
25
|
+
# print a flat profile to text
|
|
26
|
+
printer = RubyProf::FlatPrinter.new(result)
|
|
27
|
+
printer.print(STDOUT)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
require_relative '../lib/fast_excel'
|
|
2
|
+
require "benchmark/memory"
|
|
3
|
+
require "benchmark/ips"
|
|
4
|
+
|
|
5
|
+
DATA = []
|
|
6
|
+
1000.times do |n|
|
|
7
|
+
DATA << [n, "String string #{n}", (n * rand * 10).round, Time.at(n * 1000 + 1492922688)]
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
5.times do
|
|
11
|
+
workbook = FastExcel.open(constant_memory: true)
|
|
12
|
+
worksheet = workbook.add_worksheet("benchmark")
|
|
13
|
+
|
|
14
|
+
DATA.each_with_index do |row, row_num|
|
|
15
|
+
row.each_with_index do |val, cell_num|
|
|
16
|
+
worksheet.write_value(row_num + 1, cell_num + 1, val)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
workbook.read_string
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
workbook = FastExcel.open(constant_memory: true)
|
|
24
|
+
worksheet = workbook.add_worksheet("benchmark")
|
|
25
|
+
|
|
26
|
+
DATA.each_with_index do |row, row_num|
|
|
27
|
+
row.each_with_index do |val, cell_num|
|
|
28
|
+
worksheet.write_value(row_num + 1, cell_num + 1, val)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
workbook.read_string
|
|
32
|
+
|
|
33
|
+
Benchmark.ips do |x|
|
|
34
|
+
#x.config(time: 10, warmup: 2)
|
|
35
|
+
|
|
36
|
+
x.report("Normal") do
|
|
37
|
+
workbook = FastExcel.open(constant_memory: true)
|
|
38
|
+
worksheet = workbook.add_worksheet("benchmark")
|
|
39
|
+
|
|
40
|
+
DATA.each_with_index do |row, row_num|
|
|
41
|
+
row.each_with_index do |val, cell_num|
|
|
42
|
+
worksheet.write_value(row_num + 1, cell_num + 1, val)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
workbook.read_string
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
x.report("Auto-width") do
|
|
49
|
+
workbook = FastExcel.open(constant_memory: true)
|
|
50
|
+
worksheet = workbook.add_worksheet("benchmark")
|
|
51
|
+
worksheet.auto_width = true
|
|
52
|
+
|
|
53
|
+
DATA.each_with_index do |row, row_num|
|
|
54
|
+
row.each_with_index do |val, cell_num|
|
|
55
|
+
worksheet.write_value(row_num + 1, cell_num + 1, val)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
workbook.read_string
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
x.compare!
|
|
62
|
+
end
|