diff --git a/.github/workflows/luarocks.yml b/.github/workflows/luarocks.yaml similarity index 96% rename from .github/workflows/luarocks.yml rename to .github/workflows/luarocks.yaml index f3460d1..c64568f 100644 --- a/.github/workflows/luarocks.yml +++ b/.github/workflows/luarocks.yaml @@ -3,7 +3,7 @@ name: Release on: push: tags: - - "*" + - '*' workflow_dispatch: jobs: diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yaml similarity index 100% rename from .github/workflows/quality.yml rename to .github/workflows/quality.yaml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yaml similarity index 100% rename from .github/workflows/test.yml rename to .github/workflows/test.yaml diff --git a/.luarc.json b/.luarc.json index 02ba89e..3ccfeda 100644 --- a/.luarc.json +++ b/.luarc.json @@ -1,16 +1,8 @@ { "runtime.version": "Lua 5.1", - "runtime.path": [ - "lua/?.lua", - "lua/?/init.lua" - ], - "diagnostics.globals": [ - "vim" - ], - "workspace.library": [ - "$VIMRUNTIME/lua", - "${3rd}/luv/library" - ], + "runtime.path": ["lua/?.lua", "lua/?/init.lua"], + "diagnostics.globals": ["vim"], + "workspace.library": ["$VIMRUNTIME/lua", "${3rd}/luv/library"], "workspace.checkThirdParty": false, "completion.callSnippet": "Replace" } diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 74499e8..4702e92 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,4 +1,4 @@ -minimum_pre_commit_version: "3.5.0" +minimum_pre_commit_version: '3.5.0' repos: - repo: https://github.com/JohnnyMorganz/StyLua @@ -17,7 +17,7 @@ repos: files: \.py$ - id: ruff name: ruff (lint imports) - args: ["--fix", "--select=I"] + args: ['--fix', '--select=I'] files: \.py$ - repo: local @@ -26,7 +26,7 @@ repos: name: mypy (type check) entry: uv run mypy language: system - args: ["."] + args: ['.'] pass_filenames: false - repo: https://github.com/pre-commit/mirrors-prettier @@ -35,4 +35,3 @@ repos: - id: prettier name: prettier (format markdown) files: \.md$ - diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..72a3fa4 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,8 @@ +.pytest_cache/ +node_modules/ +.venv/ +build/ +dist/ +*.pyc +__pycache__/ +tests/fixtures/ diff --git a/README.md b/README.md index 9380c63..7130889 100644 --- a/README.md +++ b/README.md @@ -2,22 +2,28 @@ **The definitive competitive programming environment for Neovim** -Scrape problems, run tests, and debug solutions across multiple platforms with zero configuration. +Scrape problems, run tests, and debug solutions across multiple platforms with +zero configuration. https://github.com/user-attachments/assets/50b19481-8e6d-47b4-bebc-15e16c61a9c9 ## Features -- **Multi-platform support**: AtCoder, Codeforces, CSES with consistent interface +- **Multi-platform support**: AtCoder, Codeforces, CSES with consistent + interface - **Automatic problem setup**: Scrape test cases and metadata in seconds -- **Rich test output**: 256 color ANSI support for compiler errors and program output +- **Dual view modes**: Lightweight I/O view for quick feedback, full panel for + detailed analysis +- **Rich test output**: 256 color ANSI support for compiler errors and program + output - **Language agnostic**: Works with any language - **Diff viewer**: Compare expected vs actual output with 3 diff modes ## Optional Dependencies - [uv](https://docs.astral.sh/uv/) for problem scraping -- GNU [time](https://www.gnu.org/software/time/) and [timeout](https://www.gnu.org/software/coreutils/manual/html_node/timeout-invocation.html) +- GNU [time](https://www.gnu.org/software/time/) and + [timeout](https://www.gnu.org/software/coreutils/manual/html_node/timeout-invocation.html) ## Quick Start @@ -32,10 +38,11 @@ cp.nvim follows a simple principle: **solve locally, submit remotely**. :CP codeforces 1848 ``` -3. **Code and test** with instant feedback and rich diffs +3. **Code and test** with instant feedback ``` - :CP run + :CP run " Quick verdict summary in splits + :CP panel " Detailed analysis with diffs ``` 4. **Navigate between problems** @@ -54,7 +61,9 @@ cp.nvim follows a simple principle: **solve locally, submit remotely**. :help cp.nvim ``` -See [my config](https://github.com/barrett-ruth/dots/blob/main/nvim/lua/plugins/cp.lua) for a relatively advanced setup. +See +[my config](https://github.com/barrett-ruth/dots/blob/main/nvim/lua/plugins/cp.lua) +for a relatively advanced setup. ## Similar Projects diff --git a/doc/cp.nvim.txt b/doc/cp.nvim.txt index 5dd17b1..c429314 100644 --- a/doc/cp.nvim.txt +++ b/doc/cp.nvim.txt @@ -4,7 +4,7 @@ Author: Barrett Ruth License: Same terms as Vim itself (see |license|) ============================================================================== -INTRODUCTION *cp.nvim* +INTRODUCTION *cp.nvim* cp.nvim is a competitive programming plugin that automates problem setup, compilation, and testing workflow for online judges. @@ -12,16 +12,16 @@ compilation, and testing workflow for online judges. Supported platforms (for now!): AtCoder, Codeforces, CSES ============================================================================== -REQUIREMENTS *cp-requirements* +REQUIREMENTS *cp-requirements* - Neovim 0.10.0+ - Unix-like operating system - uv package manager (https://docs.astral.sh/uv/) ============================================================================== -COMMANDS *cp-commands* +COMMANDS *cp-commands* -:CP *:CP* +:CP *:CP* cp.nvim uses a single :CP command with intelligent argument parsing: Setup Commands ~ @@ -38,14 +38,23 @@ COMMANDS *cp-commands* Example: > :CP atcoder abc324 < - Action Commands ~ - :CP run Toggle run panel for individual test cases. - Shows per-test results with redesigned - layout for efficient comparison. - - :CP debug - Same as above but with the debug mode configured - settings. + View Commands ~ + :CP run [n] Run tests in I/O view (see |cp-io-view|). + Lightweight split showing test verdicts. + Without [n]: runs all tests, shows verdict summary + With [n]: runs test n, shows detailed output + Examples: > + :CP run " All tests, verdict list + :CP run 3 " Test 3 detail +< + :CP panel [n] Open full-screen test panel (see |cp-panel|). + Aggregate table with diff modes for detailed analysis. + Optional [n] focuses on specific test. + Example: > + :CP panel " All tests with diffs + :CP panel 2 " Focus on test 2 +< + :CP debug [n] Same as :CP panel but uses debug build configuration. :CP pick Launch configured picker for interactive platform/contest selection. @@ -84,7 +93,7 @@ COMMANDS *cp-commands* Exit with q. Template Variables ~ - *cp-template-vars* + *cp-template-vars* Command templates support variable substitution using {variable} syntax: • {source} Source file path (e.g. "abc324a.cpp") @@ -97,7 +106,7 @@ Template Variables ~ < ============================================================================== -CONFIGURATION *cp-config* +CONFIGURATION *cp-config* Here's an example configuration with lazy.nvim: >lua @@ -145,8 +154,8 @@ Here's an example configuration with lazy.nvim: open_url = true, debug = false, ui = { + ansi = true, panel = { - ansi = true, diff_mode = 'vim', max_output_lines = 50, }, @@ -197,7 +206,7 @@ run CSES problems with Rust using the single schema: }, } < - *cp.Config* + *cp.Config* Fields: ~ {languages} (table) Global language registry. Each language provides an {extension} and {commands}. @@ -214,19 +223,19 @@ run CSES problems with Rust using the single schema: {open_url} (boolean) Open the contest & problem url in the browser when the contest is first opened. - *CpPlatform* + *CpPlatform* Fields: ~ {enabled_languages} (string[]) Language ids enabled on this platform. {default_language} (string) One of {enabled_languages}. {overrides} (table, optional) Per-language overrides of {extension} and/or {commands}. - *CpLanguage* + *CpLanguage* Fields: ~ {extension} (string) File extension without leading dot. {commands} (|CpLangCommands|) Command templates. - *CpLangCommands* + *CpLangCommands* Fields: ~ {build} (string[], optional) For compiled languages. Must include {source} and {binary}. @@ -236,25 +245,25 @@ run CSES problems with Rust using the single schema: {debug} (string[], optional) Debug variant; same token rules as {build} (compiled) or {run} (interpreted). - *CpUI* + *CpUI* Fields: ~ + {ansi} (boolean, default: true) Enable ANSI color parsing + and highlighting in both I/O view and panel. {panel} (|PanelConfig|) Test panel behavior configuration. {diff} (|DiffConfig|) Diff backend configuration. {picker} (string|nil) 'telescope', 'fzf-lua', or nil. - *cp.PanelConfig* + *cp.PanelConfig* Fields: ~ - {ansi} (boolean, default: true) Enable ANSI color parsing - and highlighting. {diff_mode} (string, default: "none") Diff backend: "none", "vim", or "git". {max_output_lines} (number, default: 50) Maximum lines of test output. - *cp.DiffConfig* + *cp.DiffConfig* Fields: ~ {git} (|cp.DiffGitConfig|) Git diff backend configuration. - *cp.DiffGitConfig* + *cp.DiffGitConfig* Fields: ~ {args} (string[]) Command-line arguments for git diff. Default: { 'diff', '--no-index', '--word-diff=plain', @@ -264,40 +273,56 @@ run CSES problems with Rust using the single schema: • --word-diff-regex=.: Split on every character • --no-prefix: Remove a/ b/ prefixes from output - *cp.Hooks* + *cp.Hooks* Fields: ~ - {before_run} (function, optional) Called before test panel opens. - function(state: cp.State) - {before_debug} (function, optional) Called before debug build/run. - function(state: cp.State) - {setup_code} (function, optional) Called after source file is opened. - function(state: cp.State) + {before_run} (function, optional) Called before test panel opens. + function(state: cp.State) + {before_debug} (function, optional) Called before debug build/run. + function(state: cp.State) + {setup_code} (function, optional) Called after source file is opened. + function(state: cp.State) + {setup_io_input} (function, optional) Called when I/O input buffer created. + function(bufnr: integer, state: cp.State) + Default: helpers.clearcol (removes line numbers/columns) + {setup_io_output} (function, optional) Called when I/O output buffer created. + function(bufnr: integer, state: cp.State) + Default: helpers.clearcol (removes line numbers/columns) - Hook functions receive the cp.nvim state object (cp.State). See the state - module documentation (lua/cp/state.lua) for available methods and fields. + Hook functions receive the cp.nvim state object (|cp.State|). See + |lua/cp/state.lua| for available methods and fields. - Example usage in hook: + The I/O buffer hooks are called once when the buffers are first created + during problem setup. Use these to customize buffer appearance (e.g., + remove line numbers, set custom options). Access helpers via: +>lua + local helpers = require('cp').helpers +< + Example usage: >lua hooks = { setup_code = function(state) print("Setting up " .. state.get_base_name()) print("Source file: " .. state.get_source_file()) + end, + setup_io_input = function(bufnr, state) + -- Custom setup for input buffer + vim.api.nvim_set_option_value('number', false, { buf = bufnr }) end } < ============================================================================== -WORKFLOW *cp-workflow* +WORKFLOW *cp-workflow* For the sake of consistency and simplicity, cp.nvim extracts contest/problem identifiers from URLs. This means that, for example, CodeForces/AtCoder contests are configured by their round id rather than round number. See below. ============================================================================== -PLATFORM-SPECIFIC USAGE *cp-platforms* +PLATFORM-SPECIFIC USAGE *cp-platforms* AtCoder ~ - *cp-atcoder* + *cp-atcoder* URL format: https://atcoder.jp/contests/{contest_id}/tasks/{contest_id}_{problem_id} @@ -305,14 +330,14 @@ Usage examples: > :CP atcoder abc324 " Set up atcoder.jp/contests/abc324 Codeforces ~ - *cp-codeforces* + *cp-codeforces* URL format: https://codeforces.com/contest/{contest_id}/problem/{problem_id} Usage examples: > :CP codeforces 1934 " Set up codeforces.com/contest/1934 CSES ~ - *cp-cses* + *cp-cses* URL format: https://cses.fi/problemset/task/{problem_id} Usage examples: > @@ -320,7 +345,7 @@ Usage examples: > ============================================================================== -COMPLETE WORKFLOW EXAMPLE *cp-example* +COMPLETE WORKFLOW EXAMPLE *cp-example* Example: Setting up and solving AtCoder contest ABC324 @@ -333,8 +358,9 @@ Example: Setting up and solving AtCoder contest ABC324 3. Code your solution, then test: > :CP run -< Navigate with j/k, run specific tests with - Exit test panel with q or :CP run when done +< View test verdicts in I/O splits. For detailed analysis: + :CP panel +< Navigate tests with /, exit with q 4. Move to next problem: > :CP next @@ -350,28 +376,68 @@ Example: Setting up and solving AtCoder contest ABC324 7. Submit solutions on AtCoder website ============================================================================== -PICKER INTEGRATION *cp-picker* +I/O VIEW *cp-io-view* + +The I/O view provides the main view aggregate view into test input and +program output. Used time/memory per test case are appended to the output. +The |cp-panel| offers more fine-grained analysis into each test case. + +Access the I/O view with :CP run [n] + +Layout ~ + +The I/O view appears as 30% width splits on the right side: > + + ┌──────────────────────────┬──────────────────────────┐ + │ │ Output │ + │ │ Test 1: AC (42ms, 8MB) │ + │ │ Test 2: AC (38ms, 8MB) │ + │ Solution Code │ Test 3: WA (45ms, 8MB) │ + │ │ Test 4: AC (51ms, 9MB) │ + │ ├──────────────────────────┤ + │ │ Input │ + │ │ 5 3 │ + │ │ 1 2 3 4 5 │ + │ │ 2 1 │ + │ │ 10 20 │ + └──────────────────────────┴──────────────────────────┘ +< +Usage ~ + + :CP run Run all tests + :CP run 3 Run test 3 + +Buffer Customization ~ + +Use the setup_io_input and setup_io_output hooks (see |cp.Hooks|) to customize +buffer appearance. By default, line numbers and columns are removed via +helpers.clearcol (see |cp-helpers|). + +============================================================================== +PICKER INTEGRATION *cp-picker* When picker integration is enabled in configuration, cp.nvim provides interactive platform and contest selection using telescope.nvim or fzf-lua. -:CP pick *:CP-pick* +:CP pick *:CP-pick* Launch configured picker for interactive problem selection. Control Flow: Select Platform → Contest → Code! Requires picker = 'telescope' or picker = 'fzf-lua' in configuration. Requires corresponding plugin (telescope.nvim or fzf-lua) to be installed. -PICKER KEYMAPS *cp-picker-keys* +PICKER KEYMAPS *cp-picker-keys* Force refresh/update contest list. Useful when contest lists are outdated or incomplete ============================================================================== -PANEL *cp-run* +PANEL *cp-panel* -The panel provides individual test case debugging. Problem time/memory -limit constraints are in columns Time/Mem respectively. Used time/memory are -in columns Runtime/RSS respectively. +The panel provides full-screen test analysis with diff modes for detailed +debugging. Problem time/memory limit constraints are in columns Time/Mem +respectively. Used time/memory are in columns Runtime/RSS respectively. + +Access with :CP panel or :CP debug (uses debug build configuration). Interface ~ @@ -409,7 +475,7 @@ Test cases use competitive programming terminology with color highlighting: < ============================================================================== -INTERACTIVE MODE *cp-interact* +INTERACTIVE MODE *cp-interact* Run interactive problems manually or with an orchestrator. :CP interact is available for interactive problems. Test cases are ignored in interactive mode @@ -436,7 +502,7 @@ Keymaps ~ Close the terminal and restore the previous layout. ============================================================================== -ANSI COLORS AND HIGHLIGHTING *cp-ansi* +ANSI COLORS AND HIGHLIGHTING *cp-ansi* cp.nvim provides comprehensive ANSI color support and highlighting for compiler output, program stderr, and diff visualization. @@ -459,7 +525,7 @@ alter your config as follows: < ============================================================================== -HIGHLIGHT GROUPS *cp-highlights* +HIGHLIGHT GROUPS *cp-highlights* Test Status Groups ~ @@ -485,13 +551,13 @@ adapt to your colorscheme: DiffDelete Highlights removed text in git diffs ============================================================================== -TERMINAL COLOR INTEGRATION *cp-terminal-colors* +TERMINAL COLOR INTEGRATION *cp-terminal-colors* ANSI colors automatically use the terminal's color palette through Neovim's vim.g.terminal_color_* variables. ============================================================================== -HIGHLIGHT CUSTOMIZATION *cp-highlight-custom* +HIGHLIGHT CUSTOMIZATION *cp-highlight-custom* Customize highlight groups after your colorscheme loads: >lua @@ -502,12 +568,33 @@ Customize highlight groups after your colorscheme loads: }) ============================================================================== -PANEL KEYMAPS *cp-test-keys* +HELPERS *cp-helpers* + +The helpers module provides utility functions for buffer customization. +Access via: +>lua + local helpers = require('cp').helpers +< +Functions ~ + + helpers.clearcol({bufnr}) *helpers.clearcol* + Remove line numbers, columns, and signs from buffer. + Sets: + • number = false + • relativenumber = false + • signcolumn = 'no' + • statuscolumn = '' + + Parameters: ~ + {bufnr} (integer) Buffer handle + +============================================================================== +PANEL KEYMAPS *cp-panel-keys* Navigate to next test case Navigate to previous test case t Cycle through diff modes: none → git → vim -q Exit run panel and restore layout +q Exit panel and restore layout Exit interactive terminal and restore layout Diff Modes ~ @@ -528,7 +615,7 @@ execution pipeline, but with isolated input/output for precise failure analysis. ============================================================================== -FILE STRUCTURE *cp-files* +FILE STRUCTURE *cp-files* cp.nvim creates the following file structure upon problem setup: > @@ -537,11 +624,10 @@ cp.nvim creates the following file structure upon problem setup: > {problem_id}.run " Compiled binary io/ {problem_id}.n.cpin " nth test input - {problem_id}.n.cpout " nth program output - {problem_id}.expected " Expected output + {problem_id}.n.cpout " nth test expected output < ============================================================================== -HEALTH CHECK *cp-health* +HEALTH CHECK *cp-health* Run |:checkhealth| cp to verify your setup. diff --git a/lua/cp/commands/init.lua b/lua/cp/commands/init.lua index a7bd626..319f952 100644 --- a/lua/cp/commands/init.lua +++ b/lua/cp/commands/init.lua @@ -16,6 +16,7 @@ local actions = constants.ACTIONS ---@field platform? string ---@field problem_id? string ---@field interactor_cmd? string +---@field test_index? integer --- Turn raw args into normalized structure to later dispatch ---@param args string[] The raw command-line mode args @@ -52,6 +53,23 @@ local function parse_command(args) else return { type = 'action', action = 'interact' } end + elseif first == 'run' then + local test_arg = args[2] + if test_arg then + local test_index = tonumber(test_arg) + if not test_index then + return { + type = 'error', + message = ("Test index '%s' is not a number"):format(test_index), + } + end + if test_index < 1 or test_index ~= math.floor(test_index) then + return { type = 'error', message = ("'%s' is not a valid test index"):format(test_index) } + end + return { type = 'action', action = 'run', test_index = test_index } + else + return { type = 'action', action = 'run' } + end else return { type = 'action', action = first } end @@ -109,6 +127,8 @@ function M.handle_command(opts) if cmd.action == 'interact' then ui.toggle_interactive(cmd.interactor_cmd) elseif cmd.action == 'run' then + ui.run_io_view(cmd.test_index) + elseif cmd.action == 'panel' then ui.toggle_panel() elseif cmd.action == 'debug' then ui.toggle_panel({ debug = true }) diff --git a/lua/cp/config.lua b/lua/cp/config.lua index 4e787b4..54515ae 100644 --- a/lua/cp/config.lua +++ b/lua/cp/config.lua @@ -18,7 +18,6 @@ ---@field overrides? table ---@class PanelConfig ----@field ansi boolean ---@field diff_mode "none"|"vim"|"git" ---@field max_output_lines integer @@ -32,8 +31,11 @@ ---@field before_run? fun(state: cp.State) ---@field before_debug? fun(state: cp.State) ---@field setup_code? fun(state: cp.State) +---@field setup_io_input? fun(bufnr: integer, state: cp.State) +---@field setup_io_output? fun(bufnr: integer, state: cp.State) ---@class CpUI +---@field ansi boolean ---@field panel PanelConfig ---@field diff DiffConfig ---@field picker string|nil @@ -54,6 +56,7 @@ local M = {} local constants = require('cp.constants') +local helpers = require('cp.helpers') local utils = require('cp.utils') -- defaults per the new single schema @@ -101,12 +104,19 @@ M.defaults = { default_language = 'cpp', }, }, - hooks = { before_run = nil, before_debug = nil, setup_code = nil }, + hooks = { + before_run = nil, + before_debug = nil, + setup_code = nil, + setup_io_input = helpers.clearcol, + setup_io_output = helpers.clearcol, + }, debug = false, scrapers = constants.PLATFORMS, filename = nil, ui = { - panel = { ansi = true, diff_mode = 'none', max_output_lines = 50 }, + ansi = true, + panel = { diff_mode = 'none', max_output_lines = 50 }, diff = { git = { args = { 'diff', '--no-index', '--word-diff=plain', '--word-diff-regex=.', '--no-prefix' }, @@ -229,10 +239,12 @@ function M.setup(user_config) before_run = { cfg.hooks.before_run, { 'function', 'nil' }, true }, before_debug = { cfg.hooks.before_debug, { 'function', 'nil' }, true }, setup_code = { cfg.hooks.setup_code, { 'function', 'nil' }, true }, + setup_io_input = { cfg.hooks.setup_io_input, { 'function', 'nil' }, true }, + setup_io_output = { cfg.hooks.setup_io_output, { 'function', 'nil' }, true }, }) vim.validate({ - ansi = { cfg.ui.panel.ansi, 'boolean' }, + ansi = { cfg.ui.ansi, 'boolean' }, diff_mode = { cfg.ui.panel.diff_mode, function(v) diff --git a/lua/cp/constants.lua b/lua/cp/constants.lua index dce8751..310363f 100644 --- a/lua/cp/constants.lua +++ b/lua/cp/constants.lua @@ -1,7 +1,7 @@ local M = {} M.PLATFORMS = { 'atcoder', 'codeforces', 'cses' } -M.ACTIONS = { 'run', 'next', 'prev', 'pick', 'cache', 'interact' } +M.ACTIONS = { 'run', 'panel', 'debug', 'next', 'prev', 'pick', 'cache', 'interact' } M.PLATFORM_DISPLAY_NAMES = { atcoder = 'AtCoder', diff --git a/lua/cp/helpers.lua b/lua/cp/helpers.lua new file mode 100644 index 0000000..27c6cba --- /dev/null +++ b/lua/cp/helpers.lua @@ -0,0 +1,13 @@ +local M = {} + +---@param bufnr integer +function M.clearcol(bufnr) + for _, win in ipairs(vim.fn.win_findbuf(bufnr)) do + vim.wo[win].signcolumn = 'no' + vim.wo[win].statuscolumn = '' + vim.wo[win].number = false + vim.wo[win].relativenumber = false + end +end + +return M diff --git a/lua/cp/init.lua b/lua/cp/init.lua index 366517c..64a997d 100644 --- a/lua/cp/init.lua +++ b/lua/cp/init.lua @@ -1,8 +1,11 @@ local M = {} local config_module = require('cp.config') +local helpers = require('cp.helpers') local logger = require('cp.log') +M.helpers = helpers + if vim.fn.has('nvim-0.10.0') == 0 then logger.log('Requires nvim-0.10.0+', vim.log.levels.ERROR) return {} diff --git a/lua/cp/runner/run.lua b/lua/cp/runner/run.lua index 367d9ec..ab3d8af 100644 --- a/lua/cp/runner/run.lua +++ b/lua/cp/runner/run.lua @@ -122,7 +122,7 @@ local function run_single_test_case(test_case) local out = r.stdout or '' local highlights = {} if out ~= '' then - if config.ui.panel.ansi then + if config.ui.ansi then local parsed = ansi.parse_ansi_text(out) out = table.concat(parsed.lines, '\n') highlights = parsed.highlights @@ -224,14 +224,22 @@ function M.run_test_case(index) return true end +---@param indices? integer[] ---@return RanTestCase[] -function M.run_all_test_cases() - local results = {} - for i = 1, #panel_state.test_cases do - M.run_test_case(i) - results[i] = panel_state.test_cases[i] +function M.run_all_test_cases(indices) + local to_run = indices + if not to_run then + to_run = {} + for i = 1, #panel_state.test_cases do + to_run[i] = i + end end - return results + + for _, i in ipairs(to_run) do + M.run_test_case(i) + end + + return panel_state.test_cases end ---@return PanelState @@ -247,7 +255,7 @@ function M.handle_compilation_failure(output) local txt local hl = {} - if config.ui.panel.ansi then + if config.ui.ansi then local p = ansi.parse_ansi_text(output or '') txt = table.concat(p.lines, '\n') hl = p.highlights diff --git a/lua/cp/setup.lua b/lua/cp/setup.lua index 12fde62..d6e2f38 100644 --- a/lua/cp/setup.lua +++ b/lua/cp/setup.lua @@ -3,6 +3,7 @@ local M = {} local cache = require('cp.cache') local config_module = require('cp.config') local constants = require('cp.constants') +local helpers = require('cp.helpers') local logger = require('cp.log') local scraper = require('cp.scraper') local state = require('cp.state') @@ -59,6 +60,18 @@ local function start_tests(platform, contest_id, problems) ev.memory_mb or 0, ev.interactive ) + + local io_state = state.get_io_view_state() + if io_state then + local test_cases = cache.get_test_cases(platform, contest_id, state.get_problem_id()) + local input_lines = {} + for _, tc in ipairs(test_cases) do + for _, line in ipairs(vim.split(tc.input, '\n')) do + table.insert(input_lines, line) + end + end + require('cp.utils').update_buffer_content(io_state.input_buf, input_lines, nil, nil) + end end) end end @@ -152,6 +165,7 @@ end function M.setup_problem(problem_id, language) local platform = state.get_platform() if not platform then + logger.log('No platform/contest/problem configured.', vim.log.levels.ERROR) return end @@ -178,6 +192,9 @@ function M.setup_problem(problem_id, language) if ok then vim.b[prov.bufnr].cp_setup_done = true end + elseif not vim.b[prov.bufnr].cp_setup_done then + helpers.clearcol(prov.bufnr) + vim.b[prov.bufnr].cp_setup_done = true end cache.set_file_state( vim.fn.fnamemodify(source_file, ':p'), @@ -186,6 +203,7 @@ function M.setup_problem(problem_id, language) state.get_problem_id() or '', lang ) + require('cp.ui.panel').ensure_io_view() end state.set_provisional(nil) return @@ -201,6 +219,9 @@ function M.setup_problem(problem_id, language) if ok then vim.b[bufnr].cp_setup_done = true end + elseif not vim.b[bufnr].cp_setup_done then + helpers.clearcol(bufnr) + vim.b[bufnr].cp_setup_done = true end cache.set_file_state( vim.fn.expand('%:p'), @@ -209,6 +230,7 @@ function M.setup_problem(problem_id, language) state.get_problem_id() or '', lang ) + require('cp.ui.panel').ensure_io_view() end) end @@ -247,7 +269,11 @@ function M.navigate_problem(direction) return end - require('cp.ui.panel').disable() + local active_panel = state.get_active_panel() + if active_panel == 'run' then + require('cp.ui.panel').disable() + end + M.setup_contest(platform, contest_id, problems[new_index].id) end diff --git a/lua/cp/state.lua b/lua/cp/state.lua index c234387..4a3ff79 100644 --- a/lua/cp/state.lua +++ b/lua/cp/state.lua @@ -3,9 +3,15 @@ ---@field platform string ---@field contest_id string ---@field language string ----@field requested_problem_id string|nil +---@field requested_problem_id string? ---@field token integer +---@class cp.IoViewState +---@field output_buf integer +---@field input_buf integer +---@field output_win integer +---@field input_win integer + ---@class cp.State ---@field get_platform fun(): string? ---@field set_platform fun(platform: string) @@ -21,8 +27,12 @@ ---@field get_input_file fun(): string? ---@field get_output_file fun(): string? ---@field get_expected_file fun(): string? ----@field get_provisional fun(): cp.ProvisionalState|nil ----@field set_provisional fun(p: cp.ProvisionalState|nil) +---@field get_provisional fun(): cp.ProvisionalState? +---@field set_provisional fun(p: cp.ProvisionalState?) +---@field get_saved_session fun(): string? +---@field set_saved_session fun(path: string?) +---@field get_io_view_state fun(): cp.IoViewState? +---@field set_io_view_state fun(s: cp.IoViewState?) local M = {} @@ -36,9 +46,10 @@ local state = { active_panel = nil, provisional = nil, solution_win = nil, + io_view_state = nil, } ----@return string|nil +---@return string? function M.get_platform() return state.platform end @@ -48,7 +59,7 @@ function M.set_platform(platform) state.platform = platform end ----@return string|nil +---@return string? function M.get_contest_id() return state.contest_id end @@ -58,7 +69,7 @@ function M.set_contest_id(contest_id) state.contest_id = contest_id end ----@return string|nil +---@return string? function M.get_problem_id() return state.problem_id end @@ -68,7 +79,7 @@ function M.set_problem_id(problem_id) state.problem_id = problem_id end ----@return string|nil +---@return string? function M.get_base_name() local platform, contest_id, problem_id = M.get_platform(), M.get_contest_id(), M.get_problem_id() if not platform or not contest_id or not problem_id then @@ -86,7 +97,7 @@ function M.get_base_name() end ---@param language? string ----@return string|nil +---@return string? function M.get_source_file(language) local base_name = M.get_base_name() if not base_name or not M.get_platform() then @@ -110,46 +121,46 @@ function M.get_source_file(language) return base_name .. '.' .. eff.extension end ----@return string|nil +---@return string? function M.get_binary_file() local base_name = M.get_base_name() return base_name and ('build/%s.run'):format(base_name) or nil end ----@return string|nil +---@return string? function M.get_input_file() local base_name = M.get_base_name() return base_name and ('io/%s.cpin'):format(base_name) or nil end ----@return string|nil +---@return string? function M.get_output_file() local base_name = M.get_base_name() return base_name and ('io/%s.cpout'):format(base_name) or nil end ----@return string|nil +---@return string? function M.get_expected_file() local base_name = M.get_base_name() return base_name and ('io/%s.expected'):format(base_name) or nil end ----@return string|nil +---@return string? function M.get_active_panel() return state.active_panel end ----@param panel string|nil +---@param panel string? function M.set_active_panel(panel) state.active_panel = panel end ----@return cp.ProvisionalState|nil +---@return cp.ProvisionalState? function M.get_provisional() return state.provisional end ----@param p cp.ProvisionalState|nil +---@param p cp.ProvisionalState? function M.set_provisional(p) state.provisional = p end @@ -167,6 +178,38 @@ function M.set_solution_win(win) state.solution_win = win end +---@return cp.IoViewState? +function M.get_io_view_state() + if not state.io_view_state then + return nil + end + local s = state.io_view_state + if + vim.api.nvim_buf_is_valid(s.output_buf) + and vim.api.nvim_buf_is_valid(s.input_buf) + and vim.api.nvim_win_is_valid(s.output_win) + and vim.api.nvim_win_is_valid(s.input_win) + then + return s + end + return nil +end + +---@param s cp.IoViewState? +function M.set_io_view_state(s) + state.io_view_state = s +end + +---@return string? +function M.get_saved_session() + return state.saved_session +end + +---@param path string? +function M.set_saved_session(path) + state.saved_session = path +end + M._state = state return M diff --git a/lua/cp/ui/ansi.lua b/lua/cp/ui/ansi.lua index a72bcad..7d84d2d 100644 --- a/lua/cp/ui/ansi.lua +++ b/lua/cp/ui/ansi.lua @@ -321,6 +321,25 @@ function M.setup_highlight_groups() vim.api.nvim_set_hl(0, 'CpAnsiBold', { bold = true }) vim.api.nvim_set_hl(0, 'CpAnsiItalic', { italic = true }) vim.api.nvim_set_hl(0, 'CpAnsiBoldItalic', { bold = true, italic = true }) + + for _, combo in ipairs(combinations) do + for color_name, _ in pairs(color_map) do + local parts = { 'CpAnsi' } + if combo.bold then + table.insert(parts, 'Bold') + end + if combo.italic then + table.insert(parts, 'Italic') + end + table.insert(parts, color_name) + local hl_name = table.concat(parts) + dyn_hl_cache[hl_name] = true + end + end + + dyn_hl_cache['CpAnsiBold'] = true + dyn_hl_cache['CpAnsiItalic'] = true + dyn_hl_cache['CpAnsiBoldItalic'] = true end ---@param text string diff --git a/lua/cp/ui/layouts.lua b/lua/cp/ui/layouts.lua index d5470d1..730c17e 100644 --- a/lua/cp/ui/layouts.lua +++ b/lua/cp/ui/layouts.lua @@ -1,10 +1,13 @@ local M = {} +local helpers = require('cp.helpers') local utils = require('cp.utils') local function create_none_diff_layout(parent_win, expected_content, actual_content) local expected_buf = utils.create_buffer_with_options() local actual_buf = utils.create_buffer_with_options() + helpers.clearcol(expected_buf) + helpers.clearcol(actual_buf) vim.api.nvim_set_current_win(parent_win) vim.cmd.split() @@ -42,6 +45,8 @@ end local function create_vim_diff_layout(parent_win, expected_content, actual_content) local expected_buf = utils.create_buffer_with_options() local actual_buf = utils.create_buffer_with_options() + helpers.clearcol(expected_buf) + helpers.clearcol(actual_buf) vim.api.nvim_set_current_win(parent_win) vim.cmd.split() @@ -89,6 +94,7 @@ end local function create_git_diff_layout(parent_win, expected_content, actual_content) local diff_buf = utils.create_buffer_with_options() + helpers.clearcol(diff_buf) vim.api.nvim_set_current_win(parent_win) vim.cmd.split() diff --git a/lua/cp/ui/panel.lua b/lua/cp/ui/panel.lua index 9b1ab05..ce846ba 100644 --- a/lua/cp/ui/panel.lua +++ b/lua/cp/ui/panel.lua @@ -3,8 +3,9 @@ local M = {} ---@class PanelOpts ---@field debug? boolean +local cache = require('cp.cache') local config_module = require('cp.config') -local constants = require('cp.constants') +local helpers = require('cp.helpers') local layouts = require('cp.ui.layouts') local logger = require('cp.log') local state = require('cp.state') @@ -52,38 +53,24 @@ function M.toggle_interactive(interactor_cmd) return end - local platform, contest_id = state.get_platform(), state.get_contest_id() - if not platform then - logger.log('No platform configured.', vim.log.levels.ERROR) - return - end - if not contest_id then + local platform, contest_id, problem_id = + state.get_platform(), state.get_contest_id(), state.get_problem_id() + if not platform or not contest_id or not problem_id then logger.log( - ("No contest %s configured for platform '%s'."):format( - contest_id, - constants.PLATFORM_DISPLAY_NAMES[platform] - ), + 'No platform/contest/problem configured. Use :CP [...] first.', vim.log.levels.ERROR ) return end - local problem_id = state.get_problem_id() - if not problem_id then - logger.log('No problem is active.', vim.log.levels.ERROR) - return - end - - local cache = require('cp.cache') cache.load() local contest_data = cache.get_contest_data(platform, contest_id) if not contest_data or not contest_data.index_map - or not contest_data.problems[contest_data.index_map[problem_id]] - or not contest_data.problems[contest_data.index_map[problem_id]].interactive + or contest_data.problems[contest_data.index_map[problem_id]].interactive then - logger.log('This problem is not interactive. Use :CP run.', vim.log.levels.ERROR) + logger.log('This problem is interactive. Use :CP {run,panel}.', vim.log.levels.ERROR) return end @@ -92,9 +79,10 @@ function M.toggle_interactive(interactor_cmd) vim.cmd('silent only') local execute = require('cp.runner.execute') + local run = require('cp.runner.run') local compile_result = execute.compile_problem() if not compile_result.success then - require('cp.runner.run').handle_compilation_failure(compile_result.output) + run.handle_compilation_failure(compile_result.output) return end @@ -204,6 +192,230 @@ function M.toggle_interactive(interactor_cmd) state.set_active_panel('interactive') end +function M.ensure_io_view() + local platform, contest_id, problem_id = + state.get_platform(), state.get_contest_id(), state.get_problem_id() + if not platform or not contest_id or not problem_id then + logger.log( + 'No platform/contest/problem configured. Use :CP [...] first.', + vim.log.levels.ERROR + ) + return + end + + cache.load() + local contest_data = cache.get_contest_data(platform, contest_id) + if + contest_data + and contest_data.index_map + and contest_data.problems[contest_data.index_map[problem_id]].interactive + then + logger.log('No platform configured.', vim.log.levels.ERROR) + return + end + + local solution_win = state.get_solution_win() + local io_state = state.get_io_view_state() + local output_buf, input_buf, output_win, input_win + + if io_state then + output_buf = io_state.output_buf + input_buf = io_state.input_buf + output_win = io_state.output_win + input_win = io_state.input_win + else + vim.api.nvim_set_current_win(solution_win) + + vim.cmd.vsplit() + output_win = vim.api.nvim_get_current_win() + local width = math.floor(vim.o.columns * 0.3) + vim.api.nvim_win_set_width(output_win, width) + output_buf = utils.create_buffer_with_options() + vim.api.nvim_win_set_buf(output_win, output_buf) + + vim.cmd.split() + input_win = vim.api.nvim_get_current_win() + input_buf = utils.create_buffer_with_options() + vim.api.nvim_win_set_buf(input_win, input_buf) + + state.set_io_view_state({ + output_buf = output_buf, + input_buf = input_buf, + output_win = output_win, + input_win = input_win, + }) + + local config = config_module.get_config() + if config.hooks and config.hooks.setup_io_output then + pcall(config.hooks.setup_io_output, output_buf, state) + end + + if config.hooks and config.hooks.setup_io_input then + pcall(config.hooks.setup_io_input, input_buf, state) + end + end + + utils.update_buffer_content(input_buf, {}) + utils.update_buffer_content(output_buf, {}) + + local test_cases = cache.get_test_cases(platform, contest_id, problem_id) + if test_cases and #test_cases > 0 then + local input_lines = {} + for _, tc in ipairs(test_cases) do + for _, line in ipairs(vim.split(tc.input, '\n')) do + table.insert(input_lines, line) + end + end + utils.update_buffer_content(input_buf, input_lines, nil, nil) + end + + vim.api.nvim_set_current_win(solution_win) +end + +function M.run_io_view(test_index) + local platform, contest_id, problem_id = + state.get_platform(), state.get_contest_id(), state.get_problem_id() + if not platform or not contest_id or not problem_id then + logger.log( + 'No platform/contest/problem configured. Use :CP [...] first.', + vim.log.levels.ERROR + ) + return + end + + cache.load() + local contest_data = cache.get_contest_data(platform, contest_id) + if + not contest_data + or not contest_data.index_map + or contest_data.problems[contest_data.index_map[problem_id]].interactive + then + logger.log('This problem is interactive. Use :CP {run,panel}.', vim.log.levels.ERROR) + return + end + + M.ensure_io_view() + + local run = require('cp.runner.run') + if not run.load_test_cases() then + logger.log('No test cases available', vim.log.levels.ERROR) + return + end + + local test_state = run.get_panel_state() + local test_indices = {} + + if test_index then + if test_index < 1 or test_index > #test_state.test_cases then + logger.log( + string.format( + 'Test %d does not exist (only %d tests available)', + test_index, + #test_state.test_cases + ), + vim.log.levels.WARN + ) + return + end + test_indices = { test_index } + else + for i = 1, #test_state.test_cases do + test_indices[i] = i + end + end + + local io_state = state.get_io_view_state() + if not io_state then + return + end + + local config = config_module.get_config() + + if config.ui.ansi then + require('cp.ui.ansi').setup_highlight_groups() + end + + local execute = require('cp.runner.execute') + local compile_result = execute.compile_problem() + if not compile_result.success then + local ansi = require('cp.ui.ansi') + local output = compile_result.output or '' + local lines, highlights + + if config.ui.ansi then + local parsed = ansi.parse_ansi_text(output) + lines = parsed.lines + highlights = parsed.highlights + else + lines = vim.split(output:gsub('\027%[[%d;]*[a-zA-Z]', ''), '\n') + highlights = {} + end + + local ns = vim.api.nvim_create_namespace('cp_io_view_compile_error') + utils.update_buffer_content(io_state.output_buf, lines, highlights, ns) + return + end + + run.run_all_test_cases(test_indices) + + local input_lines = {} + for _, idx in ipairs(test_indices) do + local tc = test_state.test_cases[idx] + for _, line in ipairs(vim.split(tc.input, '\n')) do + table.insert(input_lines, line) + end + end + utils.update_buffer_content(io_state.input_buf, input_lines, nil, nil) + + local run_render = require('cp.runner.run_render') + run_render.setup_highlights() + + if #test_indices == 1 then + local idx = test_indices[1] + local tc = test_state.test_cases[idx] + local status = run_render.get_status_info(tc) + + local output_lines = {} + if tc.actual then + for _, line in ipairs(vim.split(tc.actual, '\n', { plain = true, trimempty = false })) do + table.insert(output_lines, line) + end + end + + table.insert(output_lines, '') + local time = tc.time_ms and string.format('%.2fms', tc.time_ms) or '—' + local code = tc.code and tostring(tc.code) or '—' + table.insert(output_lines, string.format('--- %s: %s | Exit: %s ---', status.text, time, code)) + + local highlights = tc.actual_highlights or {} + local ns = vim.api.nvim_create_namespace('cp_io_view_output') + utils.update_buffer_content(io_state.output_buf, output_lines, highlights, ns) + else + local verdict_lines = {} + local verdict_highlights = {} + for _, idx in ipairs(test_indices) do + local tc = test_state.test_cases[idx] + local status = run_render.get_status_info(tc) + local time = tc.time_ms and string.format('%.2f', tc.time_ms) or '—' + local mem = tc.rss_mb and string.format('%.0f', tc.rss_mb) or '—' + local line = string.format('Test %d: %s (%sms, %sMB)', idx, status.text, time, mem) + table.insert(verdict_lines, line) + local status_pos = line:find(status.text, 1, true) + if status_pos then + table.insert(verdict_highlights, { + line = #verdict_lines - 1, + col_start = status_pos - 1, + col_end = status_pos - 1 + #status.text, + highlight_group = status.highlight_group, + }) + end + end + + local verdict_ns = vim.api.nvim_create_namespace('cp_io_view_verdict') + utils.update_buffer_content(io_state.output_buf, verdict_lines, verdict_highlights, verdict_ns) + end +end + ---@param panel_opts? PanelOpts function M.toggle_panel(panel_opts) if state.get_active_panel() == 'run' then @@ -212,12 +424,14 @@ function M.toggle_panel(panel_opts) current_diff_layout = nil current_mode = nil end - if state.saved_session then - vim.cmd(('source %s'):format(state.saved_session)) - vim.fn.delete(state.saved_session) - state.saved_session = nil + local saved = state.get_saved_session() + if saved then + vim.cmd(('source %s'):format(saved)) + vim.fn.delete(saved) + state.set_saved_session(nil) end state.set_active_panel(nil) + M.ensure_io_view() return end @@ -228,26 +442,14 @@ function M.toggle_panel(panel_opts) local platform, contest_id = state.get_platform(), state.get_contest_id() - if not platform then + if not platform or not contest_id then logger.log( - 'No platform configured. Use :CP [...] first.', + 'No platform/contest configured. Use :CP [...] first.', vim.log.levels.ERROR ) return end - if not contest_id then - logger.log( - ("No contest '%s' configured for platform '%s'."):format( - contest_id, - constants.PLATFORM_DISPLAY_NAMES[platform] - ), - vim.log.levels.ERROR - ) - return - end - - local cache = require('cp.cache') cache.load() local contest_data = cache.get_contest_data(platform, contest_id) if @@ -260,6 +462,7 @@ function M.toggle_panel(panel_opts) local config = config_module.get_config() local run = require('cp.runner.run') + local run_render = require('cp.runner.run_render') local input_file = state.get_input_file() logger.log(('run panel: checking test cases for %s'):format(input_file or 'none')) @@ -268,11 +471,24 @@ function M.toggle_panel(panel_opts) return end - state.saved_session = vim.fn.tempname() - vim.cmd(('mksession! %s'):format(state.saved_session)) + local io_state = state.get_io_view_state() + if io_state then + if vim.api.nvim_win_is_valid(io_state.output_win) then + vim.api.nvim_win_close(io_state.output_win, true) + end + if vim.api.nvim_win_is_valid(io_state.input_win) then + vim.api.nvim_win_close(io_state.input_win, true) + end + state.set_io_view_state(nil) + end + + local session_file = vim.fn.tempname() + state.set_saved_session(session_file) + vim.cmd(('mksession! %s'):format(session_file)) vim.cmd('silent only') local tab_buf = utils.create_buffer_with_options() + helpers.clearcol(tab_buf) local main_win = vim.api.nvim_get_current_win() vim.api.nvim_win_set_buf(main_win, tab_buf) vim.api.nvim_set_option_value('filetype', 'cp', { buf = tab_buf }) @@ -298,7 +514,6 @@ function M.toggle_panel(panel_opts) if not test_buffers.tab_buf or not vim.api.nvim_buf_is_valid(test_buffers.tab_buf) then return end - local run_render = require('cp.runner.run_render') run_render.setup_highlights() local test_state = run.get_panel_state() local tab_lines, tab_highlights = run_render.render_test_list(test_state) @@ -368,9 +583,8 @@ function M.toggle_panel(panel_opts) refresh_panel() vim.schedule(function() - if config.ui.panel.ansi then - local ansi = require('cp.ui.ansi') - ansi.setup_highlight_groups() + if config.ui.ansi then + require('cp.ui.ansi').setup_highlight_groups() end if current_diff_layout then update_diff_panes() diff --git a/lua/cp/utils.lua b/lua/cp/utils.lua index 6ce2311..e9bba54 100644 --- a/lua/cp/utils.lua +++ b/lua/cp/utils.lua @@ -125,6 +125,10 @@ function M.create_buffer_with_options(filetype) return buf end +---@param bufnr integer +---@param lines string[] +---@param highlights? Highlight[] +---@param namespace? integer function M.update_buffer_content(bufnr, lines, highlights, namespace) local was_readonly = vim.api.nvim_get_option_value('readonly', { buf = bufnr }) diff --git a/scrapers/cses.py b/scrapers/cses.py index b8a6145..c66da96 100644 --- a/scrapers/cses.py +++ b/scrapers/cses.py @@ -72,24 +72,24 @@ async def fetch_text(client: httpx.AsyncClient, path: str) -> str: CATEGORY_BLOCK_RE = re.compile( - r'

(?P[^<]+)

\s*
    (?P.*?)
', + r'

(?P[^<]+)

\s*(?P.*?)', re.DOTALL, ) TASK_LINK_RE = re.compile( - r'
  • (?P[^<]+)</a>', + r'<li\s+class="task">\s*<a\s+href="/problemset/task/(?P<id>\d+)/?">(?P<title>[^<]+)</a\s*>', re.DOTALL, ) TITLE_RE = re.compile( - r'<div class="title-block">.*?<h1>(?P<title>[^<]+)</h1>', re.DOTALL + r'<div\s+class="title-block">.*?<h1>(?P<title>[^<]+)</h1>', re.DOTALL ) -TIME_RE = re.compile(r"<li><b>Time limit:</b>\s*([0-9.]+)\s*s</li>") -MEM_RE = re.compile(r"<li><b>Memory limit:</b>\s*(\d+)\s*MB</li>") +TIME_RE = re.compile(r"<li>\s*<b>Time limit:</b>\s*([0-9.]+)\s*s\s*</li>") +MEM_RE = re.compile(r"<li>\s*<b>Memory limit:</b>\s*(\d+)\s*MB\s*</li>") SIDEBAR_CAT_RE = re.compile( - r'<div class="nav sidebar">.*?<h4>(?P<cat>[^<]+)</h4>', re.DOTALL + r'<div\s+class="nav sidebar">.*?<h4>(?P<cat>[^<]+)</h4>', re.DOTALL ) -MD_BLOCK_RE = re.compile(r'<div class="md">(.*?)</div>', re.DOTALL | re.IGNORECASE) +MD_BLOCK_RE = re.compile(r'<div\s+class="md">(.*?)</div>', re.DOTALL | re.IGNORECASE) EXAMPLE_SECTION_RE = re.compile( r"<h[1-6][^>]*>\s*example[s]?:?\s*</h[1-6]>\s*(?P<section>.*?)(?=<h[1-6][^>]*>|$)", re.DOTALL | re.IGNORECASE, diff --git a/tests/fixtures/atcoder_task_abc100_a.html b/tests/fixtures/atcoder_task_abc100_a.html index 2e0e623..7d34b21 100644 --- a/tests/fixtures/atcoder_task_abc100_a.html +++ b/tests/fixtures/atcoder_task_abc100_a.html @@ -1,602 +1,885 @@ - - - - - - - - -<!DOCTYPE html> +<!doctype html> <html> -<head> - <title>A - Happy Birthday! - - - - - + + A - Happy Birthday! + + + + + - - - + + - - - + + - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + - + - - + + + + + + + + + + + + + + + + - - -
    + + + + + - + -
    - -
    -
    - + + + + + -
    -
    -
    - - - Contest Duration: - - (local time) - (100 minutes) - - - Back to Home -
    - +
    +
    + + A - Happy Birthday! + Editorial + + + / + + +
    +

    Time Limit: 2 sec / Memory Limit: 976 MiB

    -
    +
    + + +

    配点: 100

    - +
    +
    +

    問題文

    +

    + もうすぐ E869120 君と square1001 君の + 16 才の誕生日が来る.
    + そこで, AtCoder 王国の高橋君は, 円形のケーキ + 1 個に放射状に切れ目を入れ + 16 等分したものを, 彼らにプレゼントした. +

    +

    + E869120 君はそのうち A 切れ、square1001 君は + B 切れを食べようとした.
    + しかし, ケーキと一緒についていた紙を見ると, + 「同じ人が隣り合う + 2 + 切れのケーキを両方取ってはならない」と書かれていた. +

    +

    + さて、彼らは紙に書かれたことを守って、2 + 人とも食べたい数のケーキを取ることができるだろうか? +

    +
    +
    - -
    -
    +
    +
    +

    制約

    +
      +
    • + A, B1 以上 + 16 以下の整数 +
    • +
    • A+B16 以下である.
    • +
    +
    +
    +
    +
    +
    +
    +

    入力

    +

    入力は以下の形式で標準入力から与えられる.

    +
    A B
    +
    +
    +
    +
    +
    +

    出力

    +

    + 紙に書かれたことを守って, E869120 君と square1001 + 君両方が, 食べたい数のケーキを取ることができるならば + Yay!, そうでなければ + :( と出力しなさい. +

    +
    +
    +
    - -
    - - - -
    - - - - - - -
    +
    - - - -
    -
    -
    +
    +
    +

    入力例 1

    +
    +5 4
    +
    +
    +
    -
    - -
    -

    - +
    +
    +

    出力例 1

    +
    +Yay!
    +
    - +

    + 下の図のようにケーキを取れば、2 + 人とも目標を達成することができる.
    +  +

    +
    +
    + +
    + +
    +
    +

    入力例 2

    +
    +8 8
    +
    +
    +
    + +
    +
    +

    出力例 2

    +
    +Yay!
    +
    + +

    + 下の図のようにケーキを取れば、2 + 人とも目標を達成することができる.
    +  +

    +
    +
    + +
    + +
    +
    +

    入力例 3

    +
    +11 4
    +
    +
    +
    + +
    +
    +

    出力例 3

    +
    +:(
    +
    + +

    + この場合, 残念ながら目標を達成する方法は + 1 つもない. +

    +
    +
    + + +

    Score: 100 points

    + +
    +
    +

    Problem Statement

    +

    + E869120's and square1001's 16-th birthday is + coming soon.
    + Takahashi from AtCoder Kingdom gave them a round cake + cut into 16 equal fan-shaped pieces. +

    +

    + E869120 and square1001 were just about to eat + A and B of those pieces, + respectively,
    + when they found a note attached to the cake saying that + "the same person should not take two adjacent pieces of + cake". +

    +

    + Can both of them obey the instruction in the note and + take desired numbers of pieces of cake? +

    +
    +
    + +
    +
    +

    Constraints

    +
      +
    • + A and B are integers between + 1 and 16 (inclusive). +
    • +
    • A+B is at most 16.
    • +
    +
    +
    + +
    + +
    +
    +
    +

    Input

    +

    + Input is given from Standard Input in the following + format: +

    +
    A B
    +
    +
    +
    + +
    +
    +

    Output

    +

    + If both E869120 and square1001 can obey the + instruction in the note and take desired numbers of + pieces of cake, print Yay!; otherwise, + print :(. +

    +
    +
    +
    + +
    + +
    +
    +

    Sample Input 1

    +
    +5 4
    +
    +
    +
    + +
    +
    +

    Sample Output 1

    +
    +Yay!
    +
    + +

    + Both of them can take desired number of pieces as + follows: +  +

    +
    +
    + +
    + +
    +
    +

    Sample Input 2

    +
    +8 8
    +
    +
    +
    + +
    +
    +

    Sample Output 2

    +
    +Yay!
    +
    + +

    + Both of them can take desired number of pieces as + follows: +  +

    +
    +
    + +
    + +
    +
    +

    Sample Input 3

    +
    +11 4
    +
    +
    +
    + +
    +
    +

    Sample Output 3

    +
    +:(
    +
    + +

    + In this case, there is no way for them to take desired + number of pieces, unfortunately. +

    +
    +
    +
    + + + + + +
    + +
    + + + + + + +
    + + + +
    + + +
    + +
    +

    + + - - diff --git a/tests/fixtures/atcoder_task_abc100_b.html b/tests/fixtures/atcoder_task_abc100_b.html index c7a28d3..dd0f1f9 100644 --- a/tests/fixtures/atcoder_task_abc100_b.html +++ b/tests/fixtures/atcoder_task_abc100_b.html @@ -1,600 +1,887 @@ - - - - - - - - - + - - B - Ringo's Favorite Numbers - - - - - + + B - Ringo's Favorite Numbers + + + + + - - - + + - - - + + - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + - + - - + + + + + + + + + + + + + + + + - - -
    + + + + + - + -
    - -
    -
    - + + + + + -
    -
    -
    - - - Contest Duration: - - (local time) - (100 minutes) - - - Back to Home -
    - +
    +
    + + B - Ringo's Favorite Numbers + Editorial + + + / + + +
    +

    Time Limit: 2 sec / Memory Limit: 976 MiB

    -
    +
    + + +

    配点: 200

    - +
    +
    +

    問題文

    +

    + 今日は, 記念すべき AtCoder Beginner Contest 100 + が開催される. そのため, 高橋君はりんごさんに, + ある整数をプレゼントしようと思った.
    + 今日のコンテストは「AtCoder Beginner Contest + 100」なので, りんごさんは 100 で + ちょうど + D + 回割りきれる正の整数をプレゼントされると喜ぶ. +

    +

    + さて, りんごさんがプレゼントされると喜ぶような整数のうち + N 番目に小さいものを求めなさい. +

    +
    +
    - -
    -
    +
    +
    +

    制約

    +
      +
    • + D0, 1, 2 のいずれかである +
    • +
    • + N1 以上 + 100 以下の整数 +
    • +
    +
    +
    +
    +
    +
    +
    +

    入力

    +

    入力は以下の形式で標準入力から与えられる.

    +
    D N
    +
    +
    +
    +
    +
    +

    出力

    +

    + 100 でちょうど + D 回割りきれる正の整数の中で + N 番目に小さいものを出力しなさい. +

    +
    +
    +
    - -
    - - - -
    - - - - - - -
    +
    - - - -
    -
    -
    +
    +
    +

    入力例 1

    +
    +0 5
    +
    +
    +
    -
    - -
    -

    - +
    +
    +

    出力例 1

    +
    +5
    +
    - +

    + 100 でちょうど + 0 回割り切れる(すなわち, + 100 で割り切れない)整数は, 1, + 2, 3, 4, 5, + 6, 7, ... と続く.
    + よって, 5 番目に小さいりんごさんが喜ぶ整数は + 5 である. +

    +
    +
    + +
    + +
    +
    +

    入力例 2

    +
    +1 11
    +
    +
    +
    + +
    +
    +

    出力例 2

    +
    +1100
    +
    + +

    + 100 でちょうど + 1 回割り切れる整数は, 100, + 200, 300, 400, + 500, 600, 700, + 800, 900, 1 \ 000, + 1 \ 100, ... と続く.
    + よって, 求めたい整数は 1 \ 100 である. +

    +
    +
    + +
    + +
    +
    +

    入力例 3

    +
    +2 85
    +
    +
    +
    + +
    +
    +

    出力例 3

    +
    +850000
    +
    + +

    + 100 でちょうど + 2 回割り切れる整数は, 10 \ 000, + 20 \ 000, 30 \ 000, ... と続く.
    + よって, 求めたい整数は 850 \ 000 である. +

    +
    +
    + + +

    Score: 200 points

    + +
    +
    +

    Problem Statement

    +

    + Today, the memorable AtCoder Beginner Contest 100 takes + place. On this occasion, Takahashi would like to give an + integer to Ringo.
    + As the name of the contest is AtCoder Beginner Contest + 100, Ringo would be happy if he is given a positive + integer that can be divided by 100 + exactly D times. +

    +

    + Find the N-th smallest integer that would + make Ringo happy. +

    +
    +
    + +
    +
    +

    Constraints

    +
      +
    • + D is 0, 1 or + 2. +
    • +
    • + N is an integer between 1 and + 100 (inclusive). +
    • +
    +
    +
    + +
    + +
    +
    +
    +

    Input

    +

    + Input is given from Standard Input in the following + format: +

    +
    D N
    +
    +
    +
    + +
    +
    +

    Output

    +

    + Print the N-th smallest integer that can be + divided by 100 exactly D times. +

    +
    +
    +
    + +
    + +
    +
    +

    Sample Input 1

    +
    +0 5
    +
    +
    +
    + +
    +
    +

    Sample Output 1

    +
    +5
    +
    + +

    + The integers that can be divided by + 100 exactly 0 times (that is, not + divisible by 100) are as follows: + 1, 2, 3, 4, + 5, 6, 7, ...
    + Thus, the 5-th smallest integer that would + make Ringo happy is 5. +

    +
    +
    + +
    + +
    +
    +

    Sample Input 2

    +
    +1 11
    +
    +
    +
    + +
    +
    +

    Sample Output 2

    +
    +1100
    +
    + +

    + The integers that can be divided by + 100 exactly once are as follows: + 100, 200, 300, + 400, 500, 600, + 700, 800, 900, + 1 \ 000, 1 \ 100, ...
    + Thus, the integer we are seeking is 1 \ 100. +

    +
    +
    + +
    + +
    +
    +

    Sample Input 3

    +
    +2 85
    +
    +
    +
    + +
    +
    +

    Sample Output 3

    +
    +850000
    +
    + +

    + The integers that can be divided by + 100 exactly twice are as follows: + 10 \ 000, 20 \ 000, + 30 \ 000, ...
    + Thus, the integer we are seeking is + 850 \ 000. +

    +
    +
    +
    + + + + + +
    + +
    + + + + + + +
    + + + +
    + + +
    + +
    +

    + + - - diff --git a/tests/fixtures/atcoder_task_abc100_c.html b/tests/fixtures/atcoder_task_abc100_c.html index 6b298ba..351a2dc 100644 --- a/tests/fixtures/atcoder_task_abc100_c.html +++ b/tests/fixtures/atcoder_task_abc100_c.html @@ -1,618 +1,904 @@ - - - - - - - - - + - - C - *3 or /2 - - - - - + + C - *3 or /2 + + + + + - - - + + - - - + + - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + - + - - + + + + + + + + + + + + + + + + - - -
    + + + + + - + -
    - -
    -
    - + + + + + -
    -
    -
    - - - Contest Duration: - - (local time) - (100 minutes) - - - Back to Home -
    - +
    +
    + + C - *3 or /2 + Editorial + + + / + + +
    +

    Time Limit: 2 sec / Memory Limit: 976 MiB

    -
    +
    + + +

    配点: 300

    - +
    +
    +

    問題文

    +

    + AtCoder Beginner Contest 100 の開催にともなって, AtCoder + 社では長さ N の数列 a = {a_1, a_2, a_3, ..., a_N} が飾られることになった.
    + 社員のすぬけ君は, この数列で遊んでみようと思った. +

    +

    + 具体的には, + 以下の操作をできるだけ多くの回数繰り返そうと思った. +

    +
    1 \leq i \leq N を満たす全ての i に対して, それぞれ「a_i の値を 2 で割る」「a_i の値を 3 倍する」のどちらかを行う.  
    +ただし, 全ての i に対して 3 倍することはできず, 操作後の a_i の値は整数でなければならない.  
    +
    - -
    -
    +

    最大で何回の操作が可能か, 求めなさい.

    + +
    +
    +
    +

    制約

    +
      +
    • + N1 以上 + 10 \ 000 以下の整数 +
    • +
    • + a_i1 以上 + 1 \ 000 \ 000 \ 000 以下の整数 +
    • +
    +
    +
    +
    +
    +
    +
    +

    入力

    +

    入力は以下の形式で標準入力から与えられる.

    +
    N
    +a_1 a_2 a_3 ... a_N
    +
    +
    +
    - -
    - - - -
    - - - - - - -
    +
    +
    +

    出力

    +

    すぬけ君が行える最大の操作回数を出力しなさい.

    +
    +
    +
    - - - -
    -
    -
    +
    -
    - -
    -

    - +
    +
    +

    入力例 1

    +
    +3
    +5 2 4
    +
    +
    +
    - +
    +
    +

    出力例 1

    +
    +3
    +
    + +

    + 最初, 数列は {5, 2, 4} であるが, + 以下のように操作すれば + 3 回の操作を行うことができる. +

    +
      +
    • + 最初に, a_13 倍し, + a_23 倍し, a_3 を + 2 で割る. すると数列は + {15, 6, 2} となる. +
    • +
    • + 次に, a_13 倍し, + a_22 で割り, + a_33 倍する. すると数列は + {45, 3, 6} となる. +
    • +
    • + 最後に, a_13 倍し, + a_23 倍し, a_3 を + 2 で割る. すると数列は + {135, 9, 3} となる. +
    • +
    +
    +
    + +
    + +
    +
    +

    入力例 2

    +
    +4
    +631 577 243 199
    +
    +
    +
    + +
    +
    +

    出力例 2

    +
    +0
    +
    + +

    + 全ての要素が奇数なので, 操作はできない. よって答えは + 0 である. +

    +
    +
    + +
    + +
    +
    +

    入力例 3

    +
    +10
    +2184 2126 1721 1800 1024 2528 3360 1945 1280 1776
    +
    +
    +
    + +
    +
    +

    出力例 3

    +
    +39
    +
    +
    +
    + + +

    Score: 300 points

    + +
    +
    +

    Problem Statement

    +

    + As AtCoder Beginner Contest 100 is taking place, the + office of AtCoder, Inc. is decorated with a sequence of + length N, a = {a_1, a_2, a_3, ..., a_N}.
    + Snuke, an employee, would like to play with this + sequence. +

    +

    + Specifically, he would like to repeat the following + operation as many times as possible: +

    +
    For every i satisfying 1 \leq i \leq N, perform one of the following: "divide a_i by 2" and "multiply a_i by 3".  
    +Here, choosing "multiply a_i by 3" for every i is not allowed, and the value of a_i after the operation must be an integer.
    +
    + +

    At most how many operations can be performed?

    +
    +
    + +
    +
    +

    Constraints

    +
      +
    • + N is an integer between 1 and + 10 \ 000 (inclusive). +
    • +
    • + a_i is an integer between 1 and + 1 \ 000 \ 000 \ 000 (inclusive). +
    • +
    +
    +
    + +
    + +
    +
    +
    +

    Input

    +

    + Input is given from Standard Input in the following + format: +

    +
    N
    +a_1 a_2 a_3 ... a_N
    +
    +
    +
    + +
    +
    +

    Output

    +

    + Print the maximum number of operations that Snuke can + perform. +

    +
    +
    +
    + +
    + +
    +
    +

    Sample Input 1

    +
    +3
    +5 2 4
    +
    +
    +
    + +
    +
    +

    Sample Output 1

    +
    +3
    +
    + +

    + The sequence is initially {5, 2, 4}. Three + operations can be performed as follows: +

    +
      +
    • + First, multiply a_1 by 3, + multiply a_2 by 3 and divide + a_3 by 2. The sequence is now + {15, 6, 2}. +
    • +
    • + Next, multiply a_1 by 3, divide + a_2 by 2 and multiply + a_3 by 3. The sequence is now + {45, 3, 6}. +
    • +
    • + Finally, multiply a_1 by 3, + multiply a_2 by 3 and divide + a_3 by 2. The sequence is now + {135, 9, 3}. +
    • +
    +
    +
    + +
    + +
    +
    +

    Sample Input 2

    +
    +4
    +631 577 243 199
    +
    +
    +
    + +
    +
    +

    Sample Output 2

    +
    +0
    +
    + +

    + No operation can be performed since all the elements are + odd. Thus, the answer is 0. +

    +
    +
    + +
    + +
    +
    +

    Sample Input 3

    +
    +10
    +2184 2126 1721 1800 1024 2528 3360 1945 1280 1776
    +
    +
    +
    + +
    +
    +

    Sample Output 3

    +
    +39
    +
    +
    +
    +
    + + + + + +
    + +
    + + + + + + +
    + + + +
    + + +
    + +
    +

    + + - - diff --git a/tests/fixtures/atcoder_task_abc100_d.html b/tests/fixtures/atcoder_task_abc100_d.html index 552b276..ca47a6b 100644 --- a/tests/fixtures/atcoder_task_abc100_d.html +++ b/tests/fixtures/atcoder_task_abc100_d.html @@ -1,728 +1,1086 @@ - - - - - - - - - + - - D - Patisserie ABC - - - - - + + D - Patisserie ABC + + + + + - - - + + - - - + + - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + - + - - + + + + + + + + + + + + + + + + - - -
    + + + + + - + -
    - -
    -
    - + + + + + -
    -
    -
    - - - Contest Duration: - - (local time) - (100 minutes) - - - Back to Home -
    - +
    +
    + + D - Patisserie ABC + Editorial + + + / + + +
    +

    Time Limit: 2 sec / Memory Limit: 976 MiB

    -
    +
    + + +

    配点: 400

    - +
    +
    +

    問題文

    +

    + 高橋君はプロのパティシエになり, AtCoder Beginner Contest + 100 を記念して, 「ABC洋菓子店」というお店を開いた. +

    +

    + ABC洋菓子店では, + N 種類のケーキを売っている.
    + 各種類のケーキには「綺麗さ」「おいしさ」「人気度」の + 3 つの値を持ち, + i 種類目のケーキの綺麗さは x_i, + おいしさは y_i, 人気度は + z_i である.
    + これらの値は 0 以下である可能性もある. +

    +

    + りんごさんは, ABC洋菓子店で + M 個のケーキを食べることにした. + 彼は次のように, + 食べるケーキの組み合わせを選ぶことにした. +

    +
      +
    • 同じ種類のケーキを 2 個以上食べない.
    • +
    • + 上の条件を満たしつつ, (綺麗さの合計の絶対値) + + (おいしさの合計の絶対値) + (人気度の合計の絶対値) + が最大になるように選ぶ. +
    • +
    +

    + このとき, りんごさんが選ぶケーキの + (綺麗さの合計の絶対値) + (おいしさの合計の絶対値) + + (人気度の合計の絶対値) の最大値を求めなさい. +

    +
    +
    - -
    -
    +
    +
    +

    制約

    +
      +
    • + N1 以上 + 1 \ 000 以下の整数 +
    • +
    • + M0 以上 + N 以下の整数 +
    • +
    • + x_i, y_i, z_i \ (1 \leq i \leq N) は, + それぞれ -10 \ 000 \ 000 \ 000 以上 + 10 \ 000 \ 000 \ 000 以下の整数. +
    • +
    +
    +
    +
    +
    +
    +
    +

    入力

    +

    入力は以下の形式で標準入力から与えられる.

    +
    N M
    +x_1 y_1 z_1
    +x_2 y_2 z_2
    + :  :
    +x_N y_N z_N
    +
    +
    +
    +
    +
    +

    出力

    +

    + りんごさんが選ぶケーキの (綺麗さの合計の絶対値) + + (おいしさの合計の絶対値) + (人気度の合計の絶対値) + の最大値を出力しなさい. +

    +
    +
    +
    - -
    - - - -
    - - - - - - -
    +
    - - - -
    -
    -
    +
    +
    +

    入力例 1

    +
    +5 3
    +3 1 4
    +1 5 9
    +2 6 5
    +3 5 8
    +9 7 9
    +
    +
    +
    -
    - -
    -

    - +
    +
    +

    出力例 1

    +
    +56
    +
    - +

    + 2, 4, 5 種類目のケーキを食べることを考える. + そのとき, + 「綺麗さ」「おいしさ」「人気度」の合計はそれぞれ次のようになる. +

    +
      +
    • 綺麗さ:1 + 3 + 9 = 13
    • +
    • おいしさ:5 + 5 + 7 = 17
    • +
    • 人気度:9 + 8 + 9 = 26
    • +
    +

    + このときの (綺麗さの合計の絶対値) + + (おいしさの合計の絶対値) + (人気度の合計の絶対値) は + 13 + 17 + 26 = 56 となり, これが最大になる. +

    +
    +
    + +
    + +
    +
    +

    入力例 2

    +
    +5 3
    +1 -2 3
    +-4 5 -6
    +7 -8 -9
    +-10 11 -12
    +13 -14 15
    +
    +
    +
    + +
    +
    +

    出力例 2

    +
    +54
    +
    + +

    + 1, 3, 5 種類目のケーキを食べることを考える. + そのとき, + 「綺麗さ」「おいしさ」「人気度」の合計はそれぞれ次のようになる. +

    +
      +
    • 綺麗さ:1 + 7 + 13 = 21
    • +
    • おいしさ:(-2) + (-8) + (-14) = -24
    • +
    • 人気度:3 + (-9) + 15 = 9
    • +
    +

    + このときの (綺麗さの合計の絶対値) + + (おいしさの合計の絶対値) + (人気度の合計の絶対値) は + 21 + 24 + 9 = 54 となり, これが最大になる. +

    +
    +
    + +
    + +
    +
    +

    入力例 3

    +
    +10 5
    +10 -80 21
    +23 8 38
    +-94 28 11
    +-26 -2 18
    +-69 72 79
    +-26 -86 -54
    +-72 -50 59
    +21 65 -32
    +40 -94 87
    +-62 18 82
    +
    +
    +
    + +
    +
    +

    出力例 3

    +
    +638
    +
    + +

    + 3, 4, 5, 7, 10 種類目のケーキを食べると, + 綺麗さの合計は -323, おいしさの合計は + 66, 人気度の合計は + 249 となる.
    + このときの (綺麗さの合計の絶対値) + + (おいしさの合計の絶対値) + (人気度の合計の絶対値) は + 323 + 66 + 249 = 638 となり, + これが最大になる. +

    +
    +
    + +
    + +
    +
    +

    入力例 4

    +
    +3 2
    +2000000000 -9000000000 4000000000
    +7000000000 -5000000000 3000000000
    +6000000000 -1000000000 8000000000
    +
    +
    +
    + +
    +
    +

    出力例 4

    +
    +30000000000
    +
    + +

    + ケーキの綺麗さ, おいしさ, 人気度や出力すべき値が, 32bit + 整数に収まらない場合もある. +

    +
    +
    + + +

    Score: 400 points

    + +
    +
    +

    Problem Statement

    +

    + Takahashi became a pastry chef and opened a shop + La Confiserie d'ABC to celebrate AtCoder + Beginner Contest 100. +

    +

    + The shop sells N kinds of cakes.
    + Each kind of cake has three parameters "beauty", + "tastiness" and "popularity". The i-th kind + of cake has the beauty of x_i, the tastiness + of y_i and the popularity of + z_i.
    + These values may be zero or negative. +

    +

    + Ringo has decided to have M pieces of cakes + here. He will choose the set of cakes as follows: +

    +
      +
    • + Do not have two or more pieces of the same kind of + cake. +
    • +
    • + Under the condition above, choose the set of cakes to + maximize (the absolute value of the total beauty) + + (the absolute value of the total tastiness) + (the + absolute value of the total popularity). +
    • +
    +

    + Find the maximum possible value of (the absolute value + of the total beauty) + (the absolute value of the total + tastiness) + (the absolute value of the total + popularity) for the set of cakes that Ringo chooses. +

    +
    +
    + +
    +
    +

    Constraints

    +
      +
    • + N is an integer between 1 and + 1 \ 000 (inclusive). +
    • +
    • + M is an integer between 0 and + N (inclusive). +
    • +
    • + x_i, y_i, z_i \ (1 \leq i \leq N) are + integers between -10 \ 000 \ 000 \ 000 and + 10 \ 000 \ 000 \ 000 (inclusive). +
    • +
    +
    +
    + +
    + +
    +
    +
    +

    Input

    +

    + Input is given from Standard Input in the following + format: +

    +
    N M
    +x_1 y_1 z_1
    +x_2 y_2 z_2
    + :  :
    +x_N y_N z_N
    +
    +
    +
    + +
    +
    +

    Output

    +

    + Print the maximum possible value of (the absolute + value of the total beauty) + (the absolute value of + the total tastiness) + (the absolute value of the + total popularity) for the set of cakes that Ringo + chooses. +

    +
    +
    +
    + +
    + +
    +
    +

    Sample Input 1

    +
    +5 3
    +3 1 4
    +1 5 9
    +2 6 5
    +3 5 8
    +9 7 9
    +
    +
    +
    + +
    +
    +

    Sample Output 1

    +
    +56
    +
    + +

    + Consider having the 2-nd, 4-th and + 5-th kinds of cakes. The total beauty, + tastiness and popularity will be as follows: +

    +
      +
    • Beauty: 1 + 3 + 9 = 13
    • +
    • Tastiness: 5 + 5 + 7 = 17
    • +
    • Popularity: 9 + 8 + 9 = 26
    • +
    +

    + The value (the absolute value of the total beauty) + + (the absolute value of the total tastiness) + (the + absolute value of the total popularity) here is + 13 + 17 + 26 = 56. This is the maximum value. +

    +
    +
    + +
    + +
    +
    +

    Sample Input 2

    +
    +5 3
    +1 -2 3
    +-4 5 -6
    +7 -8 -9
    +-10 11 -12
    +13 -14 15
    +
    +
    +
    + +
    +
    +

    Sample Output 2

    +
    +54
    +
    + +

    + Consider having the 1-st, 3-rd and + 5-th kinds of cakes. The total beauty, + tastiness and popularity will be as follows: +

    +
      +
    • Beauty: 1 + 7 + 13 = 21
    • +
    • Tastiness: (-2) + (-8) + (-14) = -24
    • +
    • Popularity: 3 + (-9) + 15 = 9
    • +
    +

    + The value (the absolute value of the total beauty) + + (the absolute value of the total tastiness) + (the + absolute value of the total popularity) here is + 21 + 24 + 9 = 54. This is the maximum value. +

    +
    +
    + +
    + +
    +
    +

    Sample Input 3

    +
    +10 5
    +10 -80 21
    +23 8 38
    +-94 28 11
    +-26 -2 18
    +-69 72 79
    +-26 -86 -54
    +-72 -50 59
    +21 65 -32
    +40 -94 87
    +-62 18 82
    +
    +
    +
    + +
    +
    +

    Sample Output 3

    +
    +638
    +
    + +

    + If we have the 3-rd, 4-th, + 5-th, 7-th and 10-th + kinds of cakes, the total beauty, tastiness and + popularity will be -323, 66 and + 249, respectively.
    + The value (the absolute value of the total beauty) + + (the absolute value of the total tastiness) + (the + absolute value of the total popularity) here is + 323 + 66 + 249 = 638. This is the maximum + value. +

    +
    +
    + +
    + +
    +
    +

    Sample Input 4

    +
    +3 2
    +2000000000 -9000000000 4000000000
    +7000000000 -5000000000 3000000000
    +6000000000 -1000000000 8000000000
    +
    +
    +
    + +
    +
    +

    Sample Output 4

    +
    +30000000000
    +
    + +

    + The values of the beauty, tastiness and popularity of + the cakes and the value to be printed may not fit into + 32-bit integers. +

    +
    +
    +
    + + + + + +
    + +
    + + + + + + +
    + + + +
    + + +
    + +
    +

    + + - - diff --git a/tests/fixtures/codeforces_1550_problems.html b/tests/fixtures/codeforces_1550_problems.html index fa19bbb..449cab5 100644 --- a/tests/fixtures/codeforces_1550_problems.html +++ b/tests/fixtures/codeforces_1550_problems.html @@ -1,977 +1,10122 @@ - + + + + + + + + + + Problems - Codeforces
    Loading [MathJax]/jax/output/HTML-CSS/fonts/TeX/fontdata.js
    + + + + + + + + + + + + + Problems - Codeforces + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Loading [MathJax]/jax/output/HTML-CSS/fonts/TeX/fontdata.js +
    + +
    +
    + +
    + + + +
    -
    - + +
    \ No newline at end of file + if (document.body) { + var a = document.createElement('iframe') + a.height = 1 + a.width = 1 + a.style.position = 'absolute' + a.style.top = 0 + a.style.left = 0 + a.style.border = 'none' + a.style.visibility = 'hidden' + document.body.appendChild(a) + if ('loading' !== document.readyState) c() + else if (window.addEventListener) + document.addEventListener('DOMContentLoaded', c) + else { + var e = document.onreadystatechange || function () {} + document.onreadystatechange = function (b) { + e(b) + 'loading' !== document.readyState && + ((document.onreadystatechange = e), c()) + } + } + } + })() + + + + + + +
    +
    +
    + + diff --git a/tests/fixtures/cses_contests.html b/tests/fixtures/cses_contests.html index 1106f5a..0925bc4 100644 --- a/tests/fixtures/cses_contests.html +++ b/tests/fixtures/cses_contests.html @@ -1,43 +1,2141 @@ - + - - - - - - - - - + + + + + + + + + - -
    -
    - - - - -
    - - - Dark mode -
    + +
    +
    + + + + +
    + + + Dark mode +
    +
    -
    -
    -