gettc 1.5 → 1.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 +4 -4
- data/bin/gettc +45 -29
- data/core/lib/gettc.rb +7 -0
- data/core/lib/{topcoder → gettc}/download.rb +53 -45
- data/core/lib/gettc/generate.rb +145 -0
- data/core/lib/{topcoder → gettc}/parse.rb +104 -102
- data/core/lib/{topcoder → gettc}/print.rb +11 -11
- data/core/lib/{topcoder → gettc}/problem.rb +11 -11
- data/core/lib/{topcoder → gettc}/signature.rb +8 -8
- data/core/lib/{topcoder → gettc}/types.rb +34 -17
- data/core/test/{topcoder → gettc}/download_test.rb +6 -6
- data/core/test/gettc/generate_test.rb +31 -0
- data/core/test/{topcoder → gettc}/parse_test.rb +26 -26
- data/core/test/gettc/signature_test.rb +54 -0
- data/core/test/gettc/types_test.rb +24 -0
- data/dist/config.yml +1 -0
- data/dist/include/cpp/engine.rb +32 -32
- data/dist/include/cpp/topcoder +89 -25
- data/dist/include/haskell/TopCoder.hs +72 -53
- data/dist/include/haskell/engine.rb +49 -47
- data/dist/include/java/TopCoder.jar +0 -0
- data/dist/include/java/engine.rb +71 -71
- data/dist/include/python/engine.rb +46 -0
- data/dist/include/python/topcoder/__init__.py +3 -0
- data/dist/include/python/topcoder/errors.py +4 -0
- data/dist/include/python/topcoder/reader.py +160 -0
- data/dist/include/python/topcoder/writer.py +13 -0
- data/dist/template/bin/runner.sh +16 -6
- data/dist/template/solve/cpp/Makefile +9 -5
- data/dist/template/solve/cpp/{name}.cpp +8 -8
- data/dist/template/solve/cpp/{name}Runner.cpp +8 -8
- data/dist/template/solve/haskell/Makefile +9 -5
- data/dist/template/solve/haskell/{name}.hs +1 -1
- data/dist/template/solve/haskell/{name}Runner.hs +5 -5
- data/dist/template/solve/java/Makefile +18 -0
- data/dist/template/solve/java/build.xml +7 -3
- data/dist/template/solve/java/{name}.java +1 -1
- data/dist/template/solve/java/{name}Runner.java +1 -1
- data/dist/template/solve/python/Makefile +27 -0
- data/dist/template/solve/python/{name}.py +4 -0
- data/dist/template/solve/python/{name}Runner.py +27 -0
- data/dist/template/util/check/Makefile +3 -1
- data/dist/usage +5 -0
- metadata +30 -24
- data/Rakefile +0 -41
- data/core/lib/topcoder.rb +0 -3
- data/core/lib/topcoder/generate.rb +0 -131
- data/core/test/topcoder/generate_test.rb +0 -31
- data/core/test/topcoder/signature_test.rb +0 -52
- data/core/test/topcoder/types_test.rb +0 -24
- data/dist/template/solve/cpp/{name}Test.cpp +0 -8
- data/dist/template/solve/haskell/{name}Test.hs +0 -10
- data/dist/template/solve/java/{name}Test.java +0 -8
@@ -1,7 +1,7 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "gettc/problem"
|
2
|
+
require "rdiscount"
|
3
3
|
|
4
|
-
module
|
4
|
+
module Gettc
|
5
5
|
class Problem
|
6
6
|
def to_md
|
7
7
|
out = "# [#{@name}](#{@url})\n"
|
@@ -10,19 +10,19 @@ module TopCoder
|
|
10
10
|
out << "## Statement\n"
|
11
11
|
out << @statement << "\n\n"
|
12
12
|
|
13
|
-
|
13
|
+
unless @definitions.empty? then
|
14
14
|
out << "## Definitions\n"
|
15
15
|
@definitions.each do |k, v| out << "- *#{k}*: `#{v}`\n" end
|
16
16
|
out << "\n"
|
17
17
|
end
|
18
18
|
|
19
|
-
|
19
|
+
unless @notes.empty? then
|
20
20
|
out << "## Notes\n"
|
21
21
|
@notes.each do |note| out << "- #{note}\n" end
|
22
22
|
out << "\n"
|
23
23
|
end
|
24
24
|
|
25
|
-
|
25
|
+
unless @constraints.empty? then
|
26
26
|
out << "## Constraints\n"
|
27
27
|
@constraints.each do |constraint|
|
28
28
|
out << "- #{constraint}\n"
|
@@ -30,18 +30,18 @@ module TopCoder
|
|
30
30
|
out << "\n"
|
31
31
|
end
|
32
32
|
|
33
|
-
|
33
|
+
unless @examples.empty? then
|
34
34
|
out << "## Examples\n"
|
35
35
|
@examples.each_index do |i|
|
36
36
|
example = @examples[i]
|
37
37
|
out << "### Example #{i + 1}\n"
|
38
38
|
out << "#### Input\n<c>"
|
39
|
-
out << example.input.gsub("\n",
|
39
|
+
out << example.input.gsub("\n", "<br />")
|
40
40
|
out << "</c>\n"
|
41
41
|
out << "#### Output\n<c>"
|
42
|
-
out << example.output.gsub("\n",
|
42
|
+
out << example.output.gsub("\n", "<br />")
|
43
43
|
out << "</c>\n"
|
44
|
-
|
44
|
+
unless example.reason.empty? then
|
45
45
|
out << "#### Reason\n#{example.reason}\n\n"
|
46
46
|
end
|
47
47
|
end
|
@@ -53,4 +53,4 @@ module TopCoder
|
|
53
53
|
return markdown.to_html
|
54
54
|
end
|
55
55
|
end
|
56
|
-
end
|
56
|
+
end
|
@@ -1,27 +1,27 @@
|
|
1
|
-
module
|
1
|
+
module Gettc
|
2
2
|
class Case
|
3
3
|
attr_accessor :input, :output, :reason
|
4
4
|
def initialize
|
5
|
-
@input =
|
6
|
-
@output =
|
7
|
-
@reason =
|
5
|
+
@input = ""
|
6
|
+
@output = ""
|
7
|
+
@reason = ""
|
8
8
|
end
|
9
9
|
end
|
10
10
|
class Image
|
11
11
|
attr_accessor :name, :content
|
12
12
|
def initialize
|
13
|
-
@name =
|
14
|
-
@content =
|
13
|
+
@name = ""
|
14
|
+
@content = ""
|
15
15
|
end
|
16
16
|
end
|
17
17
|
class Problem
|
18
18
|
attr_accessor :name, :url, :source, :statement, :definitions, :notes,
|
19
19
|
:constraints, :examples, :systests, :images
|
20
20
|
def initialize
|
21
|
-
@name =
|
22
|
-
@url =
|
23
|
-
@source =
|
24
|
-
@statement =
|
21
|
+
@name = ""
|
22
|
+
@url = ""
|
23
|
+
@source = ""
|
24
|
+
@statement = ""
|
25
25
|
@definitions = { }
|
26
26
|
@notes = []
|
27
27
|
@constraints = []
|
@@ -30,4 +30,4 @@ module TopCoder
|
|
30
30
|
@images = []
|
31
31
|
end
|
32
32
|
end
|
33
|
-
end
|
33
|
+
end
|
@@ -1,18 +1,18 @@
|
|
1
|
-
require
|
1
|
+
require "gettc/types"
|
2
2
|
|
3
|
-
module
|
3
|
+
module Gettc
|
4
4
|
class SignatureError < StandardError
|
5
5
|
end
|
6
6
|
class CannotParseSignature < SignatureError
|
7
7
|
attr_accessor :source
|
8
|
-
def initialize source, msg =
|
8
|
+
def initialize source, msg = "Cannot parse signature"
|
9
9
|
@source = source
|
10
10
|
super "#{msg} (#{source}"
|
11
11
|
end
|
12
12
|
end
|
13
13
|
class InvalidVariableName < SignatureError
|
14
14
|
attr_accessor :name
|
15
|
-
def initialize name, msg =
|
15
|
+
def initialize name, msg = "Invalid variable name"
|
16
16
|
@name = name
|
17
17
|
super "#{msg} (#{name})"
|
18
18
|
end
|
@@ -39,17 +39,17 @@ module TopCoder
|
|
39
39
|
def parse_method_signature str
|
40
40
|
str.strip!
|
41
41
|
sigs = []
|
42
|
-
parts = str.split
|
42
|
+
parts = str.split "("
|
43
43
|
raise CannotParseSignature.new str if parts.size != 2
|
44
44
|
sigs << parse_signature(parts[0])
|
45
45
|
|
46
46
|
str = parts[1]
|
47
|
-
raise CannotParseSignature.new str if str[-1] !=
|
47
|
+
raise CannotParseSignature.new str if str[-1] != ")"
|
48
48
|
str.chop!
|
49
49
|
|
50
|
-
parts = str.split
|
50
|
+
parts = str.split ","
|
51
51
|
parts.each do |sig| sigs << parse_signature(sig) end
|
52
52
|
|
53
53
|
return sigs
|
54
54
|
end
|
55
|
-
end
|
55
|
+
end
|
@@ -1,11 +1,8 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
module TopCoder
|
4
|
-
class TypeError < StandardError
|
5
|
-
end
|
1
|
+
module Gettc
|
2
|
+
TypeError = Class.new StandardError
|
6
3
|
class UnrecognizedType < TypeError
|
7
4
|
attr_accessor :type
|
8
|
-
def initialize type = nil, msg =
|
5
|
+
def initialize type = nil, msg = "Not a valid TopCoder type"
|
9
6
|
@type = type
|
10
7
|
super "#{msg} (#{type})"
|
11
8
|
end
|
@@ -17,6 +14,26 @@ module TopCoder
|
|
17
14
|
def obj?
|
18
15
|
return @is_object
|
19
16
|
end
|
17
|
+
def to_s
|
18
|
+
if self == TInt then
|
19
|
+
return "int"
|
20
|
+
elsif self == TLong then
|
21
|
+
return "long"
|
22
|
+
elsif self == TFloat then
|
23
|
+
return "float"
|
24
|
+
elsif self == TDouble then
|
25
|
+
return "double"
|
26
|
+
elsif self == TChar then
|
27
|
+
return "char"
|
28
|
+
elsif self == TString then
|
29
|
+
return "String"
|
30
|
+
elsif self == TBoolean then
|
31
|
+
return "boolean"
|
32
|
+
elsif is_a? TArray then
|
33
|
+
return subtype.to_s + "[]"
|
34
|
+
end
|
35
|
+
return "unknown"
|
36
|
+
end
|
20
37
|
end
|
21
38
|
TBoolean = Type.new false
|
22
39
|
TInt = Type.new false
|
@@ -28,11 +45,11 @@ module TopCoder
|
|
28
45
|
class TArray < Type
|
29
46
|
attr_accessor :subtype
|
30
47
|
def initialize subtype
|
31
|
-
raise UnrecognizedType.new subtype
|
48
|
+
raise UnrecognizedType.new subtype unless subtype.is_a? Type
|
32
49
|
@subtype = subtype
|
33
50
|
end
|
34
51
|
def == ary
|
35
|
-
return false
|
52
|
+
return false unless ary.is_a? TArray
|
36
53
|
return @subtype == ary.subtype
|
37
54
|
end
|
38
55
|
def obj?
|
@@ -40,23 +57,23 @@ module TopCoder
|
|
40
57
|
end
|
41
58
|
end
|
42
59
|
def parse_type str
|
43
|
-
return TArray.new parse_type str[0 .. -3] if str[-2 .. -1] ==
|
60
|
+
return TArray.new parse_type str[0 .. -3] if str[-2 .. -1] == "[]"
|
44
61
|
case str
|
45
|
-
when
|
62
|
+
when "boolean"
|
46
63
|
return TBoolean
|
47
|
-
when
|
64
|
+
when "int"
|
48
65
|
return TInt
|
49
|
-
when
|
66
|
+
when "long"
|
50
67
|
return TLong
|
51
|
-
when
|
68
|
+
when "float"
|
52
69
|
return TFloat
|
53
|
-
when
|
70
|
+
when "double"
|
54
71
|
return TDouble
|
55
|
-
when
|
72
|
+
when "char"
|
56
73
|
return TChar
|
57
|
-
when
|
74
|
+
when "String"
|
58
75
|
return TString
|
59
76
|
end
|
60
77
|
raise UnrecognizedType.new str
|
61
78
|
end
|
62
|
-
end
|
79
|
+
end
|
@@ -1,14 +1,14 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "test/unit"
|
2
|
+
require "topcoder/download"
|
3
3
|
include TopCoder
|
4
4
|
|
5
5
|
class DownloadTest < Test::Unit::TestCase
|
6
6
|
def setup
|
7
|
-
@account = Account.new
|
7
|
+
@account = Account.new "gettc", "algorithm"
|
8
8
|
end
|
9
9
|
def test_wrong_account
|
10
10
|
assert_raises LoginFailed do
|
11
|
-
Downloader.new Account.new
|
11
|
+
Downloader.new Account.new "username", "password"
|
12
12
|
end
|
13
13
|
end
|
14
14
|
def test_wrong_id
|
@@ -23,7 +23,7 @@ class DownloadTest < Test::Unit::TestCase
|
|
23
23
|
3.times do
|
24
24
|
id = ids[rand ids.size]
|
25
25
|
html = downloader.download_problem id
|
26
|
-
assert_match
|
26
|
+
assert_match "<h3>Problem Statement</h3>", html
|
27
27
|
end
|
28
28
|
end
|
29
|
-
end
|
29
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require "fileutils"
|
3
|
+
require "tmpdir"
|
4
|
+
|
5
|
+
require "topcoder/generate"
|
6
|
+
include TopCoder
|
7
|
+
|
8
|
+
class GenerateTest < Test::Unit::TestCase
|
9
|
+
def setup
|
10
|
+
@source_d = File.join File.dirname(__FILE__), "../../dist/template"
|
11
|
+
@target_d = File.join Dir.tmpdir, "gettc"
|
12
|
+
FileUtils.mkdir @target_d unless File.directory? @target_d
|
13
|
+
@generator = Generator.new @source_d, @target_d
|
14
|
+
end
|
15
|
+
def test_initialize
|
16
|
+
assert_raise SourceDirNotExist do
|
17
|
+
Generator.new "this_directory_must_not_exist", @target_d
|
18
|
+
end
|
19
|
+
assert_raise TargetDirNotExist do
|
20
|
+
Generator.new @source_d, "this_directory_must_not_exist"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
def test_prob_dir_exists
|
24
|
+
prob = Problem.new
|
25
|
+
prob.name = "JustATest"
|
26
|
+
prob_d = File.join @target_d, prob.name
|
27
|
+
FileUtils.mkdir prob_d unless File.exists? prob_d
|
28
|
+
assert_raise ProblemDirExists do @generator.generate prob end
|
29
|
+
FileUtils.rmdir prob_d
|
30
|
+
end
|
31
|
+
end
|
@@ -1,34 +1,34 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "test/unit"
|
2
|
+
require "topcoder/parse"
|
3
3
|
include TopCoder
|
4
4
|
|
5
5
|
class ParseTest < Test::Unit::TestCase
|
6
6
|
def setup
|
7
|
-
downloader = Downloader.new Account.new
|
7
|
+
downloader = Downloader.new Account.new "gettc", "algorithm"
|
8
8
|
@parser = Parser.new downloader
|
9
|
-
@
|
9
|
+
@data_d = File.join File.dirname(__FILE__), "../../temp/download_problem_statement"
|
10
10
|
end
|
11
11
|
def get_problem_raw prob
|
12
|
-
return File.read File.join @
|
12
|
+
return File.read File.join @data_d, prob + ".htm"
|
13
13
|
end
|
14
14
|
def test_indexes
|
15
|
-
assert_equal [0, 3], @parser.indexes(
|
16
|
-
assert_equal nil, @parser.indexes(
|
15
|
+
assert_equal [0, 3], @parser.indexes("abcde", "bc")
|
16
|
+
assert_equal nil, @parser.indexes("abcde", "f")
|
17
17
|
end
|
18
18
|
def test_filter
|
19
|
-
assert_equal
|
20
|
-
assert_equal
|
21
|
-
assert_equal
|
19
|
+
assert_equal "Lorem Ipsum", @parser.filter("<xml> Lorem Ipsum </xml> ")
|
20
|
+
assert_equal "2^(3)", @parser.filter("2<sup>3</sup>")
|
21
|
+
assert_equal "*hi*", @parser.filter(" <b>hi</b>")
|
22
22
|
html = <<-END
|
23
23
|
<img src=
|
24
24
|
"http://www.topcoder.com/contest/problem/CirclesCountry/case1.gif">
|
25
25
|
END
|
26
|
-
assert_equal
|
26
|
+
assert_equal "", @parser.filter(html)
|
27
27
|
end
|
28
28
|
def test_PageNumbers
|
29
|
-
html = get_problem_raw
|
29
|
+
html = get_problem_raw "PageNumbers"
|
30
30
|
prob = @parser.parse html
|
31
|
-
assert_equal
|
31
|
+
assert_equal "PageNumbers", prob.name
|
32
32
|
assert_equal 5, prob.definitions.size
|
33
33
|
assert_equal 1, prob.notes.size
|
34
34
|
assert_equal 1, prob.constraints.size
|
@@ -37,9 +37,9 @@ class ParseTest < Test::Unit::TestCase
|
|
37
37
|
assert_equal 0, prob.images.size
|
38
38
|
end
|
39
39
|
def test_CirclesCountry
|
40
|
-
html = get_problem_raw
|
40
|
+
html = get_problem_raw "CirclesCountry"
|
41
41
|
prob = @parser.parse html
|
42
|
-
assert_equal
|
42
|
+
assert_equal "CirclesCountry", prob.name
|
43
43
|
assert_equal 5, prob.definitions.size
|
44
44
|
assert_equal 0, prob.notes.size
|
45
45
|
assert_equal 7, prob.constraints.size
|
@@ -48,9 +48,9 @@ class ParseTest < Test::Unit::TestCase
|
|
48
48
|
assert_equal 4, prob.images.size
|
49
49
|
end
|
50
50
|
def test_TheTournamentDivOne
|
51
|
-
html = get_problem_raw
|
51
|
+
html = get_problem_raw "TheTournamentDivOne"
|
52
52
|
prob = @parser.parse html
|
53
|
-
assert_equal
|
53
|
+
assert_equal "TheTournamentDivOne", prob.name
|
54
54
|
assert_equal 5, prob.definitions.size
|
55
55
|
assert_equal 0, prob.notes.size
|
56
56
|
assert_equal 4, prob.constraints.size
|
@@ -59,9 +59,9 @@ class ParseTest < Test::Unit::TestCase
|
|
59
59
|
assert_equal 0, prob.images.size
|
60
60
|
end
|
61
61
|
def test_FunnyGames
|
62
|
-
html = get_problem_raw
|
62
|
+
html = get_problem_raw "FunnyGames"
|
63
63
|
prob = @parser.parse html
|
64
|
-
assert_equal
|
64
|
+
assert_equal "FunnyGames", prob.name
|
65
65
|
assert_equal 5, prob.definitions.size
|
66
66
|
assert_equal 0, prob.notes.size
|
67
67
|
assert_equal 6, prob.constraints.size
|
@@ -70,9 +70,9 @@ class ParseTest < Test::Unit::TestCase
|
|
70
70
|
assert_equal 0, prob.images.size
|
71
71
|
end
|
72
72
|
def test_BuildingRoads
|
73
|
-
html = get_problem_raw
|
73
|
+
html = get_problem_raw "BuildingRoads"
|
74
74
|
prob = @parser.parse html
|
75
|
-
assert_equal
|
75
|
+
assert_equal "BuildingRoads", prob.name
|
76
76
|
assert_equal 5, prob.definitions.size
|
77
77
|
assert_equal 2, prob.notes.size
|
78
78
|
assert_equal 10, prob.constraints.size
|
@@ -81,9 +81,9 @@ class ParseTest < Test::Unit::TestCase
|
|
81
81
|
assert_equal 2, prob.images.size
|
82
82
|
end
|
83
83
|
def test_Acronyms
|
84
|
-
html = get_problem_raw
|
84
|
+
html = get_problem_raw "Acronyms"
|
85
85
|
prob = @parser.parse html
|
86
|
-
assert_equal
|
86
|
+
assert_equal "Acronyms", prob.name
|
87
87
|
assert_equal 5, prob.definitions.size
|
88
88
|
assert_equal 1, prob.notes.size
|
89
89
|
assert_equal 8, prob.constraints.size
|
@@ -92,13 +92,13 @@ class ParseTest < Test::Unit::TestCase
|
|
92
92
|
assert_equal 0, prob.images.size
|
93
93
|
end
|
94
94
|
def test_BackyardTrees
|
95
|
-
html = get_problem_raw
|
95
|
+
html = get_problem_raw "BackyardTrees"
|
96
96
|
prob = @parser.parse html
|
97
|
-
assert_equal
|
97
|
+
assert_equal "BackyardTrees", prob.name
|
98
98
|
assert_equal 5, prob.definitions.size
|
99
99
|
assert_equal 0, prob.notes.size
|
100
100
|
assert_equal 4, prob.constraints.size
|
101
101
|
assert_equal 6, prob.examples.size
|
102
102
|
assert_equal 2, prob.images.size
|
103
103
|
end
|
104
|
-
end
|
104
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require "topcoder/signature"
|
3
|
+
include TopCoder
|
4
|
+
|
5
|
+
class SignatureTest < Test::Unit::TestCase
|
6
|
+
def test_parse_signature
|
7
|
+
assert_raise CannotParseSignature do
|
8
|
+
parse_signature "invalid_signature"
|
9
|
+
end
|
10
|
+
assert_raise UnrecognizedType do
|
11
|
+
parse_signature "strange_type name"
|
12
|
+
end
|
13
|
+
assert_raise InvalidVariableName do
|
14
|
+
parse_signature "int not&ok&name"
|
15
|
+
end
|
16
|
+
assert_raise InvalidVariableName do
|
17
|
+
parse_signature "int 0zero"
|
18
|
+
end
|
19
|
+
sig = parse_signature "String[] a_valid_nam3"
|
20
|
+
assert_equal TArray.new(TString), sig.type
|
21
|
+
assert_equal "a_valid_nam3", sig.name
|
22
|
+
end
|
23
|
+
def test_parse_method_signature
|
24
|
+
assert_raise CannotParseSignature do
|
25
|
+
parse_method_signature "there are no brackets"
|
26
|
+
end
|
27
|
+
assert_raise CannotParseSignature do
|
28
|
+
parse_method_signature "int main(int main())"
|
29
|
+
end
|
30
|
+
assert_raise CannotParseSignature do
|
31
|
+
parse_method_signature "int main(oops forget to close bracket"
|
32
|
+
end
|
33
|
+
method = " int leastBorders(String[] X , int[] Y, double[] R,"
|
34
|
+
method += " char my_x1, long y1 , float x2, int[][] y2) "
|
35
|
+
sigs = parse_method_signature method
|
36
|
+
assert_equal 8, sigs.size
|
37
|
+
assert_equal TInt, sigs[0].type
|
38
|
+
assert_equal "leastBorders", sigs[0].name
|
39
|
+
assert_equal TArray.new(TString), sigs[1].type
|
40
|
+
assert_equal "X", sigs[1].name
|
41
|
+
assert_equal TArray.new(TInt), sigs[2].type
|
42
|
+
assert_equal "Y", sigs[2].name
|
43
|
+
assert_equal TArray.new(TDouble), sigs[3].type
|
44
|
+
assert_equal "R", sigs[3].name
|
45
|
+
assert_equal TChar, sigs[4].type
|
46
|
+
assert_equal "my_x1", sigs[4].name
|
47
|
+
assert_equal TLong, sigs[5].type
|
48
|
+
assert_equal "y1", sigs[5].name
|
49
|
+
assert_equal TFloat, sigs[6].type
|
50
|
+
assert_equal "x2", sigs[6].name
|
51
|
+
assert_equal TArray.new(TArray.new(TInt)), sigs[7].type
|
52
|
+
assert_equal "y2", sigs[7].name
|
53
|
+
end
|
54
|
+
end
|