gettc 1.10 → 2.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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/bin/gettc +60 -69
  3. data/dist/config.yml +1 -1
  4. data/dist/include/cpp/engine.rb +78 -86
  5. data/dist/include/cpp/topcoder +236 -236
  6. data/dist/include/go/engine.rb +53 -61
  7. data/dist/include/haskell/engine.rb +112 -122
  8. data/dist/include/java/engine.rb +187 -184
  9. data/dist/include/javascript/engine.rb +26 -30
  10. data/dist/include/javascript/topcoder.js +3 -3
  11. data/dist/include/javascript/topcoder/errors.js +5 -5
  12. data/dist/include/javascript/topcoder/reader.js +188 -165
  13. data/dist/include/javascript/topcoder/writer.js +37 -33
  14. data/dist/include/python/engine.rb +43 -52
  15. data/dist/include/python/topcoder/__init__.pyc +0 -0
  16. data/dist/include/python/topcoder/__pycache__/__init__.cpython-34.pyc +0 -0
  17. data/dist/include/python/topcoder/__pycache__/errors.cpython-34.pyc +0 -0
  18. data/dist/include/python/topcoder/__pycache__/reader.cpython-34.pyc +0 -0
  19. data/dist/include/python/topcoder/__pycache__/writer.cpython-34.pyc +0 -0
  20. data/dist/include/python/topcoder/errors.pyc +0 -0
  21. data/dist/include/python/topcoder/reader.pyc +0 -0
  22. data/dist/include/python/topcoder/writer.pyc +0 -0
  23. data/dist/include/ruby/engine.rb +47 -52
  24. data/dist/include/ruby/topcoder/reader.rb +205 -193
  25. data/dist/include/ruby/topcoder/writer.rb +39 -37
  26. data/dist/template/bin/runner.rb +146 -151
  27. data/dist/template/bin/runner.sh +96 -96
  28. data/dist/template/prob/{name}.html +1 -1
  29. data/dist/template/solve/cpp/{name}.cpp +3 -4
  30. data/dist/template/solve/cpp/{name}Solver.cpp +14 -14
  31. data/dist/template/solve/go/{name}/{name}.go +3 -3
  32. data/dist/template/solve/go/{name}Solver.go +2 -2
  33. data/dist/template/solve/haskell/{name}.hs +6 -6
  34. data/dist/template/solve/haskell/{name}Solver.hs +10 -10
  35. data/dist/template/solve/java/{name}.java +4 -4
  36. data/dist/template/solve/java/{name}Solver.java +4 -4
  37. data/dist/template/solve/javascript/{name}.js +4 -6
  38. data/dist/template/solve/javascript/{name}Solver.js +11 -9
  39. data/dist/template/solve/python/{name}.py +1 -1
  40. data/dist/template/solve/python/{name}Solver.py +2 -2
  41. data/dist/template/solve/ruby/{name}.rb +4 -4
  42. data/dist/template/solve/ruby/{name}Solver.rb +14 -17
  43. data/dist/template/util/check/check.cpp +19 -19
  44. data/{core/lib → lib}/gettc.rb +0 -0
  45. data/lib/gettc/account.rb +14 -0
  46. data/lib/gettc/download.rb +211 -0
  47. data/lib/gettc/generate.rb +156 -0
  48. data/lib/gettc/parse.rb +237 -0
  49. data/lib/gettc/print.rb +54 -0
  50. data/lib/gettc/problem.rb +39 -0
  51. data/lib/gettc/signature.rb +63 -0
  52. data/lib/gettc/types.rb +93 -0
  53. data/lib/version.rb +3 -0
  54. data/test/gettc/download_test.rb +61 -0
  55. data/test/gettc/generate_test.rb +70 -0
  56. data/test/gettc/parse_test.rb +78 -0
  57. data/test/gettc/signature_test.rb +71 -0
  58. data/test/gettc/types_test.rb +31 -0
  59. metadata +28 -23
  60. data/core/lib/gettc/download.rb +0 -130
  61. data/core/lib/gettc/generate.rb +0 -145
  62. data/core/lib/gettc/parse.rb +0 -233
  63. data/core/lib/gettc/print.rb +0 -56
  64. data/core/lib/gettc/problem.rb +0 -33
  65. data/core/lib/gettc/signature.rb +0 -55
  66. data/core/lib/gettc/types.rb +0 -83
  67. data/core/lib/version.rb +0 -3
  68. data/core/test/gettc/download_test.rb +0 -29
  69. data/core/test/gettc/generate_test.rb +0 -31
  70. data/core/test/gettc/parse_test.rb +0 -104
  71. data/core/test/gettc/signature_test.rb +0 -54
  72. data/core/test/gettc/types_test.rb +0 -28
@@ -0,0 +1,156 @@
1
+ require "fileutils"
2
+ require "pathname"
3
+ require "erb"
4
+
5
+ require "gettc/problem"
6
+ require "gettc/signature"
7
+ require "gettc/print"
8
+
9
+ module Gettc
10
+ GenerateError = Class.new StandardError
11
+
12
+ class ProblemDirExists < GenerateError
13
+ attr_accessor :dir
14
+
15
+ def initialize(dir, message = "Problem directory already exists")
16
+ @dir = dir
17
+ super "#{message}: (#{dir})"
18
+ end
19
+ end
20
+
21
+ class SourceDirNotExist < GenerateError
22
+ attr_accessor :dir
23
+
24
+ def initialize(dir, message = "Source directory does not exist")
25
+ @dir = dir
26
+ super "#{message}: (#{dir})"
27
+ end
28
+ end
29
+
30
+ class TargetDirNotExist < GenerateError
31
+ attr_accessor :dir
32
+
33
+ def initialize(dir, message = "Target directory does not exist")
34
+ @dir = dir
35
+ super "#{message}: (#{dir})"
36
+ end
37
+ end
38
+
39
+ class TemplateError < GenerateError
40
+ attr_accessor :dir
41
+
42
+ def initialize(err, source, message = "Template error")
43
+ @err = err
44
+ @source = source
45
+ super "#{message} (#{source})\n#{err}"
46
+ end
47
+ end
48
+
49
+ class Generator
50
+ def initialize(config_dir, target_dir)
51
+ @source_dir = File.join(config_dir, "template")
52
+ raise SourceDirNotExist.new(@source_dir) unless File.directory?(@source_dir)
53
+
54
+ @target_dir = target_dir
55
+ raise TargetDirNotExist.new(@target_dir) unless File.directory?(@target_dir)
56
+
57
+ load_engines(File.join(config_dir, "include"))
58
+ end
59
+
60
+ def generate(prob)
61
+ @prob = prob
62
+
63
+ @problem_dir = File.join(@target_dir, prob.name)
64
+ raise ProblemDirExists.new(@problem_dir) if File.exists?(@problem_dir)
65
+ FileUtils.mkdir(@problem_dir)
66
+
67
+ method_sig = @prob.definitions["Method signature"]
68
+ if method_sig.nil?
69
+ $stderr.puts "[Warning] No definition for method signature found"
70
+ else
71
+ vars = parse_method_signature(method_sig)
72
+ func = vars.shift
73
+ end
74
+ @context = binding
75
+
76
+ walk(@source_dir, @problem_dir)
77
+ end
78
+
79
+ private
80
+
81
+ def generate_images(images, images_dir)
82
+ images.each do |image|
83
+ filename = File.join(images_dir, image.name)
84
+ File.open(filename, "wb") { |f| f.write(image.content) }
85
+ end
86
+ end
87
+
88
+ def generate_test_case(cases, data_dir)
89
+ cases.each_with_index do |case_data, case_id|
90
+ File.write(File.join(data_dir, "#{case_id.to_s}.in"), case_data.input)
91
+ File.write(File.join(data_dir, "#{case_id.to_s}.out"), case_data.output)
92
+ end
93
+ end
94
+
95
+ def generate_template(source, target)
96
+ before = File.read(source)
97
+
98
+ begin
99
+ after = ERB.new(before).result(@context)
100
+ rescue SyntaxError, NameError => err
101
+ raise TemplateError.new(err, source)
102
+ end
103
+
104
+ begin
105
+ File.write(target, after)
106
+ rescue StandardError
107
+ raise target
108
+ end
109
+ end
110
+
111
+ def filter(target_dir, name)
112
+ case name
113
+ when "{images_d}"
114
+ generate_images(@prob.images, target_dir)
115
+ when "{examples_d}"
116
+ generate_test_case(@prob.examples, target_dir)
117
+ when "{systests_d}"
118
+ generate_test_case(@prob.systests, target_dir)
119
+ else
120
+ return name.gsub(/\{(\w*)\}/) { |match| @prob.name if $1 == "name" }
121
+ end
122
+ nil
123
+ end
124
+
125
+ def load_engines(include_dir)
126
+ return unless File.exists?(include_dir)
127
+ Dir.foreach(include_dir) do |name|
128
+ if File.directory?(child = File.join(include_dir, name))
129
+ if File.exists?(engine = File.join(child, "engine.rb"))
130
+ engine = "./#{engine}" unless Pathname.new(engine).absolute?
131
+ require engine
132
+ end
133
+ end
134
+ end
135
+ end
136
+
137
+ def walk(source_parent, target_parent)
138
+ Dir.foreach(source_parent) do |name|
139
+ next if [".", ".."].include?(name)
140
+
141
+ target_name = filter(target_parent, name)
142
+ next if target_name.nil?
143
+
144
+ source_child = File.join(source_parent, name)
145
+ target_child = File.join(target_parent, target_name)
146
+
147
+ if File.directory?(source_child)
148
+ FileUtils.mkdir(target_child) unless File.exists?(target_child)
149
+ walk(source_child, target_child)
150
+ elsif File.file?(source_child)
151
+ generate_template(source_child, target_child)
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,237 @@
1
+ require "gettc/problem"
2
+ require "gettc/download"
3
+
4
+ require "uri"
5
+ require "pathname"
6
+ require "hpricot"
7
+ require "logger"
8
+
9
+ module Gettc
10
+ class Parser
11
+ def initialize(downloader)
12
+ @downloader = downloader
13
+ @problem = Problem.new
14
+ end
15
+
16
+ def parse(problem_id)
17
+ doc = Hpricot(@downloader.download_statement(problem_id))
18
+
19
+ @problem = Problem.new
20
+ @problem.id = problem_id
21
+ @problem.name = parse_name(doc.search("tr/td.statTextBig").html)
22
+
23
+ html = doc.search("td.problemText/table").html
24
+
25
+ has_notes = true
26
+ has_constraints = true
27
+ has_examples = true
28
+
29
+ _, x = self.class.indexes(html, self.class.h3("Problem Statement"))
30
+ y, z = self.class.indexes(html, self.class.h3("Definition"))
31
+ parse_statement(html[x .. y])
32
+
33
+ x, y = self.class.indexes(html, self.class.h3("Notes"))
34
+ if x.nil?
35
+ has_notes = false
36
+ x, y = self.class.indexes(html, self.class.h3("Constraints"))
37
+ if x.nil?
38
+ has_constraints = false
39
+ x, y = self.class.indexes(html, self.class.h3("Examples"))
40
+ if x.nil?
41
+ has_examples = false
42
+ x = -2
43
+ end
44
+ end
45
+ end
46
+
47
+ parse_definitions(html[z .. x])
48
+
49
+ if has_notes
50
+ z, x = self.class.indexes(html, self.class.h3("Constraints"))
51
+ if z.nil?
52
+ has_constraints = false
53
+ z, x = self.class.indexes(html, self.class.h3("Examples"))
54
+ if z.nil?
55
+ has_examples = false
56
+ z = - 2
57
+ end
58
+ end
59
+ parse_notes(html[y .. z])
60
+ x, y = z, x
61
+ end
62
+
63
+ if has_constraints
64
+ z, x = self.class.indexes(html, self.class.h3("Examples"))
65
+ if z.nil?
66
+ has_examples = false
67
+ z = -2
68
+ end
69
+ parse_constraints(html[y .. z])
70
+ end
71
+
72
+ parse_examples(html[x .. -2]) if has_examples
73
+ parse_details(doc)
74
+
75
+ @problem
76
+ end
77
+
78
+ private
79
+
80
+ def self.indexes(str, substr)
81
+ from = str.index(substr)
82
+ return nil if from.nil?
83
+ return from - 1, from + substr.size
84
+ end
85
+
86
+ def self.h3(tag)
87
+ "<h3>#{tag}</h3>"
88
+ end
89
+
90
+ def self.filter_inout(text)
91
+ text.gsub("{", "[").gsub("}", "]").strip
92
+ end
93
+
94
+ def self.get_param_value(url, param_key)
95
+ if (match_data = url.match("[&|&amp;]#{param_key}=(\\d+)"))
96
+ match_data[1] if match_data.size > 1
97
+ end
98
+ end
99
+
100
+ def filter(html)
101
+ html = html.force_encoding("ISO-8859-1")
102
+ .encode("utf-8", {
103
+ invaid: :replace,
104
+ undef: :replace,
105
+ replace: ""
106
+ })
107
+
108
+ html.gsub!(/<b>(\w*)<\/b>/) { |match| "*#{$1}*" }
109
+ html.gsub!(/<sup>(\w*)<\/sup>/) { |match| "^(#{$1})" }
110
+ html.gsub!("&#160;", "")
111
+ html.gsub!("&nbsp;", " ")
112
+
113
+ text = Hpricot(html).to_plain_text
114
+ text.gsub!(/\[img:(http:\/\/[^\]]*)\]/) do |match|
115
+ url = $1
116
+ image = Image.new
117
+ image.name = Pathname.new(url).basename
118
+ begin
119
+ image.content = @downloader.download(url)
120
+ @problem.images << image
121
+ "![image](images/#{image.name})"
122
+ rescue HttpError
123
+ "![image](#{url})"
124
+ end
125
+ end
126
+
127
+ text
128
+ end
129
+
130
+ def parse_list_table(html)
131
+ result = []
132
+ Hpricot(html).search("/tr").each do |tr|
133
+ tds = tr.search("/td.statText")
134
+ result << filter(tds[1].html) if tds.size == 2
135
+ end
136
+ result
137
+ end
138
+
139
+ def parse_name(html)
140
+ @problem.name = filter(html.sub("Problem Statement for", ""))
141
+ end
142
+
143
+ def parse_statement(html)
144
+ @problem.statement = filter(html)
145
+ end
146
+
147
+ def parse_definitions(html)
148
+ Hpricot(html).search("/tr/td.statText/table/tr").each do |tr|
149
+ tds = tr.search("/td.statText")
150
+ @problem.definitions[tds[0].to_plain_text[0 .. -2]] = tds[1].to_plain_text if tds.size == 2
151
+ end
152
+ end
153
+
154
+ def parse_notes(html)
155
+ @problem.notes = parse_list_table(html)
156
+ end
157
+
158
+ def parse_constraints(html)
159
+ @problem.constraints = parse_list_table(html)
160
+ end
161
+
162
+ def parse_input(html)
163
+ text = ""
164
+ Hpricot(html).search("/table/tr/td.statText") do |td|
165
+ input = td.to_plain_text.strip
166
+ if text.empty?
167
+ text = input
168
+ else
169
+ text << ",\n" << input
170
+ end
171
+ end
172
+ self.class.filter_inout(text)
173
+ end
174
+
175
+ def parse_output(html)
176
+ self.class.filter_inout(Hpricot(html).to_plain_text.sub("Returns: ", ""))
177
+ end
178
+
179
+ def parse_reason(html)
180
+ filter(html)
181
+ end
182
+
183
+ def parse_examples(html)
184
+ tds = Hpricot(html).search("/tr/td.statText/table/tr/td.statText")
185
+ i = 0
186
+ while i < tds.size
187
+ example = Case.new
188
+ example.input = parse_input(tds[i].html)
189
+ example.output = parse_output(tds[i += 1].html)
190
+ example.reason = parse_reason(tds[i += 1].html)
191
+ @problem.examples << example
192
+ i += 1
193
+ end
194
+ end
195
+
196
+ def parse_systests(html)
197
+ _, y = self.class.indexes(html, "<!-- System Testing -->")
198
+ z, _ = self.class.indexes(html, "<!-- End System Testing -->")
199
+ return [] unless y && z
200
+
201
+ Hpricot(html[y .. z]).search("/table/tr[@valign=top]").each_with_object([]) do |tr, memo|
202
+ tds = tr.search("/td.statText")
203
+ next unless tds.size == 3
204
+
205
+ test = Case.new
206
+ test.input = self.class.filter_inout(tds[0].to_plain_text)
207
+ test.output = self.class.filter_inout(tds[1].to_plain_text)
208
+ @problem.systests << test
209
+ end
210
+ end
211
+
212
+ def download_systests(round_id)
213
+ detail_html = @downloader.download_detail(@problem.id, round_id)
214
+
215
+ Hpricot(detail_html).search("a[@href^=/stat?c=problem_solution]") do |elem|
216
+ url = elem.attributes["href"]
217
+
218
+ if solution_id = self.class.get_param_value(url, "cr")
219
+ parse_systests(@downloader.download_solution(@problem.id, round_id, solution_id))
220
+ return unless @problem.systests.empty?
221
+ end
222
+ end
223
+ end
224
+
225
+ def parse_details(doc)
226
+ doc.search("a[@href^=/tc?module=ProblemDetail]") do |elem|
227
+ @problem.url = elem.attributes["href"]
228
+ @problem.source = filter(elem.html)
229
+
230
+ if round_id = self.class.get_param_value(@problem.url, "rd")
231
+ download_systests(round_id)
232
+ return unless @problem.systests.empty?
233
+ end
234
+ end
235
+ end
236
+ end
237
+ end
@@ -0,0 +1,54 @@
1
+ require "gettc/problem"
2
+ require "rdiscount"
3
+
4
+ module Gettc
5
+ class Problem
6
+ def to_md
7
+ out = "# [#{@name}](#{@url})\n"
8
+ out << "*#{@source}*\n\n"
9
+
10
+ out << "## Statement\n"
11
+ out << @statement << "\n\n"
12
+
13
+ unless @definitions.empty?
14
+ out << "## Definitions\n"
15
+ @definitions.each { |k, v| out << "- *#{k}*: `#{v}`\n" }
16
+ out << "\n"
17
+ end
18
+
19
+ unless @notes.empty?
20
+ out << "## Notes\n"
21
+ @notes.each { |note| out << "- #{note}\n" }
22
+ out << "\n"
23
+ end
24
+
25
+ unless @constraints.empty?
26
+ out << "## Constraints\n"
27
+ @constraints.each { |constraint| out << "- #{constraint}\n" }
28
+ out << "\n"
29
+ end
30
+
31
+ unless @examples.empty?
32
+ out << "## Examples\n"
33
+
34
+ @examples.each_index do |i|
35
+ example = @examples[i]
36
+ out << "### Example #{i + 1}\n"
37
+ out << "#### Input\n<c>"
38
+ out << example.input.gsub("\n", "<br />")
39
+ out << "</c>\n"
40
+ out << "#### Output\n<c>"
41
+ out << example.output.gsub("\n", "<br />")
42
+ out << "</c>\n"
43
+ out << "#### Reason\n#{example.reason}\n\n" unless example.reason.empty?
44
+ end
45
+ end
46
+
47
+ out
48
+ end
49
+
50
+ def to_html
51
+ RDiscount.new(to_md).to_html
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,39 @@
1
+ module Gettc
2
+ class Case
3
+ attr_accessor :input, :output, :reason
4
+
5
+ def initialize
6
+ @input = ""
7
+ @output = ""
8
+ @reason = ""
9
+ end
10
+ end
11
+
12
+ class Image
13
+ attr_accessor :name, :content
14
+
15
+ def initialize
16
+ @name = ""
17
+ @content = ""
18
+ end
19
+ end
20
+
21
+ class Problem
22
+ attr_accessor :id, :name, :url, :source, :statement, :definitions, :notes,
23
+ :constraints, :examples, :systests, :images
24
+
25
+ def initialize
26
+ @id = 0
27
+ @name = ""
28
+ @url = ""
29
+ @source = ""
30
+ @statement = ""
31
+ @definitions = {}
32
+ @notes = []
33
+ @constraints = []
34
+ @examples = []
35
+ @systests = []
36
+ @images = []
37
+ end
38
+ end
39
+ end