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 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
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .cache
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in codeforces-viewer.gemspec
4
+ gemspec
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,5 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/codeforces/viewer/application'
4
+
5
+ app = Codeforces::Viewer::Application.new
6
+ app.parse_options ARGV
7
+ app.show
8
+
@@ -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,6 @@
1
+ module Codeforces
2
+ module Viewer
3
+ DEFAULT_CACHE_DIR="~/.codeforces-viewer/cache"
4
+ end
5
+ end
6
+
@@ -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,9 @@
1
+ module Codeforces
2
+ module Viewer
3
+ module Utils
4
+ def self.get_problem_url contest_id, problem_id
5
+ "http://codeforces.com/contest/#{contest_id}/problem/#{problem_id}"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ module Codeforces
2
+ module Viewer
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -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,5 @@
1
+ require_relative 'viewer/application'
2
+ require_relative 'viewer/viewer'
3
+ require_relative 'viewer/cacher'
4
+ require_relative 'viewer/render'
5
+ require_relative 'viewer/version'
@@ -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 &quot;extra&quot; 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 &quot;extra&quot; 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>
@@ -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