codeforces-viewer 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 +7 -0
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +5 -0
- data/bin/codeforces-viewer +8 -0
- data/codeforces-viewer.gemspec +29 -0
- data/lib/codeforces/viewer/application.rb +67 -0
- data/lib/codeforces/viewer/cacher.rb +38 -0
- data/lib/codeforces/viewer/constants.rb +6 -0
- data/lib/codeforces/viewer/render.rb +110 -0
- data/lib/codeforces/viewer/utils.rb +9 -0
- data/lib/codeforces/viewer/version.rb +5 -0
- data/lib/codeforces/viewer/viewer.rb +112 -0
- data/lib/codeforces/viewer.rb +5 -0
- data/spec/mock/problem_text_350A.html +37 -0
- data/spec/mock/problem_text_365A.html +59 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/test/t2u9hw5_render_spec.rb +58 -0
- data/spec/test/tirgu7u_viewer_spec.rb +42 -0
- data/spec/test/tukgi3p_cacher_spec.rb +29 -0
- metadata +170 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f84c5a69101037d7e82b4e19d1f70859bc1a7faa
|
4
|
+
data.tar.gz: 2982dccf14455c2c16c26bd109bcdd27c4a351e5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6e678360a4b164df4d2b49be5f552afacd187068489f3f85e9d5baa776953e57127794fe09645d7ba4039a2f47096a2883263c27ac3dc3ebc9aaf30da63759f8
|
7
|
+
data.tar.gz: 85cba4d0d3462c23f6d2985371c3ac822b9e397fdea288f9dc50c5a2bcf5154740c579809dbebe0164e4f441f1a40cc9fe671b5bb4432ef6dc49672277ea2103
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Hiroyuki Sano
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Codeforces::Viewer
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'codeforces-viewer'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install codeforces-viewer
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it ( http://github.com/<my-github-username>/codeforces-viewer/fork )
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'codeforces/viewer/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "codeforces-viewer"
|
8
|
+
spec.version = Codeforces::Viewer::VERSION
|
9
|
+
spec.authors = ["Hiroyuki Sano"]
|
10
|
+
spec.email = ["sh19910711@gmail.com"]
|
11
|
+
spec.summary = %q{A simple command-line viewer for Codeforces}
|
12
|
+
spec.description = %q{A simple command-line viewer for Codeforces}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "nokogiri"
|
22
|
+
spec.add_dependency "colorize"
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
25
|
+
spec.add_development_dependency "rake"
|
26
|
+
spec.add_development_dependency "rspec"
|
27
|
+
spec.add_development_dependency "webmock"
|
28
|
+
spec.add_development_dependency "fakefs"
|
29
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require_relative 'version'
|
2
|
+
require_relative 'viewer'
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
module Codeforces
|
6
|
+
module Viewer
|
7
|
+
class Application
|
8
|
+
attr_accessor :option
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@option = {
|
12
|
+
:contest_id => nil,
|
13
|
+
:problem_id => nil,
|
14
|
+
:input_only => false,
|
15
|
+
:output_only => false,
|
16
|
+
:cache => true,
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def parse_options(argv)
|
21
|
+
OptionParser.new do |option_parser|
|
22
|
+
option_parser.banner = "Usage: codeforces_viewer -c {contest-id} -p {problem-id} [options]"
|
23
|
+
option_parser.separator ""
|
24
|
+
option_parser.version = Codeforces::Viewer::VERSION
|
25
|
+
|
26
|
+
# contest id
|
27
|
+
option_parser.on('-c contest-id', '--contest-id contest-id', 'specify codeforces contest id (e.g. 100, 99)', String) {|contest_id| @option[:contest_id] = contest_id }
|
28
|
+
|
29
|
+
# problem_id
|
30
|
+
option_parser.on('-p problem-id', '--problem-id problem-id', 'specify codeforces problem id (e.g. A, B)', String) {|problem_id| @option[:problem_id] = problem_id }
|
31
|
+
|
32
|
+
option_parser.separator ""
|
33
|
+
|
34
|
+
# show input only
|
35
|
+
option_parser.on('-i input-no', '--input input-no', 'specify sample input id (0 is all)', Integer) {|flag| @option[:input_only] = flag }
|
36
|
+
|
37
|
+
# show output only
|
38
|
+
option_parser.on('-o input-no', '--output input-no', 'specify sample output id (0 is all)', Integer) {|flag| @option[:output_only] = flag }
|
39
|
+
|
40
|
+
option_parser.separator ""
|
41
|
+
|
42
|
+
# no cache
|
43
|
+
option_parser.on('--no-cache', 'no cache html files') {|flag| @option[:cache] = flag }
|
44
|
+
|
45
|
+
option_parser.separator ""
|
46
|
+
|
47
|
+
# parse
|
48
|
+
option_parser.parse! argv
|
49
|
+
end
|
50
|
+
|
51
|
+
# check
|
52
|
+
if @option[:contest_id].nil? || @option[:contest_id] == ""
|
53
|
+
abort "Error: contest id must be specified"
|
54
|
+
end
|
55
|
+
if @option[:problem_id].nil? || @option[:problem_id] == ""
|
56
|
+
abort "Error: problem id must be specified"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def show
|
61
|
+
viewer = Codeforces::Viewer::Viewer.new @option
|
62
|
+
viewer.show
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require_relative 'constants'
|
2
|
+
|
3
|
+
require 'digest/md5'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'net/http'
|
6
|
+
require 'uri'
|
7
|
+
|
8
|
+
module Codeforces
|
9
|
+
module Viewer
|
10
|
+
class Cacher
|
11
|
+
attr_accessor :option
|
12
|
+
|
13
|
+
def initialize(enabled = true, cache_dir = DEFAULT_CACHE_DIR)
|
14
|
+
@option = {}
|
15
|
+
@option[:enabled] = enabled
|
16
|
+
if @option[:enabled]
|
17
|
+
@option[:cache_dir] = File.expand_path cache_dir
|
18
|
+
FileUtils.mkdir_p @option[:cache_dir]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_url(url)
|
23
|
+
file_path = "#{@option[:cache_dir]}/#{Digest::MD5.hexdigest url}"
|
24
|
+
if @option[:enabled] && File.exists?(file_path)
|
25
|
+
File.read file_path
|
26
|
+
else
|
27
|
+
body = Net::HTTP.get URI.parse url
|
28
|
+
if @option[:enabled]
|
29
|
+
FileUtils.touch file_path
|
30
|
+
File.write file_path, body
|
31
|
+
end
|
32
|
+
body
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'colorize'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
module Codeforces
|
5
|
+
module Viewer
|
6
|
+
class Render
|
7
|
+
def render_list_item(depth, doc, number = false)
|
8
|
+
cnt = 0
|
9
|
+
doc.children.each do |item|
|
10
|
+
case item.name
|
11
|
+
when "ol", "ul"
|
12
|
+
item = render_list(depth, item)
|
13
|
+
when "li"
|
14
|
+
if number
|
15
|
+
cnt += 1
|
16
|
+
item.content = "#{" " * depth}#{cnt}. #{item.text}\n"
|
17
|
+
else
|
18
|
+
item.content = "#{" " * depth}* #{item.text}\n"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
doc
|
23
|
+
end
|
24
|
+
|
25
|
+
def render_list(depth, doc)
|
26
|
+
case doc.name
|
27
|
+
when "ol"
|
28
|
+
doc = render_list_item(depth + 2, doc, true)
|
29
|
+
when "ul"
|
30
|
+
doc = render_list_item(depth + 2, doc)
|
31
|
+
end
|
32
|
+
doc.content = "#{doc.text}\n"
|
33
|
+
doc
|
34
|
+
end
|
35
|
+
|
36
|
+
def render_normal_tag(doc)
|
37
|
+
case doc.name
|
38
|
+
when "div", "pre"
|
39
|
+
doc.content = "\n#{doc.content}\n"
|
40
|
+
when "p"
|
41
|
+
doc.content = "\n\n#{doc.content}\n\n"
|
42
|
+
when "ol"
|
43
|
+
doc = render_list(0, doc)
|
44
|
+
when "br"
|
45
|
+
doc.content = "\n"
|
46
|
+
when "sup"
|
47
|
+
doc.content = "^#{doc.content}"
|
48
|
+
when "sub"
|
49
|
+
doc.content = "[#{doc.content}]"
|
50
|
+
when "img"
|
51
|
+
doc.content = "\n!! Image File: #{doc["src"]}\n\n"
|
52
|
+
end
|
53
|
+
doc
|
54
|
+
end
|
55
|
+
|
56
|
+
def render_header_class(doc)
|
57
|
+
case doc["class"]
|
58
|
+
when "title", "section-title"
|
59
|
+
doc.content = "# #{doc.text.strip}".colorize(:mode => :bold)
|
60
|
+
when "tex-span", "tex-font-style-tt"
|
61
|
+
doc.content = doc.text.strip.colorize(:mode => :bold)
|
62
|
+
when "property-title"
|
63
|
+
doc.content = doc.text.strip.colorize(:mode => :underline)
|
64
|
+
end
|
65
|
+
doc
|
66
|
+
end
|
67
|
+
|
68
|
+
def render_header(doc)
|
69
|
+
doc = render_header_class(doc)
|
70
|
+
if doc.children.length > 0
|
71
|
+
doc.children.map {|item| render_header item }
|
72
|
+
end
|
73
|
+
doc = render_normal_tag(doc)
|
74
|
+
doc
|
75
|
+
end
|
76
|
+
|
77
|
+
def render(doc)
|
78
|
+
if doc.children.length > 0
|
79
|
+
doc.children.map {|item| render item }
|
80
|
+
end
|
81
|
+
doc = render_normal_tag(doc)
|
82
|
+
doc = render_header_class(doc)
|
83
|
+
doc
|
84
|
+
end
|
85
|
+
|
86
|
+
def render_html(html)
|
87
|
+
render(Nokogiri::HTML.fragment(html)).text
|
88
|
+
end
|
89
|
+
|
90
|
+
def render_input(input_id, doc)
|
91
|
+
input_id = input_id.to_i
|
92
|
+
if input_id == 0
|
93
|
+
doc.xpath('./div[@class="input"]/pre').map {|item| render item }
|
94
|
+
else
|
95
|
+
[render(doc.xpath('./div[@class="input"]/pre')[input_id - 1])]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def render_output(output_id, doc)
|
100
|
+
output_id = output_id.to_i
|
101
|
+
if output_id == 0
|
102
|
+
doc.xpath('./div[@class="output"]/pre').map {|item| render item }
|
103
|
+
else
|
104
|
+
[render(doc.xpath('./div[@class="output"]/pre')[output_id - 1])]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require_relative 'version'
|
2
|
+
require_relative 'render'
|
3
|
+
require_relative 'cacher'
|
4
|
+
require_relative 'utils'
|
5
|
+
|
6
|
+
require 'nokogiri'
|
7
|
+
|
8
|
+
module Codeforces
|
9
|
+
module Viewer
|
10
|
+
class Viewer
|
11
|
+
def initialize(option = {})
|
12
|
+
@option = option
|
13
|
+
@render = Codeforces::Viewer::Render.new
|
14
|
+
@cacher = Codeforces::Viewer::Cacher.new @option[:cache]
|
15
|
+
end
|
16
|
+
|
17
|
+
# fetch problem text
|
18
|
+
def fetch_problem_text(contest_id, problem_id)
|
19
|
+
url = Codeforces::Viewer::Utils.get_problem_url(contest_id, problem_id)
|
20
|
+
body = @cacher.get_url url
|
21
|
+
doc = Nokogiri::HTML(body)
|
22
|
+
problem_statement = doc.xpath('//div[@class="problem-statement"]')
|
23
|
+
|
24
|
+
div_list = problem_statement.xpath('./div')
|
25
|
+
|
26
|
+
res = {}
|
27
|
+
res[:round] = get_round_info(contest_id, doc)
|
28
|
+
res[:info] = get_problem_info(div_list[0])
|
29
|
+
res[:body] = @render.render(div_list[1]).text
|
30
|
+
res[:input] = @render.render(div_list[2]).text
|
31
|
+
res[:output] = @render.render(div_list[3]).text
|
32
|
+
res[:sample_test] = @render.render(div_list[4]).text
|
33
|
+
if div_list.length >= 6
|
34
|
+
res[:note] = @render.render(div_list[5]).text
|
35
|
+
else
|
36
|
+
res[:note] = ""
|
37
|
+
end
|
38
|
+
res
|
39
|
+
end
|
40
|
+
|
41
|
+
def fetch_input(input_id, contest_id, problem_id)
|
42
|
+
url = Codeforces::Viewer::Utils.get_problem_url(contest_id, problem_id)
|
43
|
+
body = @cacher.get_url url
|
44
|
+
doc = Nokogiri::HTML(body)
|
45
|
+
@render.render_input(input_id, doc.xpath('//div[@class="sample-test"]')).map {|item| item.text.strip + "\n" }.join
|
46
|
+
end
|
47
|
+
|
48
|
+
def fetch_output(output_id, contest_id, problem_id)
|
49
|
+
url = Codeforces::Viewer::Utils.get_problem_url(contest_id, problem_id)
|
50
|
+
body = @cacher.get_url url
|
51
|
+
doc = Nokogiri::HTML(body)
|
52
|
+
@render.render_output(output_id, doc.xpath('//div[@class="sample-test"]')).map {|item| item.text.strip + "\n" }.join
|
53
|
+
end
|
54
|
+
|
55
|
+
def get_round_info(contest_id, doc)
|
56
|
+
doc.xpath("//table//a[@href='/contest/#{contest_id}']").text.strip
|
57
|
+
end
|
58
|
+
|
59
|
+
# get title and other info
|
60
|
+
def get_problem_info(doc)
|
61
|
+
res = {
|
62
|
+
:title => @render.render_header(doc.xpath('//div[@class="title"]')[0]).text,
|
63
|
+
:time_limit => @render.render_header(doc.xpath('//div[@class="time-limit"]')[0]).text,
|
64
|
+
:memory_limit => @render.render_header(doc.xpath('//div[@class="memory-limit"]')[0]).text,
|
65
|
+
:input_file => @render.render_header(doc.xpath('//div[@class="input-file"]')[0]).text,
|
66
|
+
:output_file => @render.render_header(doc.xpath('//div[@class="output-file"]')[0]).text,
|
67
|
+
}
|
68
|
+
res
|
69
|
+
end
|
70
|
+
|
71
|
+
def show()
|
72
|
+
contest_id = @option[:contest_id]
|
73
|
+
problem_id = @option[:problem_id]
|
74
|
+
|
75
|
+
|
76
|
+
if @option[:input_only]
|
77
|
+
puts fetch_input(@option[:input_only], contest_id, problem_id)
|
78
|
+
elsif @option[:output_only]
|
79
|
+
puts fetch_output(@option[:output_only], contest_id, problem_id)
|
80
|
+
else
|
81
|
+
ret = fetch_problem_text contest_id, problem_id
|
82
|
+
puts "# #{ret[:round]}"
|
83
|
+
puts "URL: #{Codeforces::Viewer::Utils.get_problem_url(contest_id, problem_id)}"
|
84
|
+
print ret[:info][:title]
|
85
|
+
print ret[:info][:time_limit]
|
86
|
+
print ret[:info][:memory_limit]
|
87
|
+
print ret[:info][:input_file]
|
88
|
+
print ret[:info][:output_file]
|
89
|
+
puts ""
|
90
|
+
puts ""
|
91
|
+
puts " * * * * *"
|
92
|
+
puts ""
|
93
|
+
puts ret[:body]
|
94
|
+
puts ""
|
95
|
+
puts " * * * * *"
|
96
|
+
puts ""
|
97
|
+
puts ret[:input]
|
98
|
+
puts ret[:output]
|
99
|
+
puts ""
|
100
|
+
puts " * * * * *"
|
101
|
+
puts ""
|
102
|
+
puts ret[:sample_test]
|
103
|
+
puts ""
|
104
|
+
puts " * * * * *"
|
105
|
+
puts ""
|
106
|
+
puts ret[:note]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
<html>
|
2
|
+
<body>
|
3
|
+
<table class="rtable ">
|
4
|
+
<tr>
|
5
|
+
<td>
|
6
|
+
<a style="color: black" href="/contest/350">Codeforces Round #203 (Div. 2)</a>
|
7
|
+
</td>
|
8
|
+
</tr>
|
9
|
+
</table>
|
10
|
+
<div class="ttypography">
|
11
|
+
<div class="problem-statement">
|
12
|
+
<div class="header">
|
13
|
+
<div class="title">A. TL</div>
|
14
|
+
<div class="time-limit"><div class="property-title">time limit per test</div>2 seconds</div>
|
15
|
+
<div class="memory-limit"><div class="property-title">memory limit per test</div>256 megabytes</div>
|
16
|
+
<div class="input-file"><div class="property-title">input</div>standard input</div>
|
17
|
+
<div class="output-file"><div class="property-title">output</div>standard output</div>
|
18
|
+
</div>
|
19
|
+
<div>
|
20
|
+
<p>Valera wanted to prepare a Codesecrof round. He's already got one problem and he wants to set a time limit (TL) on it.</p><p>Valera has written <span class="tex-span"><i>n</i></span> correct solutions. For each correct solution, he knows its running time (in seconds). Valera has also wrote <span class="tex-span"><i>m</i></span> wrong solutions and for each wrong solution he knows its running time (in seconds).</p><p>Let's suppose that Valera will set <span class="tex-span"><i>v</i></span> seconds TL in the problem. Then we can say that a solution passes the system testing if its running time is at most <span class="tex-span"><i>v</i></span> seconds. We can also say that a solution passes the system testing with some "extra" time if for its running time, <span class="tex-span"><i>a</i></span> seconds, an inequality <span class="tex-span">2<i>a</i> ≤ <i>v</i></span> holds.</p><p>As a result, Valera decided to set <span class="tex-span"><i>v</i></span> seconds TL, that the following conditions are met:</p><ol> <li> <span class="tex-span"><i>v</i></span> is a positive integer; </li><li> all correct solutions pass the system testing; </li><li> at least one correct solution passes the system testing with some "extra" time; </li><li> all wrong solutions do not pass the system testing; </li><li> value <span class="tex-span"><i>v</i></span> is minimum among all TLs, for which points <span class="tex-span">1</span>, <span class="tex-span">2</span>, <span class="tex-span">3</span>, <span class="tex-span">4</span> hold. </li></ol><p>Help Valera and find the most suitable TL or else state that such TL doesn't exist.</p></div>
|
21
|
+
<div class="input-specification">
|
22
|
+
<div class="section-title">Input</div><p>The first line contains two integers <span class="tex-span"><i>n</i></span>, <span class="tex-span"><i>m</i></span> (<span class="tex-span">1 ≤ <i>n</i>, <i>m</i> ≤ 100</span>). The second line contains <span class="tex-span"><i>n</i></span> space-separated positive integers <span class="tex-span"><i>a</i><sub class="lower-index">1</sub>, <i>a</i><sub class="lower-index">2</sub>, ..., <i>a</i><sub class="lower-index"><i>n</i></sub></span> (<span class="tex-span">1 ≤ <i>a</i><sub class="lower-index"><i>i</i></sub> ≤ 100</span>) — the running time of each of the <span class="tex-span"><i>n</i></span> correct solutions in seconds. The third line contains <span class="tex-span"><i>m</i></span> space-separated positive integers <span class="tex-span"><i>b</i><sub class="lower-index">1</sub>, <i>b</i><sub class="lower-index">2</sub>, ..., <i>b</i><sub class="lower-index"><i>m</i></sub></span> (<span class="tex-span">1 ≤ <i>b</i><sub class="lower-index"><i>i</i></sub> ≤ 100</span>) — the running time of each of <span class="tex-span"><i>m</i></span> wrong solutions in seconds. </p></div>
|
23
|
+
<div class="output-specification">
|
24
|
+
<div class="section-title">Output</div><p>If there is a valid TL value, print it. Otherwise, print -1.</p></div>
|
25
|
+
<div class="sample-tests">
|
26
|
+
<div class="section-title">Sample test(s)</div>
|
27
|
+
<div class="sample-test">
|
28
|
+
<div class="input"><div class="title">Input</div><pre>3 6<br />4 5 2<br />8 9 6 10 7 11<br /></pre></div>
|
29
|
+
<div class="output"><div class="title">Output</div><pre>5</pre></div>
|
30
|
+
<div class="input"><div class="title">Input</div><pre>3 1<br />3 4 5<br />6<br /></pre>
|
31
|
+
</div>
|
32
|
+
<div class="output"><div class="title">Output</div><pre>-1<br /></pre></div></div>
|
33
|
+
</div>
|
34
|
+
</div>
|
35
|
+
</div>
|
36
|
+
</body>
|
37
|
+
</html>
|
@@ -0,0 +1,59 @@
|
|
1
|
+
<html>
|
2
|
+
<body>
|
3
|
+
<table class="rtable ">
|
4
|
+
<tr>
|
5
|
+
<td>
|
6
|
+
<div><a style="color: black" href="/contest/365">Codeforces Round #213 (Div. 2)</a></div>
|
7
|
+
</td>
|
8
|
+
</tr>
|
9
|
+
</table>
|
10
|
+
<div class="ttypography">
|
11
|
+
<div class="problem-statement">
|
12
|
+
<div class="header">
|
13
|
+
<div class="title">
|
14
|
+
A. Good Number
|
15
|
+
</div>
|
16
|
+
<div class="time-limit"><div class="property-title">time limit per test</div>1 second</div>
|
17
|
+
<div class="memory-limit"><div class="property-title">memory limit per test</div>256 megabytes</div>
|
18
|
+
<div class="input-file"><div class="property-title">input</div>standard input</div>
|
19
|
+
<div class="output-file"><div class="property-title">output</div>standard output</div>
|
20
|
+
</div>
|
21
|
+
|
22
|
+
<div><p>Let's call a number <span class="tex-span"><i>k</i></span>-good if it contains all digits not exceeding <span class="tex-span"><i>k</i></span> (<span class="tex-span">0, ..., <i>k</i></span>). You've got a number <span class="tex-span"><i>k</i></span> and an array <span class="tex-span"><i>a</i></span> containing <span class="tex-span"><i>n</i></span> numbers. Find out how many <span class="tex-span"><i>k</i></span>-good numbers are in <span class="tex-span"><i>a</i></span> (count each number every time it occurs in array <span class="tex-span"><i>a</i></span>).</p></div>
|
23
|
+
<div class="input-specification">
|
24
|
+
<div class="section-title">Input</div>
|
25
|
+
<p>The first line contains integers <span class="tex-span"><i>n</i></span> and <span class="tex-span"><i>k</i></span> (<span class="tex-span">1 ≤ <i>n</i> ≤ 100</span>, <span class="tex-span">0 ≤ <i>k</i> ≤ 9</span>). The <span class="tex-span"><i>i</i></span>-th of the following <span class="tex-span"><i>n</i></span> lines contains integer <span class="tex-span"><i>a</i><sub class="lower-index"><i>i</i></sub></span> without leading zeroes (<span class="tex-span">1 ≤ <i>a</i><sub class="lower-index"><i>i</i></sub> ≤ 10<sup class="upper-index">9</sup></span>).</p>
|
26
|
+
</div>
|
27
|
+
<div class="output-specification">
|
28
|
+
<div class="section-title">Output</div>
|
29
|
+
<p>Print a single integer — the number of <span class="tex-span"><i>k</i></span>-good numbers in <span class="tex-span"><i>a</i></span>.</p>
|
30
|
+
</div>
|
31
|
+
|
32
|
+
<div class="sample-tests">
|
33
|
+
<div class="section-title">Sample test(s)
|
34
|
+
</div>
|
35
|
+
<div class="sample-test">
|
36
|
+
<div class="input">
|
37
|
+
<div class="title">Input</div><pre>10 6<br />1234560<br />1234560<br />1234560<br />1234560<br />1234560<br />1234560<br />1234560<br />1234560<br />1234560<br />1234560<br /></pre>
|
38
|
+
</div>
|
39
|
+
|
40
|
+
<div class="output">
|
41
|
+
<div class="title">Output</div>
|
42
|
+
<pre>10<br /></pre>
|
43
|
+
</div>
|
44
|
+
|
45
|
+
<div class="input">
|
46
|
+
<div class="title">Input</div>
|
47
|
+
<pre>2 1<br />1<br />10<br /></pre>
|
48
|
+
</div>
|
49
|
+
|
50
|
+
<div class="output">
|
51
|
+
<div class="title">Output</div>
|
52
|
+
<pre>1<br /></pre>
|
53
|
+
</div>
|
54
|
+
</div>
|
55
|
+
</div>
|
56
|
+
</div>
|
57
|
+
</div>
|
58
|
+
</body>
|
59
|
+
</html>
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'codeforces/viewer'
|
2
|
+
|
3
|
+
require 'fakefs/safe'
|
4
|
+
require 'fakefs/spec_helpers'
|
5
|
+
|
6
|
+
RSpec.configure do |config|
|
7
|
+
config.include FakeFS::SpecHelpers
|
8
|
+
end
|
9
|
+
|
10
|
+
require 'webmock'
|
11
|
+
WebMock.disable_net_connect!
|
12
|
+
|
13
|
+
module SpecHelpers
|
14
|
+
def self.read_file array_of_path
|
15
|
+
File.read get_path(array_of_path)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.get_path array_of_path
|
19
|
+
File.expand_path(File.join(File.dirname(__FILE__), File.join(array_of_path)))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Codeforces::Viewer::Render do
|
4
|
+
before do
|
5
|
+
@render = Codeforces::Viewer::Render.new
|
6
|
+
end
|
7
|
+
|
8
|
+
context :render_html do
|
9
|
+
context :sup do
|
10
|
+
it "<sup>" do
|
11
|
+
expect(@render.render_html("x<sup>y</sup>")).to eq "x^y"
|
12
|
+
end
|
13
|
+
it "<sup class>" do
|
14
|
+
expect(@render.render_html("x<sup class>y</sup>")).to eq "x^y"
|
15
|
+
end
|
16
|
+
it "<sup><sup>" do
|
17
|
+
expect(@render.render_html("x<sup>y</sup><sup>z</sup>")).to eq "x^y^z"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context :sub do
|
22
|
+
it "<sub>" do
|
23
|
+
expect(@render.render_html("A<sub>i</sub>")).to eq "A[i]"
|
24
|
+
end
|
25
|
+
it "<sub class>" do
|
26
|
+
expect(@render.render_html("A<sub class>i</sub>")).to eq "A[i]"
|
27
|
+
end
|
28
|
+
it "span sub" do
|
29
|
+
html = '<span class="tex-span">1 ≤ <i>a</i><sub class="lower-index"><i>i</i></sub> ≤ 100</span>'
|
30
|
+
expect(@render.render_html(html)).to include "1 ≤ a[i] ≤ 100"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context :div do
|
35
|
+
it "<div>" do
|
36
|
+
expect(@render.render_html("A<div>B</div>C")).to eq "A\nB\nC"
|
37
|
+
end
|
38
|
+
it "<div class>" do
|
39
|
+
expect(@render.render_html("A<div>B</div>C")).to eq "A\nB\nC"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context :p do
|
44
|
+
it "<p>" do
|
45
|
+
expect(@render.render_html("A<p>B</p>C")).to eq "A\n\nB\n\nC"
|
46
|
+
end
|
47
|
+
it "<p class>" do
|
48
|
+
expect(@render.render_html("A<p class>B</p>C")).to eq "A\n\nB\n\nC"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context :list do
|
53
|
+
it "<ol>" do
|
54
|
+
expect(@render.render_html("<ol><li>1</li><li>2</li><li>3</li></ol>")).to eq " 1. 1\n 2. 2\n 3. 3\n\n"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Codeforces::Viewer::Viewer do
|
4
|
+
before do
|
5
|
+
@viewer = Codeforces::Viewer::Viewer.new
|
6
|
+
end
|
7
|
+
|
8
|
+
before do
|
9
|
+
FakeFS.deactivate!
|
10
|
+
dummy_response_365A = SpecHelpers.read_file ['mock', 'problem_text_365A.html']
|
11
|
+
dummy_response_350A = SpecHelpers.read_file ['mock', 'problem_text_350A.html']
|
12
|
+
FakeFS.activate!
|
13
|
+
WebMock.stub_request(:get, /http:\/\/codeforces\.com\/contest\/[0-9]*?\/problem/).to_return(
|
14
|
+
:body => dummy_response_365A,
|
15
|
+
)
|
16
|
+
WebMock.stub_request(:get, /http:\/\/codeforces\.com\/contest\/350\/problem\/A/).to_return(
|
17
|
+
:body => dummy_response_350A,
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
context :fetch_problem_text do
|
22
|
+
subject(:ret) { @viewer.fetch_problem_text '350', 'A' }
|
23
|
+
it { ret[:round].should eq "Codeforces Round #203 (Div. 2)" }
|
24
|
+
it { ret[:body].should include "Valera" }
|
25
|
+
end
|
26
|
+
|
27
|
+
context :get_round_info do
|
28
|
+
it do
|
29
|
+
url = Codeforces::Viewer::Utils.get_problem_url '350', 'A'
|
30
|
+
body = Net::HTTP.get URI.parse url
|
31
|
+
doc = Nokogiri::HTML(body)
|
32
|
+
expect(@viewer.get_round_info('350', doc)).to eq 'Codeforces Round #203 (Div. 2)'
|
33
|
+
end
|
34
|
+
it do
|
35
|
+
url = Codeforces::Viewer::Utils.get_problem_url '365', 'A'
|
36
|
+
body = Net::HTTP.get URI.parse url
|
37
|
+
doc = Nokogiri::HTML(body)
|
38
|
+
expect(@viewer.get_round_info('365', doc)).to eq 'Codeforces Round #213 (Div. 2)'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Codeforces::Viewer::Cacher do
|
4
|
+
before do
|
5
|
+
@cacher = Codeforces::Viewer::Cacher.new true, ".cache"
|
6
|
+
end
|
7
|
+
|
8
|
+
context :cache do
|
9
|
+
before do
|
10
|
+
WebMock.stub_request(:get, /^http:\/\/foo\/bar$/).to_return(
|
11
|
+
:body => 'this is test',
|
12
|
+
)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "enable cache: http://foo/bar" do
|
16
|
+
url = "http://foo/bar"
|
17
|
+
ret = @cacher.get_url url
|
18
|
+
expect(ret).to eq File.read ".cache/#{Digest::MD5.hexdigest(url)}"
|
19
|
+
end
|
20
|
+
|
21
|
+
it "disable cache: http://foo/bar" do
|
22
|
+
@cacher = Codeforces::Viewer::Cacher.new false
|
23
|
+
url = "http://foo/bar"
|
24
|
+
ret = @cacher.get_url url
|
25
|
+
expect { File.read(".cache/#{Digest::MD5.hexdigest(url)}") }.to raise_error
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
metadata
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: codeforces-viewer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Hiroyuki Sano
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-02-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: nokogiri
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: colorize
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.5'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.5'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: webmock
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: fakefs
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: A simple command-line viewer for Codeforces
|
112
|
+
email:
|
113
|
+
- sh19910711@gmail.com
|
114
|
+
executables:
|
115
|
+
- codeforces-viewer
|
116
|
+
extensions: []
|
117
|
+
extra_rdoc_files: []
|
118
|
+
files:
|
119
|
+
- ".gitignore"
|
120
|
+
- Gemfile
|
121
|
+
- LICENSE.txt
|
122
|
+
- README.md
|
123
|
+
- Rakefile
|
124
|
+
- bin/codeforces-viewer
|
125
|
+
- codeforces-viewer.gemspec
|
126
|
+
- lib/codeforces/viewer.rb
|
127
|
+
- lib/codeforces/viewer/application.rb
|
128
|
+
- lib/codeforces/viewer/cacher.rb
|
129
|
+
- lib/codeforces/viewer/constants.rb
|
130
|
+
- lib/codeforces/viewer/render.rb
|
131
|
+
- lib/codeforces/viewer/utils.rb
|
132
|
+
- lib/codeforces/viewer/version.rb
|
133
|
+
- lib/codeforces/viewer/viewer.rb
|
134
|
+
- spec/mock/problem_text_350A.html
|
135
|
+
- spec/mock/problem_text_365A.html
|
136
|
+
- spec/spec_helper.rb
|
137
|
+
- spec/test/t2u9hw5_render_spec.rb
|
138
|
+
- spec/test/tirgu7u_viewer_spec.rb
|
139
|
+
- spec/test/tukgi3p_cacher_spec.rb
|
140
|
+
homepage: ''
|
141
|
+
licenses:
|
142
|
+
- MIT
|
143
|
+
metadata: {}
|
144
|
+
post_install_message:
|
145
|
+
rdoc_options: []
|
146
|
+
require_paths:
|
147
|
+
- lib
|
148
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
154
|
+
requirements:
|
155
|
+
- - ">="
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
158
|
+
requirements: []
|
159
|
+
rubyforge_project:
|
160
|
+
rubygems_version: 2.2.0
|
161
|
+
signing_key:
|
162
|
+
specification_version: 4
|
163
|
+
summary: A simple command-line viewer for Codeforces
|
164
|
+
test_files:
|
165
|
+
- spec/mock/problem_text_350A.html
|
166
|
+
- spec/mock/problem_text_365A.html
|
167
|
+
- spec/spec_helper.rb
|
168
|
+
- spec/test/t2u9hw5_render_spec.rb
|
169
|
+
- spec/test/tirgu7u_viewer_spec.rb
|
170
|
+
- spec/test/tukgi3p_cacher_spec.rb
|