From 051544369d631018f91214ca47c5b879e4326175 Mon Sep 17 00:00:00 2001 From: Keenan Gugeler Date: Sun, 7 Jan 2024 23:51:01 -0500 Subject: [PATCH] problem_format: add C++ templates Many templates have been floating around in the DMOJ community for validation and input handling in checkers. This commit aims to consolidate them. It has two main goals: - Correct. Duh. - Simple. Other templates that circulate, including the ones I have published, are too complex. People naively try and write their own. I am sick and tired of reading over incorrect validators. These templates forgo some principles of good design (such as object-oriented programming) in favour of pure simplicity. They should be simple enough that they are understandable by the broader community, and are not a black box. Hopefully this also dissuades re-writing. --- .github/workflows/build.yml | 21 +++ docs/_sidebar.md | 1 + docs/problem_format/cpp_psetting_templates.md | 61 +++++++ .../identical_checker_interactor.cpp | 129 +++++++++++++ .../standard_checker_interactor.cpp | 171 ++++++++++++++++++ sample_files/problem_setting/test/run_test.sh | 39 ++++ .../esoteric_validator/cases/basic/code | 1 + .../esoteric_validator/cases/basic/input | 2 + .../esoteric_validator/cases/basic/output | 2 + .../esoteric_validator/cases/noeol/code | 1 + .../esoteric_validator/cases/noeol/input | 2 + .../esoteric_validator/cases/noeol/output | 1 + .../testsuite/esoteric_validator/main.cpp | 22 +++ .../cases/basic/code | 1 + .../cases/basic/input | 2 + .../cases/basic/output | 1 + .../cases/leading_zeroes/code | 1 + .../cases/leading_zeroes/input | 2 + .../cases/leading_zeroes/output | 0 .../cases/leading_zeroes2/code | 1 + .../cases/leading_zeroes2/input | 2 + .../cases/leading_zeroes2/output | 0 .../cases/nan/code | 1 + .../cases/nan/input | 2 + .../cases/nan/output | 0 .../cases/scientific_notation/code | 1 + .../cases/scientific_notation/input | 2 + .../cases/scientific_notation/output | 0 .../identical_checker_interactor/main.cpp | 11 ++ .../cases/basic/code | 1 + .../cases/basic/input | 6 + .../cases/basic/output | 0 .../cases/newline_instead_of_space/code | 1 + .../cases/newline_instead_of_space/input | 7 + .../cases/newline_instead_of_space/output | 0 .../cases/no_newline/code | 1 + .../cases/no_newline/input | 6 + .../cases/no_newline/output | 0 .../cases/trailing_tokens/code | 1 + .../cases/trailing_tokens/input | 5 + .../cases/trailing_tokens/output | 0 .../cases/weird_whitespace/code | 1 + .../cases/weird_whitespace/input | 15 ++ .../cases/weird_whitespace/output | 0 .../standard_checker_interactor/main.cpp | 18 ++ .../test/testsuite/validator/cases/basic/code | 1 + .../testsuite/validator/cases/basic/input | 2 + .../testsuite/validator/cases/basic/output | 1 + .../cases/expect_integer_get_float/code | 1 + .../cases/expect_integer_get_float/input | 2 + .../cases/expect_integer_get_float/output | 0 .../validator/cases/extra_space/code | 1 + .../validator/cases/extra_space/input | 2 + .../validator/cases/extra_space/output | 0 .../validator/cases/integer_out_of_range/code | 1 + .../cases/integer_out_of_range/input | 2 + .../cases/integer_out_of_range/output | 0 .../validator/cases/malformed_integer/code | 1 + .../validator/cases/malformed_integer/input | 2 + .../validator/cases/malformed_integer/output | 0 .../validator/cases/trailing_newline/code | 1 + .../validator/cases/trailing_newline/input | 3 + .../validator/cases/trailing_newline/output | 0 .../test/testsuite/validator/main.cpp | 15 ++ sample_files/problem_setting/validator.cpp | 79 ++++++++ 65 files changed, 656 insertions(+) create mode 100644 .github/workflows/build.yml create mode 100644 docs/problem_format/cpp_psetting_templates.md create mode 100644 sample_files/problem_setting/identical_checker_interactor.cpp create mode 100644 sample_files/problem_setting/standard_checker_interactor.cpp create mode 100755 sample_files/problem_setting/test/run_test.sh create mode 100644 sample_files/problem_setting/test/testsuite/esoteric_validator/cases/basic/code create mode 100644 sample_files/problem_setting/test/testsuite/esoteric_validator/cases/basic/input create mode 100644 sample_files/problem_setting/test/testsuite/esoteric_validator/cases/basic/output create mode 100644 sample_files/problem_setting/test/testsuite/esoteric_validator/cases/noeol/code create mode 100644 sample_files/problem_setting/test/testsuite/esoteric_validator/cases/noeol/input create mode 100644 sample_files/problem_setting/test/testsuite/esoteric_validator/cases/noeol/output create mode 100644 sample_files/problem_setting/test/testsuite/esoteric_validator/main.cpp create mode 100644 sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/basic/code create mode 100644 sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/basic/input create mode 100644 sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/basic/output create mode 100644 sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/leading_zeroes/code create mode 100644 sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/leading_zeroes/input create mode 100644 sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/leading_zeroes/output create mode 100644 sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/leading_zeroes2/code create mode 100644 sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/leading_zeroes2/input create mode 100644 sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/leading_zeroes2/output create mode 100644 sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/nan/code create mode 100644 sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/nan/input create mode 100644 sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/nan/output create mode 100644 sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/scientific_notation/code create mode 100644 sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/scientific_notation/input create mode 100644 sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/scientific_notation/output create mode 100644 sample_files/problem_setting/test/testsuite/identical_checker_interactor/main.cpp create mode 100644 sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/basic/code create mode 100644 sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/basic/input create mode 100644 sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/basic/output create mode 100644 sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/newline_instead_of_space/code create mode 100644 sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/newline_instead_of_space/input create mode 100644 sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/newline_instead_of_space/output create mode 100644 sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/no_newline/code create mode 100644 sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/no_newline/input create mode 100644 sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/no_newline/output create mode 100644 sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/trailing_tokens/code create mode 100644 sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/trailing_tokens/input create mode 100644 sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/trailing_tokens/output create mode 100644 sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/weird_whitespace/code create mode 100644 sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/weird_whitespace/input create mode 100644 sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/weird_whitespace/output create mode 100644 sample_files/problem_setting/test/testsuite/standard_checker_interactor/main.cpp create mode 100644 sample_files/problem_setting/test/testsuite/validator/cases/basic/code create mode 100644 sample_files/problem_setting/test/testsuite/validator/cases/basic/input create mode 100644 sample_files/problem_setting/test/testsuite/validator/cases/basic/output create mode 100644 sample_files/problem_setting/test/testsuite/validator/cases/expect_integer_get_float/code create mode 100644 sample_files/problem_setting/test/testsuite/validator/cases/expect_integer_get_float/input create mode 100644 sample_files/problem_setting/test/testsuite/validator/cases/expect_integer_get_float/output create mode 100644 sample_files/problem_setting/test/testsuite/validator/cases/extra_space/code create mode 100644 sample_files/problem_setting/test/testsuite/validator/cases/extra_space/input create mode 100644 sample_files/problem_setting/test/testsuite/validator/cases/extra_space/output create mode 100644 sample_files/problem_setting/test/testsuite/validator/cases/integer_out_of_range/code create mode 100644 sample_files/problem_setting/test/testsuite/validator/cases/integer_out_of_range/input create mode 100644 sample_files/problem_setting/test/testsuite/validator/cases/integer_out_of_range/output create mode 100644 sample_files/problem_setting/test/testsuite/validator/cases/malformed_integer/code create mode 100644 sample_files/problem_setting/test/testsuite/validator/cases/malformed_integer/input create mode 100644 sample_files/problem_setting/test/testsuite/validator/cases/malformed_integer/output create mode 100644 sample_files/problem_setting/test/testsuite/validator/cases/trailing_newline/code create mode 100644 sample_files/problem_setting/test/testsuite/validator/cases/trailing_newline/input create mode 100644 sample_files/problem_setting/test/testsuite/validator/cases/trailing_newline/output create mode 100644 sample_files/problem_setting/test/testsuite/validator/main.cpp create mode 100644 sample_files/problem_setting/validator.cpp diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..bbd4705 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,21 @@ +name: build +on: [push, pull_request] +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Install clang-format 12 + run: | + wget -O clang-format https://github.com/DMOJ/clang-tools-static-binaries/releases/download/master-5ea3d18c/clang-format-12_linux-amd64 + chmod a+x ./clang-format + - name: Run clang-format + run: find sample_files/problem_setting \( -name '*.h' -or -name '*.cpp' -or -name '*.c' \) -print0 | xargs -0 ./clang-format --dry-run -Werror --color + cpp_template_tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Run C++ template tests + run: | + cd sample_files/problem_setting/test + ./run_test.sh diff --git a/docs/_sidebar.md b/docs/_sidebar.md index 215e58a..c0da7cd 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -24,6 +24,7 @@ - [Custom graders](problem_format/custom_graders.md) - [Generators](problem_format/generator.md) - [Problem examples](problem_format/problem_examples.md) + - [C++ Problem Setting Templates](problem_format/cpp_psetting_templates.md) - About - [License](about/LICENSE.md) diff --git a/docs/problem_format/cpp_psetting_templates.md b/docs/problem_format/cpp_psetting_templates.md new file mode 100644 index 0000000..b6bd539 --- /dev/null +++ b/docs/problem_format/cpp_psetting_templates.md @@ -0,0 +1,61 @@ +# C++ Problem Setting Templates - `cpp_psetting_templates` + +There are three C++ input-handling templates provided for aiding problem setters. They are as follows: + +- [Validator Template](https://github.com/DMOJ/docs/blob/master/sample_files/problem_setting/validator.cpp) +- [Identical Checker/Interactor Template](https://github.com/DMOJ/docs/blob/master/sample_files/problem_setting/identical_checker_interactor.cpp) +- [Standard Checker/Interactor Template](https://github.com/DMOJ/docs/blob/master/sample_files/problem_setting/standard_checker_interactor.cpp) + +## Validator + +This is a template for validating the input data of problems. It aims to be simple and of course, correct. It contains seven functions. The first three are whitespace functions: + +- `void readSpace()` expects a space at the current position in the input, and aborts the program if there is not a space. +- `void readNewLine()` expects a newline at the current position in the input. +- `void readEOF()` expects the input file to end immediately at the current position. + +The remaining four are for actual content: + +- `std::string readToken(char min_char = 0, char max_char = 127)` returns the next token in the input stream. A token is defined as a whitespace-separated string. If the next character in the input is a whitespace character, this method aborts the program. The optional arguments `min_char` and `max_char` can be used to enforce a range on the characters in the token. For instance, `readToken('a', 'z')` reads a lowercase string of english letters. +- `std::string readLine(char min_char = 0, char max_char = 127)` returns the next line in the input stream. Specifically, it reads until it encounters a `\n`, and discards it (the newline is not part of the returned string). `min_char` and `max_char` are the same as for `readToken`. If `readLine` encounters an EOF, it fails. +- `long long readInt(long long lo, long long hi)` parses the next token as an integer. It aborts on overflow, malformed integers, and if the resultant integer is not in the range [lo, hi], inclusive. Leading zeroes and `-0` are not accepted. +- `long double readFloat(long double lo, long double hi, long double eps = 1e-9)` parses the next token as a float. It aborts on overflow, malformed floats, and if the resultant float is not in the range [lo, hi], inclusive, using the provided epsilon to perform the comparison. Scientific notation and NaNs are not accepted, nor are leading zeroes. `-0` is allowed. Trailing zeroes are also permitted. +- `std::vector readIntArray(size_t N, long long lo, long long hi)` parses the next space-separated N integers into an array, and then reads a final newline. It must be given a template argument, which is the type of the array elements. For example, `readIntArray(5, 1, 10)` reads five space-separated integers into a `std::vector`, where each integer is in the range [1, 10], inclusive. + +`readFloat()` will likely be of no use for many validators, and can be safely deleted. Similarly, `readIntArray` can be deleted if unneeded. + +## Checkers/Interactors + +The next pair of templates are for checkers/interactors. The difference is the type of whitespace handling: the identical checker/interactor expects whitespace to match exactly. The standard checker/interactor handles whitespace like the `standard` checker. + +The checkers and interactors are designed for the `coci` bridged checker/interactor type. However, updating the codes used and the order of command line parameters to work with other types should not be challenging. + +Both files can be used for either checkers/interactors, with the following caveat: interactors MUST close `stdout` BEFORE calling `readEOF()`, so that the user process can terminate in case it _also_ expects an EOF. Checker stdout is used for feedback displayed to the user, and as such `stdout` should not be closed in this case. Validators also do not need to worry about this - only interactors do, and they should only call `readEOF()` once they have finished communicating with the user, to clean up and assert that the user didn't send any trailing data. + +The general format of the checkers/interactors are the same as the validator, with a few changes: + +- `readSpace(), readNewLine(), readEOF()`: Under the identical checker, these return Presentation Error if the check fails. Under the standard checker, these return WA. +- `readToken()`: Under the identical checker, this returns Presentation Error if the token is empty, and WA if any character is not in range. +- `readLine()`: Under the identical checker, this returns Presentation Error if an EOF is encountered, and WA if any character is not in range. This function cannot be used correctly under the standard checker, and so is not provided in that template. +- `readInt(), readIntArray(), readFloat()`: Returns WA if the token is malformed or out of range. + +Additionally, two new functions are provided. + +- `exitWA()` unconditionally exits with a WA verdict. +- `assertWA(bool)` takes a condition and exits with WA if the condition is false. + +Under the identical checker, corresponding functions `exitPE()` and `assertPE` are provided. Standard checkers should not use the Presentation Error code, as the builtin `standard` checker does not use this code. + +Finally, there is an empty function `errorHook()`. This function is called whenever the provided function would exit with an error. It should be used to do custom handling, such as providing partial points for outputting part of an answer, or outputting `-1` in interactors to signal errors to the user submission. + +## Standard Checker/Interactor Design + +This section is purely for those interested in the design and inner workings of the standard checker/interactor routines. + +The general overview is that `readSpace()` should read non-line whitespace characters, `readNewLine` should read whitespace and expect a line whitespace character, and `readEOF` should read all whitespace and check for EOF. Additionally, any leading whitespace in the input should be trimmed. + +There are two major challenges with making a standard checker/interactor design ergonomic: +- Under interactors, it is not acceptable to consume all whitespace in the `readNewLine` method, as the user submission will likely output a single line and then wait for the interactor to send another query. If the interactor naively tried to consume all whitespace, it would block, and the user submission would TLE. +- After reading the end of the input, it's most ergonomic to have the checker read a newline, and then call `readEOF()`, as this is the canonical input format. However, the standard checker allows users to forgo the last newline, and if the `readNewLine()` method expected a newline, we would erroneously return WA. + +To solve both of these problems, we employ a lazy whitespace checking scheme. `readSpace()` and `readNewLine()` simply set a flag for `readToken()`. `readToken()` then consumes the whitespace and validates it, before reading the token. Additionally, `readEOF()`, if called after `readNewLine()`, ignores the flag and consumes all whitespace, and then checks for EOF. diff --git a/sample_files/problem_setting/identical_checker_interactor.cpp b/sample_files/problem_setting/identical_checker_interactor.cpp new file mode 100644 index 0000000..10d01f8 --- /dev/null +++ b/sample_files/problem_setting/identical_checker_interactor.cpp @@ -0,0 +1,129 @@ +#include +#include +#include +#include +#include +#include +#include + +namespace regex_helpers { +regex_t compile(const char *pattern) { + regex_t re; + if (regcomp(&re, pattern, REG_EXTENDED | REG_NOSUB) != 0) { + throw std::runtime_error("Pattern failed to compile."); + } + return re; +} +bool match(regex_t re, const std::string &text) { + return regexec(&re, text.c_str(), 0, NULL, 0) == 0; +} +} // namespace regex_helpers + +void errorHook(); +void exitWA() { + errorHook(); + std::exit(1); +} +void exitPE() { + errorHook(); + std::exit(2); +} +void assertWA(bool condition) { + if (!condition) { + exitWA(); + } +} +void assertPE(bool condition) { + if (!condition) { + exitPE(); + } +} +void readSpace() { assertPE(getchar() == ' '); } +void readNewLine() { assertPE(getchar() == '\n'); } +void readEOF() { assertPE(getchar() == EOF); } +std::string readToken(char min_char = 0, char max_char = 127) { + static constexpr size_t MAX_TOKEN_SIZE = 1e7; + std::string token; + int c = getchar(); + assertPE(!isspace(c)); + while (!isspace(c) && c != EOF) { + assertWA(token.size() < MAX_TOKEN_SIZE); + assertWA(min_char <= c && c <= max_char); + token.push_back(char(c)); + c = getchar(); + } + ungetc(c, stdin); + return token; +} +std::string readLine(char min_char = 0, char max_char = 127) { + static constexpr size_t MAX_LINE_SIZE = 1e7; + std::string line; + int c = getchar(); + while (c != '\n') { + assertPE(c != EOF); + assertWA(line.size() < MAX_LINE_SIZE); + assertWA(min_char <= c && c <= max_char); + line.push_back(char(c)); + c = getchar(); + } + return line; +} +long long readInt(long long lo, long long hi) { + static regex_t re = regex_helpers::compile("^(0|-?[1-9][0-9]*)$"); + std::string token = readToken(); + assertWA(regex_helpers::match(re, token)); + + long long parsedInt; + try { + parsedInt = stoll(token); + } catch (const std::invalid_argument &) { + exitWA(); + } catch (const std::out_of_range &) { + exitWA(); + } + assertWA(lo <= parsedInt && parsedInt <= hi); + return parsedInt; +} +long double readFloat(long double min, long double max, + long double eps = 1e-9) { + static regex_t re = regex_helpers::compile("^-?(0|[1-9][0-9])(\\.[0-9]+)?$"); + std::string token = readToken(); + assertWA(regex_helpers::match(re, token)); + long double parsedDouble; + try { + parsedDouble = stold(token); + } catch (const std::invalid_argument &) { + exitWA(); + } catch (const std::out_of_range &) { + exitWA(); + } + assertWA(min - eps <= parsedDouble && parsedDouble <= max + eps); + return parsedDouble; +} +template +std::vector readIntArray(size_t N, long long lo, long long hi) { + std::vector arr; + arr.reserve(N); + for (size_t i = 0; i < N; i++) { + arr.push_back(readInt(lo, hi)); + if (i != N - 1) { + readSpace(); + } + } + readNewLine(); + return arr; +} +void errorHook() {} + +// If this is a checker: +// int main(int argc, char **argv) { +// std::ifstream judge_input(argv[1]); +// freopen(argv[2], "r", stdin); +// std::ifstream judge_answer(argv[3]); +// } + +// If this is an interactor: +// int main(int argc, char **argv) { +// std::ifstream judge_input(argv[1]); +// std::ifstream judge_answer(argv[2]); +// } diff --git a/sample_files/problem_setting/standard_checker_interactor.cpp b/sample_files/problem_setting/standard_checker_interactor.cpp new file mode 100644 index 0000000..8ba8c3d --- /dev/null +++ b/sample_files/problem_setting/standard_checker_interactor.cpp @@ -0,0 +1,171 @@ +#include +#include +#include +#include +#include +#include +#include + +void assertWA(bool); + +// Implementation of the tricky whitespace logic for standard checkers. +namespace standard_whitespace_detail { +enum WhitespaceFlag { NONE = 0, SPACE = 1, NEWLINE = 2, ALL = 3 }; +WhitespaceFlag current_flag = ALL; // At checker start, consume all whitespace. + +void pokeFlag(WhitespaceFlag flag) { + if (current_flag != NONE && (current_flag != NEWLINE || flag != ALL)) { + throw std::runtime_error("Never call two whitespace methods in a row, " + "except for readNewLine() followed by readEOF()."); + } + current_flag = flag; +} + +enum ConsumeResult { + NO_WHITESPACE, + NO_LINES, + LINES, +}; +ConsumeResult consumeWhitespace() { + int c = getchar(); + ConsumeResult result = NO_WHITESPACE; + while (isspace(c) && c != EOF) { + if (result == NO_WHITESPACE) { + result = NO_LINES; + } + if (c == '\r' || c == '\n') { + result = LINES; + } + c = getchar(); + } + ungetc(c, stdin); + current_flag = NONE; + return result; +} + +void preReadToken() { + switch (current_flag) { + case NONE: + throw std::runtime_error( + "Must not call readInt (or readToken, or readFloat) twice in a row!"); + case SPACE: + assertWA(consumeWhitespace() == NO_LINES); + break; + case NEWLINE: + assertWA(consumeWhitespace() == LINES); + break; + case ALL: + consumeWhitespace(); + break; + } +} +} // namespace standard_whitespace_detail + +namespace regex_helpers { +regex_t compile(const char *pattern) { + regex_t re; + if (regcomp(&re, pattern, REG_EXTENDED | REG_NOSUB) != 0) { + throw std::runtime_error("Pattern failed to compile."); + } + return re; +} +bool match(regex_t re, const std::string &text) { + return regexec(&re, text.c_str(), 0, NULL, 0) == 0; +} +} // namespace regex_helpers + +void errorHook(); +void exitWA() { + errorHook(); + std::exit(1); +} +void assertWA(bool condition) { + if (!condition) { + exitWA(); + } +} +void readSpace() { + standard_whitespace_detail::pokeFlag(standard_whitespace_detail::SPACE); +} +void readNewLine() { + standard_whitespace_detail::pokeFlag(standard_whitespace_detail::NEWLINE); +} +void readEOF() { + standard_whitespace_detail::pokeFlag(standard_whitespace_detail::ALL); + standard_whitespace_detail::consumeWhitespace(); + assertWA(getchar() == EOF); +} +std::string readToken(char min_char = 0, char max_char = 127) { + standard_whitespace_detail::preReadToken(); + static constexpr size_t MAX_TOKEN_SIZE = 1e7; + std::string token; + int c = getchar(); + assertWA(!isspace(c)); + while (!isspace(c) && c != EOF) { + assertWA(token.size() < MAX_TOKEN_SIZE); + assertWA(min_char <= c && c <= max_char); + token.push_back(char(c)); + c = getchar(); + } + ungetc(c, stdin); + return token; +} +long long readInt(long long lo, long long hi) { + static regex_t re = regex_helpers::compile("^(0|-?[1-9][0-9]*)$"); + std::string token = readToken(); + assertWA(regex_helpers::match(re, token)); + + long long parsedInt; + try { + parsedInt = stoll(token); + } catch (const std::invalid_argument &) { + exitWA(); + } catch (const std::out_of_range &) { + exitWA(); + } + assertWA(lo <= parsedInt && parsedInt <= hi); + return parsedInt; +} +long double readFloat(long double min, long double max, + long double eps = 1e-9) { + static regex_t re = regex_helpers::compile("^-?(0|[1-9][0-9])(\\.[0-9]+)?$"); + std::string token = readToken(); + assertWA(regex_helpers::match(re, token)); + long double parsedDouble; + try { + parsedDouble = stold(token); + } catch (const std::invalid_argument &) { + exitWA(); + } catch (const std::out_of_range &) { + exitWA(); + } + assertWA(min - eps <= parsedDouble && parsedDouble <= max + eps); + return parsedDouble; +} +template +std::vector readIntArray(size_t N, long long lo, long long hi) { + std::vector arr; + arr.reserve(N); + for (size_t i = 0; i < N; i++) { + arr.push_back(readInt(lo, hi)); + if (i != N - 1) { + readSpace(); + } + } + readNewLine(); + return arr; +} +void errorHook() {} + +// If this is a checker: +// int main(int argc, char **argv) { +// std::ifstream judge_input(argv[1]); +// freopen(argv[2], "r", stdin); +// std::ifstream judge_answer(argv[3]); +// } + +// If this is an interactor: +// int main(int argc, char **argv) { +// std::ifstream judge_input(argv[1]); +// std::ifstream judge_answer(argv[2]); +// } diff --git a/sample_files/problem_setting/test/run_test.sh b/sample_files/problem_setting/test/run_test.sh new file mode 100755 index 0000000..4f9e32b --- /dev/null +++ b/sample_files/problem_setting/test/run_test.sh @@ -0,0 +1,39 @@ +#!/bin/sh +for suite in $(ls testsuite); do + if ! g++ testsuite/$suite/main.cpp -I.. -o test; then + echo "Compilation of $suite failed." + return 1 + fi + echo "===Suite $suite===" + for testcase in $(ls testsuite/$suite/cases); do + casepath=testsuite/$suite/cases/$testcase + ./test < $casepath/input > output 2> error + + exit_code="$?" + expected_code=$(cat $casepath/code) + if [ "$exit_code" != "$expected_code" ]; then + echo "Wrong exit code for $suite/$testcase. Expected $expected_code, got $exit_code." + echo "stderr:" + cat error + + rm test output error + return 1 + fi + if ! cmp output $casepath/output; then + echo "Incorrect output for $suite/$testcase. Expected:" + cat $casepath/output + echo "But got:" + cat output + echo "stderr:" + cat error + + rm test output error + return 1 + fi + echo "$suite/$testcase passed." + done + echo "" +done + +rm test output error +echo "All testcases passed." diff --git a/sample_files/problem_setting/test/testsuite/esoteric_validator/cases/basic/code b/sample_files/problem_setting/test/testsuite/esoteric_validator/cases/basic/code new file mode 100644 index 0000000..573541a --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/esoteric_validator/cases/basic/code @@ -0,0 +1 @@ +0 diff --git a/sample_files/problem_setting/test/testsuite/esoteric_validator/cases/basic/input b/sample_files/problem_setting/test/testsuite/esoteric_validator/cases/basic/input new file mode 100644 index 0000000..c4c5f02 --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/esoteric_validator/cases/basic/input @@ -0,0 +1,2 @@ +ABABABBBBBB aa aa z zzz sdhiuwqiuwbq +dmoj diff --git a/sample_files/problem_setting/test/testsuite/esoteric_validator/cases/basic/output b/sample_files/problem_setting/test/testsuite/esoteric_validator/cases/basic/output new file mode 100644 index 0000000..4063f47 --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/esoteric_validator/cases/basic/output @@ -0,0 +1,2 @@ +129 +4 diff --git a/sample_files/problem_setting/test/testsuite/esoteric_validator/cases/noeol/code b/sample_files/problem_setting/test/testsuite/esoteric_validator/cases/noeol/code new file mode 100644 index 0000000..405e2af --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/esoteric_validator/cases/noeol/code @@ -0,0 +1 @@ +134 diff --git a/sample_files/problem_setting/test/testsuite/esoteric_validator/cases/noeol/input b/sample_files/problem_setting/test/testsuite/esoteric_validator/cases/noeol/input new file mode 100644 index 0000000..edd3ea7 --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/esoteric_validator/cases/noeol/input @@ -0,0 +1,2 @@ +A a +b \ No newline at end of file diff --git a/sample_files/problem_setting/test/testsuite/esoteric_validator/cases/noeol/output b/sample_files/problem_setting/test/testsuite/esoteric_validator/cases/noeol/output new file mode 100644 index 0000000..205a12b --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/esoteric_validator/cases/noeol/output @@ -0,0 +1 @@ +194 diff --git a/sample_files/problem_setting/test/testsuite/esoteric_validator/main.cpp b/sample_files/problem_setting/test/testsuite/esoteric_validator/main.cpp new file mode 100644 index 0000000..139d613 --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/esoteric_validator/main.cpp @@ -0,0 +1,22 @@ +#include + +#include "validator.cpp" + +// A validator that uses `readToken` and `readLine`. + +int main() { + std::string first = readToken('A', 'B'); + readSpace(); + std::string rest = readLine(); + assert(std::all_of(rest.begin(), rest.end(), [](char c) { + return isspace(c) || 'a' <= c && c <= 'z'; + })); + assert(rest.size() > 0 && rest.back() != '\n'); + printf("%d\n", int(rest.front()) + int(rest.back())); + fflush(stdout); + + std::string secondLine = readLine('a', 'z'); + printf("%d\n", (int)secondLine.size()); + + readEOF(); +} diff --git a/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/basic/code b/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/basic/code new file mode 100644 index 0000000..573541a --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/basic/code @@ -0,0 +1 @@ +0 diff --git a/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/basic/input b/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/basic/input new file mode 100644 index 0000000..c3ec801 --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/basic/input @@ -0,0 +1,2 @@ +5 +10 diff --git a/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/basic/output b/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/basic/output new file mode 100644 index 0000000..22f33e3 --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/basic/output @@ -0,0 +1 @@ +50.00 diff --git a/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/leading_zeroes/code b/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/leading_zeroes/code new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/leading_zeroes/code @@ -0,0 +1 @@ +1 diff --git a/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/leading_zeroes/input b/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/leading_zeroes/input new file mode 100644 index 0000000..1f1b968 --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/leading_zeroes/input @@ -0,0 +1,2 @@ +5 +000 diff --git a/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/leading_zeroes/output b/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/leading_zeroes/output new file mode 100644 index 0000000..e69de29 diff --git a/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/leading_zeroes2/code b/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/leading_zeroes2/code new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/leading_zeroes2/code @@ -0,0 +1 @@ +1 diff --git a/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/leading_zeroes2/input b/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/leading_zeroes2/input new file mode 100644 index 0000000..c27e64a --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/leading_zeroes2/input @@ -0,0 +1,2 @@ +5 +00.5 diff --git a/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/leading_zeroes2/output b/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/leading_zeroes2/output new file mode 100644 index 0000000..e69de29 diff --git a/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/nan/code b/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/nan/code new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/nan/code @@ -0,0 +1 @@ +1 diff --git a/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/nan/input b/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/nan/input new file mode 100644 index 0000000..3562c43 --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/nan/input @@ -0,0 +1,2 @@ +5 +NaN diff --git a/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/nan/output b/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/nan/output new file mode 100644 index 0000000..e69de29 diff --git a/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/scientific_notation/code b/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/scientific_notation/code new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/scientific_notation/code @@ -0,0 +1 @@ +1 diff --git a/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/scientific_notation/input b/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/scientific_notation/input new file mode 100644 index 0000000..1e7df3e --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/scientific_notation/input @@ -0,0 +1,2 @@ +5 +1e+9 diff --git a/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/scientific_notation/output b/sample_files/problem_setting/test/testsuite/identical_checker_interactor/cases/scientific_notation/output new file mode 100644 index 0000000..e69de29 diff --git a/sample_files/problem_setting/test/testsuite/identical_checker_interactor/main.cpp b/sample_files/problem_setting/test/testsuite/identical_checker_interactor/main.cpp new file mode 100644 index 0000000..43e70c3 --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/identical_checker_interactor/main.cpp @@ -0,0 +1,11 @@ +#include "identical_checker_interactor.cpp" + +int main() { + int N = readInt(1, 10); + readNewLine(); + float f = readFloat(-10, 10); + readNewLine(); + readEOF(); + + printf("%0.2f\n", f * N); +} diff --git a/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/basic/code b/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/basic/code new file mode 100644 index 0000000..573541a --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/basic/code @@ -0,0 +1 @@ +0 diff --git a/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/basic/input b/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/basic/input new file mode 100644 index 0000000..0f3ec82 --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/basic/input @@ -0,0 +1,6 @@ +5 5 +1 2 3 +2 3 -10 +3 4 10 +4 5 -5 +5 1 0 diff --git a/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/basic/output b/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/basic/output new file mode 100644 index 0000000..e69de29 diff --git a/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/newline_instead_of_space/code b/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/newline_instead_of_space/code new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/newline_instead_of_space/code @@ -0,0 +1 @@ +1 diff --git a/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/newline_instead_of_space/input b/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/newline_instead_of_space/input new file mode 100644 index 0000000..b04f28c --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/newline_instead_of_space/input @@ -0,0 +1,7 @@ +5 +5 +1 2 3 +2 3 -10 +3 4 10 +4 5 -5 +5 1 0 diff --git a/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/newline_instead_of_space/output b/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/newline_instead_of_space/output new file mode 100644 index 0000000..e69de29 diff --git a/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/no_newline/code b/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/no_newline/code new file mode 100644 index 0000000..573541a --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/no_newline/code @@ -0,0 +1 @@ +0 diff --git a/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/no_newline/input b/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/no_newline/input new file mode 100644 index 0000000..a57250e --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/no_newline/input @@ -0,0 +1,6 @@ +5 5 +1 2 3 +2 3 -10 +3 4 10 +4 5 -5 +5 1 0 \ No newline at end of file diff --git a/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/no_newline/output b/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/no_newline/output new file mode 100644 index 0000000..e69de29 diff --git a/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/trailing_tokens/code b/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/trailing_tokens/code new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/trailing_tokens/code @@ -0,0 +1 @@ +1 diff --git a/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/trailing_tokens/input b/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/trailing_tokens/input new file mode 100644 index 0000000..a7198de --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/trailing_tokens/input @@ -0,0 +1,5 @@ +3 2 +1 2 0 +2 3 9 + +3 1 6 diff --git a/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/trailing_tokens/output b/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/trailing_tokens/output new file mode 100644 index 0000000..e69de29 diff --git a/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/weird_whitespace/code b/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/weird_whitespace/code new file mode 100644 index 0000000..573541a --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/weird_whitespace/code @@ -0,0 +1 @@ +0 diff --git a/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/weird_whitespace/input b/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/weird_whitespace/input new file mode 100644 index 0000000..e035328 --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/weird_whitespace/input @@ -0,0 +1,15 @@ + + +5 5 +1 2 3 +2 3 -10 +3 4 10 + +4 5 -5 + +5 1 0 + + + + + diff --git a/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/weird_whitespace/output b/sample_files/problem_setting/test/testsuite/standard_checker_interactor/cases/weird_whitespace/output new file mode 100644 index 0000000..e69de29 diff --git a/sample_files/problem_setting/test/testsuite/standard_checker_interactor/main.cpp b/sample_files/problem_setting/test/testsuite/standard_checker_interactor/main.cpp new file mode 100644 index 0000000..0cd4d44 --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/standard_checker_interactor/main.cpp @@ -0,0 +1,18 @@ +#include "standard_checker_interactor.cpp" + +int main() { + int N = readInt(1, 10); + readSpace(); + int M = readInt(1, 10); + readNewLine(); + + for (int i = 0; i < M; i++) { + int a = readInt(1, N); + readSpace(); + int b = readInt(1, N); + readSpace(); + int c = readInt(-10, 10); + readNewLine(); + } + readEOF(); +} diff --git a/sample_files/problem_setting/test/testsuite/validator/cases/basic/code b/sample_files/problem_setting/test/testsuite/validator/cases/basic/code new file mode 100644 index 0000000..573541a --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/validator/cases/basic/code @@ -0,0 +1 @@ +0 diff --git a/sample_files/problem_setting/test/testsuite/validator/cases/basic/input b/sample_files/problem_setting/test/testsuite/validator/cases/basic/input new file mode 100644 index 0000000..8598312 --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/validator/cases/basic/input @@ -0,0 +1,2 @@ +5 100 +1 2 3 10 100 diff --git a/sample_files/problem_setting/test/testsuite/validator/cases/basic/output b/sample_files/problem_setting/test/testsuite/validator/cases/basic/output new file mode 100644 index 0000000..4699eb3 --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/validator/cases/basic/output @@ -0,0 +1 @@ +116 diff --git a/sample_files/problem_setting/test/testsuite/validator/cases/expect_integer_get_float/code b/sample_files/problem_setting/test/testsuite/validator/cases/expect_integer_get_float/code new file mode 100644 index 0000000..405e2af --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/validator/cases/expect_integer_get_float/code @@ -0,0 +1 @@ +134 diff --git a/sample_files/problem_setting/test/testsuite/validator/cases/expect_integer_get_float/input b/sample_files/problem_setting/test/testsuite/validator/cases/expect_integer_get_float/input new file mode 100644 index 0000000..a402786 --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/validator/cases/expect_integer_get_float/input @@ -0,0 +1,2 @@ +5 100 +1.0 2 3 10 100 diff --git a/sample_files/problem_setting/test/testsuite/validator/cases/expect_integer_get_float/output b/sample_files/problem_setting/test/testsuite/validator/cases/expect_integer_get_float/output new file mode 100644 index 0000000..e69de29 diff --git a/sample_files/problem_setting/test/testsuite/validator/cases/extra_space/code b/sample_files/problem_setting/test/testsuite/validator/cases/extra_space/code new file mode 100644 index 0000000..405e2af --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/validator/cases/extra_space/code @@ -0,0 +1 @@ +134 diff --git a/sample_files/problem_setting/test/testsuite/validator/cases/extra_space/input b/sample_files/problem_setting/test/testsuite/validator/cases/extra_space/input new file mode 100644 index 0000000..a8af8ea --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/validator/cases/extra_space/input @@ -0,0 +1,2 @@ +5 100 +1 2 3 10 100 diff --git a/sample_files/problem_setting/test/testsuite/validator/cases/extra_space/output b/sample_files/problem_setting/test/testsuite/validator/cases/extra_space/output new file mode 100644 index 0000000..e69de29 diff --git a/sample_files/problem_setting/test/testsuite/validator/cases/integer_out_of_range/code b/sample_files/problem_setting/test/testsuite/validator/cases/integer_out_of_range/code new file mode 100644 index 0000000..405e2af --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/validator/cases/integer_out_of_range/code @@ -0,0 +1 @@ +134 diff --git a/sample_files/problem_setting/test/testsuite/validator/cases/integer_out_of_range/input b/sample_files/problem_setting/test/testsuite/validator/cases/integer_out_of_range/input new file mode 100644 index 0000000..6930798 --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/validator/cases/integer_out_of_range/input @@ -0,0 +1,2 @@ +5 10 +1 2 3 8 11 diff --git a/sample_files/problem_setting/test/testsuite/validator/cases/integer_out_of_range/output b/sample_files/problem_setting/test/testsuite/validator/cases/integer_out_of_range/output new file mode 100644 index 0000000..e69de29 diff --git a/sample_files/problem_setting/test/testsuite/validator/cases/malformed_integer/code b/sample_files/problem_setting/test/testsuite/validator/cases/malformed_integer/code new file mode 100644 index 0000000..405e2af --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/validator/cases/malformed_integer/code @@ -0,0 +1 @@ +134 diff --git a/sample_files/problem_setting/test/testsuite/validator/cases/malformed_integer/input b/sample_files/problem_setting/test/testsuite/validator/cases/malformed_integer/input new file mode 100644 index 0000000..44570a0 --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/validator/cases/malformed_integer/input @@ -0,0 +1,2 @@ +5 10 +1 2 3 8 10a diff --git a/sample_files/problem_setting/test/testsuite/validator/cases/malformed_integer/output b/sample_files/problem_setting/test/testsuite/validator/cases/malformed_integer/output new file mode 100644 index 0000000..e69de29 diff --git a/sample_files/problem_setting/test/testsuite/validator/cases/trailing_newline/code b/sample_files/problem_setting/test/testsuite/validator/cases/trailing_newline/code new file mode 100644 index 0000000..405e2af --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/validator/cases/trailing_newline/code @@ -0,0 +1 @@ +134 diff --git a/sample_files/problem_setting/test/testsuite/validator/cases/trailing_newline/input b/sample_files/problem_setting/test/testsuite/validator/cases/trailing_newline/input new file mode 100644 index 0000000..5971c0c --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/validator/cases/trailing_newline/input @@ -0,0 +1,3 @@ +5 10 +1 2 3 8 10 + diff --git a/sample_files/problem_setting/test/testsuite/validator/cases/trailing_newline/output b/sample_files/problem_setting/test/testsuite/validator/cases/trailing_newline/output new file mode 100644 index 0000000..e69de29 diff --git a/sample_files/problem_setting/test/testsuite/validator/main.cpp b/sample_files/problem_setting/test/testsuite/validator/main.cpp new file mode 100644 index 0000000..454f2ef --- /dev/null +++ b/sample_files/problem_setting/test/testsuite/validator/main.cpp @@ -0,0 +1,15 @@ +#include + +#include "validator.cpp" + +int main() { + int N = readInt(1, 10); + readSpace(); + int M = readInt(1, 100); + readNewLine(); + + auto arr = readIntArray(N, 1, M); + readEOF(); + + printf("%d\n", std::accumulate(arr.begin(), arr.end(), 0)); +} diff --git a/sample_files/problem_setting/validator.cpp b/sample_files/problem_setting/validator.cpp new file mode 100644 index 0000000..d59d073 --- /dev/null +++ b/sample_files/problem_setting/validator.cpp @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include + +namespace regex_helpers { +regex_t compile(const char *pattern) { + regex_t re; + assert(regcomp(&re, pattern, REG_EXTENDED | REG_NOSUB) == 0); + return re; +} +bool match(regex_t re, const std::string &text) { + return regexec(&re, text.c_str(), 0, NULL, 0) == 0; +} +} // namespace regex_helpers + +void readSpace() { assert(getchar() == ' '); } +void readNewLine() { assert(getchar() == '\n'); } +void readEOF() { assert(getchar() == EOF); } + +std::string readToken(char min_char = 0, char max_char = 127) { + static constexpr size_t MAX_TOKEN_SIZE = 1e7; + std::string token; + int c = getchar(); + assert(!isspace(c)); + while (!isspace(c) && c != EOF) { + assert(token.size() < MAX_TOKEN_SIZE); + assert(min_char <= c && c <= max_char); + token.push_back(char(c)); + c = getchar(); + } + ungetc(c, stdin); + return token; +} +std::string readLine(char min_char = 0, char max_char = 127) { + static constexpr size_t MAX_LINE_SIZE = 1e7; + std::string line; + int c = getchar(); + while (c != '\n') { + assert(line.size() < MAX_LINE_SIZE); + assert(c != EOF); + assert(min_char <= c && c <= max_char); + line.push_back(char(c)); + c = getchar(); + } + return line; +} +long long readInt(long long lo, long long hi) { + static regex_t re = regex_helpers::compile("^(0|-?[1-9][0-9]*)$"); + std::string token = readToken(); + assert(regex_helpers::match(re, token)); + + long long parsedInt = stoll(token); // May throw. + assert(lo <= parsedInt && parsedInt <= hi); + return parsedInt; +} +long double readFloat(long double min, long double max, + long double eps = 1e-9) { + static regex_t re = regex_helpers::compile("^-?(0|[1-9][0-9])(\\.[0-9]+)?$"); + std::string token = readToken(); + assert(regex_helpers::match(re, token)); + long double parsedDouble = stold(token); // May throw. + assert(min - eps <= parsedDouble && parsedDouble <= max + eps); + return parsedDouble; +} +template +std::vector readIntArray(size_t N, long long lo, long long hi) { + std::vector arr; + arr.reserve(N); + for (size_t i = 0; i < N; i++) { + arr.push_back(readInt(lo, hi)); + if (i != N - 1) { + readSpace(); + } + } + readNewLine(); + return arr; +}