From 195ee9888d55099e1224eb8daaaacfea06962eb8 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 | 93 +++++++++ .../examples/identical_checker.cpp | 132 +++++++++++++ .../examples/standard_interactor.cpp | 179 ++++++++++++++++++ .../problem_setting/examples/validator.cpp | 59 ++++++ .../identical_checker_interactor.cpp | 136 +++++++++++++ .../standard_checker_interactor.cpp | 177 +++++++++++++++++ sample_files/problem_setting/test/run_test.sh | 43 +++++ .../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 ++++++++ 68 files changed, 1075 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/examples/identical_checker.cpp create mode 100644 sample_files/problem_setting/examples/standard_interactor.cpp create mode 100644 sample_files/problem_setting/examples/validator.cpp 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..5981848 --- /dev/null +++ b/docs/problem_format/cpp_psetting_templates.md @@ -0,0 +1,93 @@ +# 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) + +Examples of their use are as follows: + +- A validator for is [here](https://github.com/DMOJ/docs/blob/master/sample_files/problem_setting/examples/validator.cpp). +- An identical-style checker for is [here](https://github.com/DMOJ/docs/blob/master/sample_files/problem_setting/examples/identical_checker.cpp). +- A standard-style interactor for is [here](https://github.com/DMOJ/docs/blob/master/sample_files/problem_setting/examples/standard_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 function 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)` calls `readToken()` and parses the 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)` calls `readToken()` and parses the 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 in the decimal portion 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. + +A small caveat: `readToken` and `readLine` will throw if the string exceeds 10 million characters. + +`readFloat()` and `readLine()` 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. + +### Identical Checker/Interactor + +This template expects whitespace to match exactly, just as in the validator. The template is simpler, but it is less forgiving to contestants. + +The same functions are provided, but have slightly different behaviour: + +- `readSpace(), readNewLine(), readEOF()`: These return Presentation Error if the check fails. +- `readToken()`: This exits with a Presentation Error if the token is empty, and WA if any character is not in range. +- `readLine()`: This exits with a Presentation Error if an EOF is encountered, and WA if any character is not in range. +- `readInt(), readIntArray(), readFloat()`: These exit with WA on failure. + +Four new functions are provided: + +- `exitWA(), exitPE()`: These functions exit immediately with the specified code. +- `assertWA(bool), assertPE(bool)`: These functions exit if the provided condition is false. Useful as a replacement for `assert()`. + +One new namespace is provided. The `CheckerCodes` namespace contains the constants `AC, WA, PE`, and `PARTIAL`. It is recommended to use them in `main` to return a verdict. For instance, to return an AC verdict, use `return CheckerCodes::AC;` + +Finally, there is an empty function `errorHook()`, which is called whenever any of the functions in the API would exit with an error. This can be used to implement functionality such as partial points, or outputting `-1` to signal errors in interactors. + +### Standard Checker/Interactor + +This template is much more complex, but is more lenient for submissions. It matches the whitespace of the `standard` builtin checker. + +The behaviour of the non-whitespace functions are the same as for the identical checker template, with the following caveats: + +- `exitPE()` and `assertPE()` don't exist, since the builtin `standard` checker never uses the Presentation Error code. Checker writers are discouraged from using it. +- `readToken()` always exits with WA on failure. +- `readLine()` doesn't exist, since the way it should process whitespace is not clear for the standard checker. Checker writers reaching for this method should consider the identical checker template instead, or rethink their output format entirely. +- `CheckerCodes` doesn't contain the constant for `PE`. + +The remaining functions have the same behaviour as with the identical checker. + +#### Whitespace functions + +These functions all exit with `WA` on failure instead of `PE`, for the reasons described above. + +The code maintains a flag of the type of whitespace it expects, one of `NONE, SPACE, NEWLINE, ALL`, initially ALL. `readSpace()` sets the flag to `SPACE` and `readNewLine()` sets it to `NEWLINE`. + +`readToken()` sets the flag to `NONE` and consumes all the whitespace, and exits with WA if either: + +- The current flag is `SPACE` and a newline was found. +- The current flag is `NEWLINE` and no newline was found. + +It never exits with WA if the flag is `ALL`. It causes an IE if the flag is `NONE`, which only happens when `readToken()` is called twice in a row without an intervening whitespace function. Note that `readInt()` and `readFloat()` call `readToken()` internally. + +`readEOF()` sets the flag to `ALL`, consumes all whitespace, and then exits with WA if any character remains in the stream. + +No two whitespace functions can be called back to back, except for `readNewLine()` followed by `readEOF()`. The reason for the exception is that the canonical form for output should have a trailing newline, and so this exception allows checkers writers to think in terms of the canonical form of the output. Also, it allows calling `readIntArray()` right before `readEOF()`, since `readIntArray()` internally calls `readNewLine()`. + +Note that this scheme is lazy. This is intentional; it allows the same code to be used by interactors without difficulty. diff --git a/sample_files/problem_setting/examples/identical_checker.cpp b/sample_files/problem_setting/examples/identical_checker.cpp new file mode 100644 index 0000000..8cd8b60 --- /dev/null +++ b/sample_files/problem_setting/examples/identical_checker.cpp @@ -0,0 +1,132 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace CheckerCodes { +int AC = 0; +int WA = 1; +int PE = 2; +int PARTIAL = 7; +} // namespace CheckerCodes + +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(CheckerCodes::WA); +} +void exitPE() { + errorHook(); + std::exit(CheckerCodes::PE); +} +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; +} +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; +} +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() {} + +// readLine() and readFloat() removed for brevity. + +int main(int argc, char **argv) { + std::ifstream judge_input(argv[1]); + freopen(argv[2], "r", stdin); + std::ifstream judge_answer(argv[3]); + + int N, K; + judge_input >> N >> K; + + // If any integer is greater than K, we give an immediate WA. + std::vector arr = readIntArray(N, 0, K); + // We can read EOF now, since we are done with the input. This makes it easier + // to remember. + readEOF(); + + // Note that we must store the sum in a long long, since it may overflow a + // 32-bit integer. + long long sum = std::accumulate(arr.begin(), arr.end(), 0LL); + + assertWA(sum == K); + + // It turns out that the minimum product is always zero, since [0, 0, ..., K] + // is always valid. + // Thus, it suffices to check for a zero in the array. + if (std::find(arr.begin(), arr.end(), 0) == arr.end()) { + // No zero found. Give partial points: + // Output to stderr for the coci contrib module to grant partial AC. + fprintf(stderr, "partial 50/100\n"); + // Output to stdout to give user feedback. + printf("50/100 points"); + return CheckerCodes::PARTIAL; + } + return CheckerCodes::AC; +} diff --git a/sample_files/problem_setting/examples/standard_interactor.cpp b/sample_files/problem_setting/examples/standard_interactor.cpp new file mode 100644 index 0000000..acd4651 --- /dev/null +++ b/sample_files/problem_setting/examples/standard_interactor.cpp @@ -0,0 +1,179 @@ +#include +#include +#include +#include +#include +#include + +namespace CheckerCodes { +int AC = 0; +int WA = 1; +int PARTIAL = 7; +} // namespace CheckerCodes + +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(CheckerCodes::WA); +} +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; +} +// readFloat() and readIntArray() removed for brevity. + +void putAndFlush(const char *message) { + puts(message); + fflush(stdout); +} + +void errorHook() { + // Not specified by the problem description, but this is an example of + // outputting a flag value. + putAndFlush("INVALID"); +} + +// readFloat() and readIntArray() were removed for brevity. + +int main(int argc, char **argv) { + // Under an interactor, stdin already points to the user's output stream. + std::ifstream judge_input(argv[1]); + std::ifstream judge_answer(argv[2]); + + // Assume the judge answer contains the correct pressure. + int answer; + judge_answer >> answer; + + int num_guesses = 0; + for (;;) { + // If we exceed the number of guesses, we will quit. + num_guesses++; + assertWA(num_guesses <= 31); + + int guess = readInt(1, 2e9); + readNewLine(); + if (guess < answer) { + putAndFlush("SINKS"); + } else if (guess > answer) { + putAndFlush("FLOATS"); + } else { + putAndFlush("OK"); + // Close our output pipe now that we are done interacting. Then, make sure + // the user has no trailing data. + fclose(stdout); + readEOF(); + return CheckerCodes::AC; + } + } +} diff --git a/sample_files/problem_setting/examples/validator.cpp b/sample_files/problem_setting/examples/validator.cpp new file mode 100644 index 0000000..f578a24 --- /dev/null +++ b/sample_files/problem_setting/examples/validator.cpp @@ -0,0 +1,59 @@ +#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; +} +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; +} + +// NOTE: `readLine(), readFloat(), and readIntArray()` were removed for brevity. + +int main() { + int N = readInt(1, 1e5); + for (int i = 0; i < N; i++) { + int A = readInt(-1e9, 1e9); + readSpace(); + int B = readInt(-1e9, 1e9); + readNewLine(); + + printf("%d\n", A + B); + } + // A common mistake is forgetting to read EOF: be careful! + readEOF(); +} 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..2e66002 --- /dev/null +++ b/sample_files/problem_setting/identical_checker_interactor.cpp @@ -0,0 +1,136 @@ +#include +#include +#include +#include +#include +#include +#include + +namespace CheckerCodes { +int AC = 0; +int WA = 1; +int PE = 2; +int PARTIAL = 7; +} // namespace CheckerCodes + +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(CheckerCodes::WA); +} +void exitPE() { + errorHook(); + std::exit(CheckerCodes::PE); +} +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..e520283 --- /dev/null +++ b/sample_files/problem_setting/standard_checker_interactor.cpp @@ -0,0 +1,177 @@ +#include +#include +#include +#include +#include +#include +#include + +namespace CheckerCodes { +int AC = 0; +int WA = 1; +int PARTIAL = 7; +} // namespace CheckerCodes + +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(CheckerCodes::WA); +} +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..8c99514 --- /dev/null +++ b/sample_files/problem_setting/test/run_test.sh @@ -0,0 +1,43 @@ +#!/bin/sh + +BLUE="\e[0;34m" +GREEN="\e[0;32m" +WHITE="\e[0m" + +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 -e ${BLUE}"===Suite $suite==="${WHITE} + 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 -e "$suite/$testcase" ${GREEN}"passed."${WHITE} + done +done + +rm test output error +echo -e ${GREEN}"All testcases passed."${WHITE} 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; +}