coloryze 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +0 -0
- data/bin/coloryze +16 -0
- data/lib/color.rb +16 -0
- data/lib/color/coloryze.rb +225 -0
- data/lib/color/full_palette.rb +158 -0
- data/lib/color/hsb.rb +107 -0
- data/lib/color/palette.rb +92 -0
- data/lib/color/rgb.rb +133 -0
- data/lib/color/sample_palette.rb +78 -0
- data/lib/color/scheme.rb +124 -0
- data/lib/color/util.rb +30 -0
- data/test/color/coloryze_test.rb +90 -0
- data/test/color/full_palette_test.rb +153 -0
- data/test/color/hsb_test.rb +54 -0
- data/test/color/rgb_test.rb +104 -0
- data/test/color/sample_palette_test.rb +64 -0
- data/test/color/scheme_test.rb +95 -0
- data/test/color/util_test.rb +33 -0
- metadata +105 -0
@@ -0,0 +1,90 @@
|
|
1
|
+
#!/usr/bin/ruby -w
|
2
|
+
# -*- ruby -*-
|
3
|
+
|
4
|
+
require 'rubyunit'
|
5
|
+
require 'color/coloryze'
|
6
|
+
require 'stringio'
|
7
|
+
require 'riel'
|
8
|
+
|
9
|
+
Log.verbose = true
|
10
|
+
|
11
|
+
class ColoryzeTestCase < RUNIT::TestCase
|
12
|
+
include Loggable
|
13
|
+
|
14
|
+
def assert_iostring(ary, io, verbose = false)
|
15
|
+
str = io.string
|
16
|
+
info str if verbose
|
17
|
+
assert_equals ary.collect { |x| x + "\n" }.join(''), io.string
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_usage
|
21
|
+
io = StringIO.new
|
22
|
+
col = Coloryze.new(%w{ --help }, io)
|
23
|
+
str = io.string
|
24
|
+
|
25
|
+
exp = Array.new
|
26
|
+
exp << "usage: coloryze [options]"
|
27
|
+
exp << "options:"
|
28
|
+
exp << " --help, --usage display this help message"
|
29
|
+
exp << " --monochromatic=HUE display one hue"
|
30
|
+
exp << " --split-complementary=HUE,DEG display hue and two complements near 180 degrees"
|
31
|
+
exp << " --double-complementary=HUE,HUE display two hues and their two complements"
|
32
|
+
exp << " --complementary=HUE display hues at 180 degrees from each other"
|
33
|
+
exp << " --analogous=HUE,DEG display three hues near each other"
|
34
|
+
exp << " --triadic=HUE display three equidistant hues"
|
35
|
+
exp << " --hues=START,END,INT display colors for the generated hues (no scheme applied)"
|
36
|
+
exp << " --sample=NUM select only NUM colors per hue, chosen randomly"
|
37
|
+
exp << " --total-sample=NUM select only NUM total colors, chosen randomly"
|
38
|
+
exp << " --saturation=START,END,INT set the saturation start, end, and interval"
|
39
|
+
exp << " --brightness=START,END,INT set the brightness start, end, and interval"
|
40
|
+
exp << " --output=TYPE set the output type, \"text\" (the default) or \"html\""
|
41
|
+
exp << " --verbose produce debugging output"
|
42
|
+
exp << " --version display the version and exit"
|
43
|
+
|
44
|
+
assert_iostring exp, io
|
45
|
+
|
46
|
+
assert_equal 0, col.exit_status
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_version
|
50
|
+
io = StringIO.new
|
51
|
+
col = Coloryze.new(%w{ --version }, io)
|
52
|
+
str = io.string
|
53
|
+
|
54
|
+
exp = Array.new
|
55
|
+
exp << "coloryze, version 2.0.0"
|
56
|
+
exp << "Written by Jeff Pace (jpace@incava.org)"
|
57
|
+
exp << "Released under the GNU Public License."
|
58
|
+
|
59
|
+
assert_iostring exp, io
|
60
|
+
|
61
|
+
assert_equal 0, col.exit_status
|
62
|
+
end
|
63
|
+
|
64
|
+
def do_invalid_scheme_arguments_test(scheme, required, *options)
|
65
|
+
([ '', '='] | options).each do |option|
|
66
|
+
outio = StringIO.new
|
67
|
+
errio = StringIO.new
|
68
|
+
col = Coloryze.new([ '--' + scheme + option ], outio, errio)
|
69
|
+
str = errio.string
|
70
|
+
|
71
|
+
exp = Array.new
|
72
|
+
exp << "error: scheme '#{scheme}' requires #{required}"
|
73
|
+
|
74
|
+
assert_iostring exp, errio
|
75
|
+
|
76
|
+
assert_equal 1, col.exit_status
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_invalid_scheme_arguments
|
81
|
+
do_invalid_scheme_arguments_test 'monochromatic', 'hue', '=31,34', '=,aa'
|
82
|
+
do_invalid_scheme_arguments_test 'split-complementary', 'hue,deg', '=123', '=123,321,23', '=123,01aabc'
|
83
|
+
do_invalid_scheme_arguments_test 'double-complementary', 'hue,hue', '=11', '=123,321,23'
|
84
|
+
do_invalid_scheme_arguments_test 'complementary', 'hue', '=12,31'
|
85
|
+
do_invalid_scheme_arguments_test 'analogous', 'hue,deg', '=41', '=,43', '=1,2,3', '=23,aabbcc'
|
86
|
+
do_invalid_scheme_arguments_test 'triadic', 'hue', '=1,2', '=2,3,4,5', '=123,234,'
|
87
|
+
do_invalid_scheme_arguments_test 'hues', 'start,end,int', '=1,2', '=1,2,3,4'
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
#!/usr/bin/ruby -w
|
2
|
+
# -*- ruby -*-
|
3
|
+
|
4
|
+
require 'rubyunit'
|
5
|
+
require 'color/hsb'
|
6
|
+
require 'color/rgb'
|
7
|
+
require 'color/scheme'
|
8
|
+
require 'color/full_palette'
|
9
|
+
require 'stringio'
|
10
|
+
require 'riel'
|
11
|
+
|
12
|
+
|
13
|
+
class FullPaletteTestCase < RUNIT::TestCase
|
14
|
+
include Loggable
|
15
|
+
|
16
|
+
def assert_iostring(ary, io, verbose = false)
|
17
|
+
str = io.string
|
18
|
+
info str if verbose
|
19
|
+
assert_equals ary.collect { |x| x + "\n" }.join(''), io.string
|
20
|
+
end
|
21
|
+
|
22
|
+
def xtest_as_text
|
23
|
+
hues = [ [ 123, 23, 12 ] ].collect { |vals| Color::RGB.new(*vals).to_hsb.hue }
|
24
|
+
log "hues: #{hues.inspect}"
|
25
|
+
args = {
|
26
|
+
:background => nil
|
27
|
+
}
|
28
|
+
fp = Color::FullPalette.new(hues, args)
|
29
|
+
|
30
|
+
str = StringIO.new
|
31
|
+
fp.print_as_text(str)
|
32
|
+
# puts str.string
|
33
|
+
end
|
34
|
+
|
35
|
+
def xtest_as_html
|
36
|
+
hues = [ [ 123, 23, 12 ] ].collect { |vals| Color::RGB.new(*vals).to_hsb.hue }
|
37
|
+
log "hues: #{hues.inspect}"
|
38
|
+
background = nil
|
39
|
+
args = {
|
40
|
+
:background => nil
|
41
|
+
}
|
42
|
+
fp = Color::FullPalette.new(hues, args)
|
43
|
+
|
44
|
+
str = StringIO.new
|
45
|
+
fp.print_as_html(str)
|
46
|
+
# puts str.string
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_monochromatic
|
50
|
+
scheme = Color::Monochromatic.new(123)
|
51
|
+
info "scheme: #{scheme}"
|
52
|
+
|
53
|
+
hues = scheme.hues
|
54
|
+
info "hues: #{hues}"
|
55
|
+
|
56
|
+
assert_equal [ 123 ], scheme.hues
|
57
|
+
|
58
|
+
params = {
|
59
|
+
:saturation_start => 20,
|
60
|
+
:saturation_end => 25,
|
61
|
+
:saturation_step => 5,
|
62
|
+
:brightness_start => 10,
|
63
|
+
:brightness_end => 100,
|
64
|
+
:brightness_step => 50,
|
65
|
+
}
|
66
|
+
|
67
|
+
palette = Color::FullPalette.new(hues, params)
|
68
|
+
info "palette: #{palette}"
|
69
|
+
|
70
|
+
info "palette.colors: #{palette.colors}"
|
71
|
+
|
72
|
+
exp_colors = [
|
73
|
+
Color::HSB.new(123, 0.2, 0.1),
|
74
|
+
Color::HSB.new(123, 0.2, 0.6),
|
75
|
+
Color::HSB.new(123, 0.25, 0.1),
|
76
|
+
Color::HSB.new(123, 0.25, 0.6),
|
77
|
+
]
|
78
|
+
|
79
|
+
assert_equal exp_colors, palette.colors
|
80
|
+
|
81
|
+
io = StringIO.new
|
82
|
+
palette.print_as_html(io)
|
83
|
+
str = io.string
|
84
|
+
|
85
|
+
exp = Array.new
|
86
|
+
|
87
|
+
exp << "<div style=\"background-color: FFFFFF;\">"
|
88
|
+
exp << " <table width=\"100%\">"
|
89
|
+
exp << " <tr>"
|
90
|
+
exp << " <td style=\"color: #000000;\">hue 123 °</td>"
|
91
|
+
exp << " </tr>"
|
92
|
+
exp << " <tr>"
|
93
|
+
exp << " <td>"
|
94
|
+
exp << " <table width=\"100%\">"
|
95
|
+
exp << " <tr>"
|
96
|
+
exp << " <td colspan=2>"
|
97
|
+
exp << " saturation: 0.2"
|
98
|
+
exp << " </td>"
|
99
|
+
exp << " </tr>"
|
100
|
+
exp << " <tr>"
|
101
|
+
exp << " <td style=\"width: 50%; vertical-align: bottom; border:2px outset; border-collapse:collapse;\">"
|
102
|
+
exp << " <table width=\"100%\" border=\"0\" border-width=\"4px\">"
|
103
|
+
exp << " <tr><td>#7a997b; b: 0.60</td></tr"
|
104
|
+
exp << " <tr><td style=\"background-color: #7a997b; vertical-align: bottom;\"><br> </td></tr>"
|
105
|
+
exp << " </table>"
|
106
|
+
exp << " </td>"
|
107
|
+
exp << " <td style=\"width: 50%; vertical-align: bottom; border:2px outset; border-collapse:collapse;\">"
|
108
|
+
exp << " <table width=\"100%\" border=\"0\" border-width=\"4px\">"
|
109
|
+
exp << " <tr><td>#141914; b: 0.10</td></tr"
|
110
|
+
exp << " <tr><td style=\"background-color: #141914; vertical-align: bottom;\"><br> </td></tr>"
|
111
|
+
exp << " </table>"
|
112
|
+
exp << " </td>"
|
113
|
+
exp << " </tr>"
|
114
|
+
exp << " </table>"
|
115
|
+
exp << " </td>"
|
116
|
+
exp << " </tr>"
|
117
|
+
exp << " <tr>"
|
118
|
+
exp << " <td>"
|
119
|
+
exp << " <table width=\"100%\">"
|
120
|
+
exp << " <tr>"
|
121
|
+
exp << " <td colspan=2>"
|
122
|
+
exp << " saturation: 0.25"
|
123
|
+
exp << " </td>"
|
124
|
+
exp << " </tr>"
|
125
|
+
exp << " <tr>"
|
126
|
+
exp << " <td style=\"width: 50%; vertical-align: bottom; border:2px outset; border-collapse:collapse;\">"
|
127
|
+
exp << " <table width=\"100%\" border=\"0\" border-width=\"4px\">"
|
128
|
+
exp << " <tr><td>#729974; b: 0.60</td></tr"
|
129
|
+
exp << " <tr><td style=\"background-color: #729974; vertical-align: bottom;\"><br> </td></tr>"
|
130
|
+
exp << " </table>"
|
131
|
+
exp << " </td>"
|
132
|
+
exp << " <td style=\"width: 50%; vertical-align: bottom; border:2px outset; border-collapse:collapse;\">"
|
133
|
+
exp << " <table width=\"100%\" border=\"0\" border-width=\"4px\">"
|
134
|
+
exp << " <tr><td>#131913; b: 0.10</td></tr"
|
135
|
+
exp << " <tr><td style=\"background-color: #131913; vertical-align: bottom;\"><br> </td></tr>"
|
136
|
+
exp << " </table>"
|
137
|
+
exp << " </td>"
|
138
|
+
exp << " </tr>"
|
139
|
+
exp << " </table>"
|
140
|
+
exp << " </td>"
|
141
|
+
exp << " </tr>"
|
142
|
+
exp << " </table>"
|
143
|
+
exp << "</div>"
|
144
|
+
|
145
|
+
puts io.string
|
146
|
+
assert_iostring exp, io
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
#$$$ todo: add tests for schemes
|
151
|
+
|
152
|
+
|
153
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
#!/usr/bin/ruby -w
|
2
|
+
# -*- ruby -*-
|
3
|
+
|
4
|
+
require 'rubyunit'
|
5
|
+
require 'color/hsb'
|
6
|
+
require 'color/rgb'
|
7
|
+
require 'riel'
|
8
|
+
|
9
|
+
|
10
|
+
class HSBTestCase < RUNIT::TestCase
|
11
|
+
include Loggable
|
12
|
+
|
13
|
+
def do_initialize_test(h, s, b)
|
14
|
+
log "h: #{h}; s: #{s}; b: #{b}"
|
15
|
+
hsb = Color::HSB.new(h, s, b)
|
16
|
+
log "hsb: #{hsb}"
|
17
|
+
assert_equals h, hsb.hue
|
18
|
+
assert_equals s, hsb.saturation
|
19
|
+
assert_equals b, hsb.brightness
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_initialize
|
23
|
+
do_initialize_test(0, 0, 0)
|
24
|
+
do_initialize_test(0, 0, 1)
|
25
|
+
do_initialize_test(0, 0, 0.5)
|
26
|
+
do_initialize_test(0, 0, 0.25)
|
27
|
+
|
28
|
+
[ -1, -0.1, 1.01, 10 ].each do |val|
|
29
|
+
[ [ val, 0 ], [0, val ] ].each do |s, b|
|
30
|
+
assert_raise RuntimeError do
|
31
|
+
do_initialize_test(0, s, b)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
do_initialize_test(70, 1.0, 1.0)
|
37
|
+
end
|
38
|
+
|
39
|
+
def do_to_rgb_test(rgb_expected, h, s, b)
|
40
|
+
log "h: #{h}; s: #{s}; b: #{b}"
|
41
|
+
rgb_actual = Color::HSB.new(h, s, b).to_rgb
|
42
|
+
log sprintf("rgb: exp: #{rgb_expected.inspect} (%06x); act: #{rgb_actual} (%06x)\n", rgb_expected, rgb_actual.to_i)
|
43
|
+
assert_equals rgb_expected, rgb_actual.to_i, "h: #{h}; s: #{s}; b: #{b}"
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_to_rgb
|
47
|
+
do_to_rgb_test(0, 0, 0, 0)
|
48
|
+
do_to_rgb_test(0xffffff, 0, 0, 1)
|
49
|
+
do_to_rgb_test(0x7f7f7f, 0, 0, 0.5)
|
50
|
+
do_to_rgb_test(0x3f3f3f, 0, 0, 0.25)
|
51
|
+
do_to_rgb_test(0xd4ff00, 70, 1.0, 1.0)
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
#!/usr/bin/ruby -w
|
2
|
+
# -*- ruby -*-
|
3
|
+
|
4
|
+
require 'rubyunit'
|
5
|
+
require 'color/rgb'
|
6
|
+
require 'color/hsb'
|
7
|
+
require 'riel'
|
8
|
+
|
9
|
+
|
10
|
+
class RGBTestCase < RUNIT::TestCase
|
11
|
+
include Color, Loggable
|
12
|
+
|
13
|
+
def do_initialize_from_numbers_test(r, g, b)
|
14
|
+
log "r: #{r}; g: #{g}; b: #{b}"
|
15
|
+
rgb = RGB.new(r, g, b)
|
16
|
+
assert_equals r, rgb.red
|
17
|
+
assert_equals g, rgb.green
|
18
|
+
assert_equals b, rgb.blue
|
19
|
+
end
|
20
|
+
|
21
|
+
def do_initialize_from_rgb_test(str, r, g, b)
|
22
|
+
log "str: #{str}; r: #{r}; g: #{g}; b: #{b}"
|
23
|
+
rgb = RGB.new(str)
|
24
|
+
assert_equals r, rgb.red
|
25
|
+
assert_equals g, rgb.green
|
26
|
+
assert_equals b, rgb.blue
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_initialize_from_numbers
|
30
|
+
do_initialize_from_numbers_test( 0, 0, 0)
|
31
|
+
do_initialize_from_numbers_test( 10, 55, 105)
|
32
|
+
do_initialize_from_numbers_test(115, 100, 210)
|
33
|
+
do_initialize_from_numbers_test(225, 220, 99)
|
34
|
+
do_initialize_from_numbers_test(255, 255, 255)
|
35
|
+
|
36
|
+
[ -1, -0.1, 255.01, 256, 1000 ].each do |val|
|
37
|
+
[ [ val, 0, 0 ], [ 0, val, 0 ], [ 0, 0, val ] ].each do |r, g, b|
|
38
|
+
assert_raise RuntimeError do
|
39
|
+
do_initialize_from_numbers_test(r, g, b)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_initialize_from_rgb
|
47
|
+
do_initialize_from_rgb_test "#000000", 0, 0, 0
|
48
|
+
do_initialize_from_rgb_test "#ffffff", 255, 255, 255
|
49
|
+
do_initialize_from_rgb_test "000000", 0, 0, 0
|
50
|
+
do_initialize_from_rgb_test "ffffff", 255, 255, 255
|
51
|
+
do_initialize_from_rgb_test "aabeef", 170, 190, 239
|
52
|
+
do_initialize_from_rgb_test "9a8b7c", 154, 139, 124
|
53
|
+
|
54
|
+
[ '#0101g1', '0101g1' ].each do |str|
|
55
|
+
assert_raise RuntimeError do
|
56
|
+
do_initialize_from_rgb_test(str, nil, nil, nil)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_equal
|
63
|
+
assert_equal RGB.new(0, 10, 20), RGB.new(0, 10, 20)
|
64
|
+
end
|
65
|
+
|
66
|
+
def do_to_hsb_test(h_exp, s_exp, b_exp, r, g, b)
|
67
|
+
log "r: #{r}; g: #{g}; b: #{b}"
|
68
|
+
hsb_expected = HSB.new(h_exp, s_exp, b_exp)
|
69
|
+
hsb_actual = RGB.new(r, g, b).to_hsb
|
70
|
+
log "hsb: exp: #{hsb_expected}; act: #{hsb_actual}"
|
71
|
+
assert_equals hsb_expected, hsb_actual, "r: #{r}; g: #{g}; b: #{b}"
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_to_hsb
|
75
|
+
# do_to_hsb_test(0, 0, 0, 0, 0, 0)
|
76
|
+
# do_to_hsb_test(210.0, 0.6666667, 0.02352941, 2, 4, 6)
|
77
|
+
|
78
|
+
# from: http://en.wikipedia.org/wiki/HSL_and_HSV
|
79
|
+
tests = [
|
80
|
+
[ 61.4, 0.779, 0.639, 0.628, 0.643, 0.142 ],
|
81
|
+
[ 49.6, 0.946, 0.937, 0.941, 0.785, 0.053 ],
|
82
|
+
].each do |vals|
|
83
|
+
log "vals: #{vals.inspect}"
|
84
|
+
r, g, b = (3 .. 5).collect { |n| vals[n] * 255 }
|
85
|
+
do_to_hsb_test(vals[0], vals[1], vals[2], r, g, b)
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
def do_matches_rgb_string_test(match_expected, val)
|
91
|
+
assert_equals match_expected, !Color::RGB.matches_rgb_string?(val).nil?
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_matches_rgb_string
|
95
|
+
do_matches_rgb_string_test true, "ff88dd"
|
96
|
+
do_matches_rgb_string_test true, "001122"
|
97
|
+
do_matches_rgb_string_test false, "ffd"
|
98
|
+
do_matches_rgb_string_test false, "012"
|
99
|
+
do_matches_rgb_string_test true, "#ff88dd"
|
100
|
+
do_matches_rgb_string_test true, "#001122"
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
#!/usr/bin/ruby -w
|
2
|
+
# -*- ruby -*-
|
3
|
+
|
4
|
+
require 'rubyunit'
|
5
|
+
require 'color/hsb'
|
6
|
+
require 'color/rgb'
|
7
|
+
require 'color/sample_palette'
|
8
|
+
require 'stringio'
|
9
|
+
require 'riel'
|
10
|
+
|
11
|
+
|
12
|
+
class SamplePaletteTestCase < RUNIT::TestCase
|
13
|
+
include Loggable
|
14
|
+
|
15
|
+
def test_total_samples_as_text
|
16
|
+
hues = [
|
17
|
+
[ 123, 23, 12 ],
|
18
|
+
[ 25, 37, 31 ]
|
19
|
+
].collect do |vals|
|
20
|
+
Color::RGB.new(*vals).to_hsb.hue
|
21
|
+
end
|
22
|
+
|
23
|
+
log "hues: #{hues.inspect}"
|
24
|
+
args = {
|
25
|
+
:background => nil,
|
26
|
+
:total_samples => 5,
|
27
|
+
}
|
28
|
+
fp = Color::SamplePalette.new(hues, args)
|
29
|
+
|
30
|
+
str = StringIO.new
|
31
|
+
fp.print_as_text(str)
|
32
|
+
# puts str.string
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_as_text
|
36
|
+
hues = [ [ 123, 23, 12 ] ].collect { |vals| Color::RGB.new(*vals).to_hsb.hue }
|
37
|
+
log "hues: #{hues.inspect}"
|
38
|
+
args = {
|
39
|
+
:background => nil,
|
40
|
+
:total_samples => 4,
|
41
|
+
}
|
42
|
+
fp = Color::SamplePalette.new(hues, args)
|
43
|
+
|
44
|
+
str = StringIO.new
|
45
|
+
fp.print_as_text(str)
|
46
|
+
# puts str.string
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_as_html
|
50
|
+
hues = [ [ 123, 23, 12 ] ].collect { |vals| Color::RGB.new(*vals).to_hsb.hue }
|
51
|
+
log "hues: #{hues.inspect}"
|
52
|
+
background = nil
|
53
|
+
args = {
|
54
|
+
:background => nil,
|
55
|
+
:total_samples => 4,
|
56
|
+
}
|
57
|
+
fp = Color::SamplePalette.new(hues, args)
|
58
|
+
|
59
|
+
str = StringIO.new
|
60
|
+
fp.print_as_html(str)
|
61
|
+
# puts str.string
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|