diff --git a/example_package/.gitignore b/example_package/.gitignore new file mode 100644 index 00000000..1e728bc9 --- /dev/null +++ b/example_package/.gitignore @@ -0,0 +1,76 @@ +# sinol-make +.cache +cache +in/.md5sums + +# Tests +in/*.in +out/*.out +!in/???0*.in +!out/???0*.out + +# export package file +*.tgz + +# LaTeX +*.pdf +*.ps +!doc/logo.* + +*.aux +*.lof +*.log +*.lot +*.fls +doc/*.out +*.toc +*.fmt +*.fot +*.cb +*.cb2 +.*.lb + +*.dvi +*.xdv +*-converted-to.* +# these rules might exclude image files for figures etc. +*.eps + +## Bibliography auxiliary files (bibtex/biblatex/biber): +*.bbl +*.bcf +*.blg +*-blx.aux +*-blx.bib +*.run.xml + +# Encrypted files +*.gpg + +# SIO binnary +*.e + +# Python Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# pyenv +.python-version + +# IPython +profile_default/ +ipython_config.py + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so +*.o diff --git a/examples/config.yml b/example_package/config.yml similarity index 94% rename from examples/config.yml rename to example_package/config.yml index 21baee08..b6228d8c 100644 --- a/examples/config.yml +++ b/example_package/config.yml @@ -13,10 +13,8 @@ title_pl: Przykładowe zadanie # (if number of groups doesn't divide 100, then the last groups will have the remaining points). # Group 0 always has zero points. scores: - 1: 20 - 2: 30 - 3: 25 - 4: 25 + 1: 40 + 2: 60 # Time limit for all tests is defined in `time_limit` key. # More precise time limit for each group or test can be defined in `time_limits` key. @@ -43,18 +41,22 @@ override_limits: # Extra compilation arguments can be defined in `extra_compile_args` key. # Each language can have different extra arguments. -extra_compilation_args: - cpp: 'abclib.cpp' + +# extra_compilation_args: +# cpp: 'abclib.cpp' + # The arguments can also be in an array: -extra_compilation_args: - cpp: - - 'abclib.cpp' - - 'abclib2.cpp' + +# extra_compilation_args: +# cpp: +# - 'abclib.cpp' +# - 'abclib2.cpp' # Additional files used in compilation can be defined in `extra_compilation_files` key. # They are copied to the directory where the source code is compiled. # All languages have the same additional files. -extra_compilation_files: ['abclib.cpp', 'abclib.py'] + +# extra_compilation_files: ['abclib.cpp', 'abclib.py'] ### Keys used by sinol-make: diff --git a/example_package/in/.gitkeep b/example_package/in/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/example_package/out/.gitkeep b/example_package/out/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/example_package/prog/abc.cpp b/example_package/prog/abc.cpp new file mode 100644 index 00000000..2d1f3b2f --- /dev/null +++ b/example_package/prog/abc.cpp @@ -0,0 +1,12 @@ +// This is the main model solution. +// It is used for generating output files. + +#include + +using namespace std; + +int main() { + int a, b; + cin >> a >> b; + cout << a + b << endl; +} diff --git a/example_package/prog/abcingen.cpp b/example_package/prog/abcingen.cpp new file mode 100644 index 00000000..b5a0f7db --- /dev/null +++ b/example_package/prog/abcingen.cpp @@ -0,0 +1,45 @@ +#include +#include "oi.h" +using namespace std; + +// Change this function to generate one test for stresstesting. +// The script prog/abcingen.sh in 10 seconds generates +// as much tests as possible and compares the outputs +// of the model solution and brute solution. +// The tests shouldn't be very big, but should be able to cover edge cases. +void generate_one_stresstest(oi::Random &rng) { + cout << rng.randSInt(1, 10) << ' ' << rng.randSInt(1, 10) << endl; +} + +// Change this function to create a test with the given name. +// The lists of tests to generate needs to be written in prog/abcingen.sh +void generate_proper_test(string test_name, oi::Random &rng) { + if (test_name == "0a") + cout << "0 1" << endl; + else if (test_name == "1a") + cout << rng.randSInt(5, 1'000) << ' ' << rng.randSInt(5, 1'000) << endl; + else if (test_name == "2a") + cout << "2 2" << endl; + else { + cerr << "Unrecognized test_name = " << test_name << endl; + exit(1); + } +} + +int main(int argc, char *argv[]) { + if (argc == 3 && string(argv[1]) == "stresstest") { + unsigned int seed = atoi(argv[2]); + oi::Random rng(seed); + generate_one_stresstest(rng); + return 0; + } + if (argc != 2) { + cerr << "Run prog/abcingen.sh to stresstest and create proper tests." << endl; + exit(1); + } + string test_name = argv[1]; + unsigned int seed = (unsigned int) hash{}(test_name); + oi::Random rng(seed); + cerr << "Generating test " << test_name << "..." << endl; + generate_proper_test(test_name, rng); +} diff --git a/example_package/prog/abcingen.sh b/example_package/prog/abcingen.sh new file mode 100644 index 00000000..ad7b6e87 --- /dev/null +++ b/example_package/prog/abcingen.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +# This script first stresstests the model solution for 10 seconds +# and if it passes, it will generate the proper tests. +# To generate both types of tests, it executes ingen.cpp and passes it some arguments. +# The `test_ids` variable needs to have a manually written list of all proper tests. + +prog_dir="$(realpath "$(dirname "$0")")" +cache_dir="$prog_dir/../.cache" +mkdir -p "$cache_dir" +script_name="$(basename "$0")" +task_id=${script_name:0:3} +gen_exe="$cache_dir/${task_id}ingen" +sol_exe="$cache_dir/${task_id}solution" +slo_exe="$cache_dir/${task_id}slow" +stresstest_seconds=10 +function compile_cpp { + g++ -std=c++20 -O3 -lm -Werror -Wall -Wextra -Wshadow -Wconversion -Wno-unused-result -Wfloat-equal "$1" -o "$2" \ + || exit 1 +} + +# Change the list of tests to generate and (if needed) the paths of solutions. +test_ids="0a 1a 2a" +compile_cpp "$prog_dir/${task_id}ingen.cpp" "$gen_exe" +compile_cpp "$prog_dir/${task_id}.cpp" "$sol_exe" +compile_cpp "$prog_dir/${task_id}s.cpp" "$slo_exe" + +for (( i=0, SECONDS=0; SECONDS < stresstest_seconds; i++ )); do + in_test="$cache_dir/input.in" + slo_out="$cache_dir/slo.out" + sol_out="$cache_dir/sol.out" + printf "Running stresstest $i\r" + "$gen_exe" stresstest $i > "$in_test" || { echo "Failed to generate test $i"; exit 1; } + "$slo_exe" < "$in_test" > "$slo_out" || { echo "Brute crashed on test $i"; exit 1; } + "$sol_exe" < "$in_test" > "$sol_out" || { echo "Solution crashed on test $i"; exit 1; } + diff "$slo_out" "$sol_out" -w > /dev/null || { echo "Outputs differ on test $i"; exit 1; } +done +echo "Stresstest passed with $i tests" + +for test in $test_ids; do + "$gen_exe" "$test" > "$prog_dir/../in/${task_id}${test}.in" || { echo "Failed to generate test $test"; exit 1; } +done diff --git a/example_package/prog/abcinwer.cpp b/example_package/prog/abcinwer.cpp new file mode 100644 index 00000000..2bfb3a5f --- /dev/null +++ b/example_package/prog/abcinwer.cpp @@ -0,0 +1,56 @@ +#include +#include "oi.h" +using namespace std; + +int main() { + oi::Scanner in(stdin, oi::PL); + + // Change this code to read and validate the input. + int n = in.readInt(0, 1'000); + in.readSpace(); + int m = in.readInt(0, 1'000); + in.readEoln(); + in.readEof(); + assert(n > 0 || m > 0); + + // Change this code to have functions which return + // whether the test satisfies a given subtask. + auto is_subtask1 = [&]() -> bool { + return n >= 0 && m >= 0; + }; + auto is_subtask2 = [&]() -> bool { + return n >= 5 && m >= 5; + }; + + // Change this code to have functions which return + // whether the test is exactly the same as + // the sample tests in the statement. + auto is_0a = [&]() -> bool { + return n == 0 && m == 1; + }; + auto is_1ocen = [&]() -> bool { + return n == 1000 && m == 1000; + }; + + map subtasks = { + {1, is_subtask1()}, + {2, is_subtask2()}, + }; + string subtasks_s; + for (auto [subtask_id, is_valid] : subtasks) + subtasks_s += is_valid ? to_string(subtask_id) : string("-"); + + map sample_tests = { + {"0a", is_0a()}, + {"1ocen", is_1ocen()}, + }; + string sample_test_s = "-"; + for (auto [name, is_valid] : sample_tests) + if (is_valid) + sample_test_s = name; + + cout << "OK " + << "n = " << setw(4) << n << ", " + << "m = " << setw(4) << m << ", " + << "subtasks = " << subtasks_s << ", sample test = " << sample_test_s << endl; +} diff --git a/example_package/prog/abcs.cpp b/example_package/prog/abcs.cpp new file mode 100644 index 00000000..b25a5dff --- /dev/null +++ b/example_package/prog/abcs.cpp @@ -0,0 +1,11 @@ +// This is a "brute force" solution for testing model solution. + +#include + +using namespace std; + +int main() { + int a, b; + cin >> a >> b; + cout << a + b << endl; +} diff --git a/example_package/prog/oi.h b/example_package/prog/oi.h new file mode 100644 index 00000000..3dc11bd8 --- /dev/null +++ b/example_package/prog/oi.h @@ -0,0 +1,651 @@ +/* + oi.h - pakiet funkcji do pisania weryfikatorow wejsc (inwer) i wyjsc (chk) + Pierwotny autor: Piotr Niedzwiedz + W razie problemow, bledow lub pomyslow na ulepszenie prosze pisac issues: https://sinol3.dasie.mimuw.edu.pl/sinol3/template-package +*/ + +#ifndef OI_LIB_OI_H_ +#define OI_LIB_OI_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +using std::vector; +using std::max; +using std::swap; + +// We want prevent usage of standard random function. +int rand(){ + fprintf(stderr, "DONT USE rand or random_shuffle!\nUse oi::Random class.\n"); + exit(1); + return 0; +} + +namespace oi { + +enum Lang { + EN = 0, + PL = 1 +}; + +class Reader; + +class Scanner { + protected: + static const int realNumbersLimit = 20; + + Lang lang; + Reader* reader; + void(*end)(const char* msg, int line, int position); + + void readULL(unsigned long long int limit, unsigned long long int &val, bool &sign); + void readLDB(long double &val, bool &sign); + + public: + Scanner(const char* file, Lang _lang = Lang(EN)); + Scanner(const char* file, void(*endf)(const char* msg, int line, int position), Lang _lang = Lang(EN)); + Scanner(FILE* input, Lang _lang = Lang(EN)); + Scanner(FILE* input, void(*endf)(const char* msg, int line, int position), Lang _lang = Lang(EN)); + ~Scanner(); + + void error(const char* msg); + + // Skips all whitespaces until any occurrence of EOF or other non-white character. + int skipWhitespaces(); + // Skips all whitespaces until any occurrence of EOF, EOLN or other non-white character. + int skipWhitespacesUntilEOLN(); + + int readInt(int min_value = INT_MIN, int max_value = INT_MAX); + unsigned int readUInt(unsigned int min_value = 0, unsigned int max_value = UINT_MAX); + long long readLL(long long int min_value = LLONG_MIN, long long int max_value = LLONG_MAX); + unsigned long long readULL(unsigned long long int min_value = 0, unsigned long long int max_value = ULLONG_MAX); + + float readFloat(float min_value, float max_value); + double readDouble(double min_value, double max_value); + long double readLDouble(long double min_value, long double max_value); + + char readChar(); + + // Newline character is read, but isn't added to s + int readLine(char* s, int size); + + // Reads a string until occurrence of EOF, EOLN or whitespace. + // Returns the number of characters read (possibly 0). + int readString(char* s, int size); + + void readEof(); + void readEofOrEoln(); + void readEoln(); + void readSpace(); + void readTab(); + + bool isEOF(); + private: + Scanner(Scanner&) {} +}; + +class MultipleRecursiveGenerator; + +class Random { + public: + Random(); + explicit Random(unsigned int seed); + ~Random(); + void setSeed(unsigned int seed); + + // Random number from range <0..2^31 - 1> + int rand(); + + // randS* - signed * + // randU* - unsigned * + int randSInt(); + unsigned int randUInt(); + long long randSLL(); + unsigned long long randULL(); + + /** + * Generuje liczbe pseudo-losowa z przedzialu [a..b] (obustronnie wlacznie). + * Podanie na wejsciu pustego przedzialu (b < a) jest bledem i skutkuje + * dzialaniem niezdefiniowanym. + */ + int randSInt(const int a, const int b); + + template + void randomShuffle(RandomAccessIterator first, RandomAccessIterator last); + + private: + Random(Random&) {} + MultipleRecursiveGenerator* mrg_; + void init(); +}; + +class MultipleRecursiveGenerator { + public: + MultipleRecursiveGenerator(unsigned int modulo, + const vector &A); + void setSeed(unsigned int seed); + + /** + * Generuje liczbe pseudo-losowa z przedzialu [0..modulo-1]. Chcemy zeby to + * bylo co najmniej 16 bitow, czyli musi byc (0xFFFF < modulo). Gwarantuje + * to asercja w konstruktorze klasy. + */ + unsigned int next16Bits(); + + private: + long int n_; + unsigned int modulo_; + vector A_; + vector X_; +}; + +class Reader { + private: + static const int bufferSize = 1000; + char Buffer[bufferSize]; + int head, tail; + int line, position; + void fillBuffer(); + FILE* input; + public: + explicit Reader(const char* file); + explicit Reader(FILE* _input); + ~Reader(); + bool isEOF(); + int getLine() {return line;} + int getPosition() {return position;} + char read(bool move = false); + private: + Reader(Reader&) {}; +}; + + +const char* msgLeadingZeros[]= { + "Leading zeros", + "Zera wiodace"}; +const char* msgMinusZero[]= { + "Minus zero -0", + "Minus zero -0"}; +const char* msgNoNumber[]= { + "No number", + "Brak liczby"}; +const char* msgNoChar[]= { + "No char - EOF", + "Brak znaku - EOF"}; +const char* msgNotEof[]= { + "Not EOF", + "Brak konca pliku"}; +const char* msgNotEoln[]= { + "Not EOLN", + "Brak konca linii"}; +const char* msgNotEofOrEoln[]= { + "Not EOF or EOLN", + "Brak konca linii i brak konca pliku"}; +const char* msgNotSpace[]= { + "Not space", + "Brak spacji"}; +const char* msgNotTab[]= { + "Not tab", + "Brak znaku tabulacji"}; +const char* msgOutOfRangeInt[]= { + "Integer out of range", + "Liczba calkowita spoza zakresu"}; +const char* msgOutOfRangeReal[]= { + "Real number out of range", + "Liczba rzeczywista spoza zakresu"}; +const char* msgRealNumberLimit[]= { + "Too many digits after dot", + "Za duzo cyfr po kropce dziesietnej"}; +const char* msgBadRealNumberFormat[]= { + "Bad real number format", + "Niepoprawny format liczby rzeczywistej"}; + +// ------------------------------- Implementation ----------------------------- + +typedef unsigned long long ull; +typedef unsigned int uint; +typedef long long ll; +typedef long double ldb; + + +inline bool isDot(char x) { + return x == '.'; +} + +inline bool isEOLN(char x) { + return x == '\n'; +} + +inline bool isMinus(char x) { + return x == '-'; +} + +inline bool isSpace(char x) { + return x == ' '; +} + +inline bool isTab(char x) { + return x == '\t'; +} + +inline bool isWhitespace(char x) { + return x == ' ' || x == '\t' || x == '\n'; +} + +void endDefault(const char* msg, int line, int position) { + printf("ERROR(line: %d, position: %d): %s\n", line, position, msg); + exit(1); +} + +// ------------------------------- Random ------------------------------------- + +void Random::init() { +// Here is a reference about it: +// http://random.mat.sbg.ac.at/results/karl/server/node7.html + vector A(5, 0); + static_assert(4<=sizeof(int), "Typ int musi miescic co najmniej 4 bajty."); + static_assert(0x7FFFFFFFLL<=INT_MAX, "Wartosc 2^31-1 musi miescic sie w typie int."); + A[0] = 107374182, A[4] = 104480; + unsigned int modulo = 2147483647; // 2^31 -1 + mrg_ = new MultipleRecursiveGenerator(modulo, A); +} + +Random::Random() { + init(); +} + +Random::Random(unsigned int seed) { + init(); + setSeed(seed); +} + +Random::~Random() { + delete mrg_; +} + +void Random::setSeed(unsigned int seed) { + mrg_->setSeed(seed); +} + +static_assert(((8==CHAR_BIT) && (UCHAR_MAX==0xFF)), "Wiecej niz 8 bitow w bajcie? Nie wspierane, sorry."); +#define RAND_TYPE(type)\ + type res = 0;\ + for (size_t i = 0; i < sizeof(type)/2; ++i) {\ + res |= (((type)mrg_->next16Bits()) & (0xFFFF)) << (i * 16);\ + }\ + return res; + + +int Random::rand() { + int x = randSInt(); + if (x<0) return ~x; + return x; +} + +int Random::randSInt(const int a, const int b) { + return (rand() % (b-a+1)) + a; +} + +int Random::randSInt() { + RAND_TYPE(int); +} + +unsigned int Random::randUInt() { + RAND_TYPE(unsigned int); +} + +long long Random::randSLL() { + RAND_TYPE(long long); +} + +unsigned long long Random::randULL() { + RAND_TYPE(unsigned long long); +} + +template +void Random::randomShuffle(RandomAccessIterator first, RandomAccessIterator last) { + long int n = last - first; + for (int i = 1; i < n; ++i) { + int to = rand() % (i+1); + swap(first[to], first[i]); + } +} + +// ----------------------- MultipleRecursiveGenerator ------------------------- + +MultipleRecursiveGenerator::MultipleRecursiveGenerator( + unsigned int modulo, + const vector &A) : modulo_(modulo), A_(A) { + assert(0xFFFFUL < modulo); + n_ = A_.size(); + X_ = vector(n_, 0); + setSeed(0); +} + +void MultipleRecursiveGenerator::setSeed(unsigned int seed) { + for (int i = 0; i < n_; ++i) { + seed = (seed + 1) % modulo_; + X_[i] = seed; + } + for (int i = 0; i < n_; ++i) next16Bits(); +} + +unsigned int MultipleRecursiveGenerator::next16Bits() { + unsigned int res = 0; + static_assert(2 * sizeof(unsigned int) <= sizeof(unsigned long long), "Mozliwe przepelnienie arytmetyczne!"); + for (int i = 0; i < n_; ++i) { + res = (unsigned int)((res + (unsigned long long)A_[i] * X_[i]) % (unsigned long long)modulo_); + if (i < n_ - 1) X_[i] = X_[i+1]; + } + X_[n_ - 1] = res; + return res; +} + +// --------------------------- Reader's methods ------------------------------- + +Reader::Reader(const char* file) { + assert((input = fopen(file, "r")) != NULL); + head = tail= 0; + line = position = 1; +} + +Reader::Reader(FILE* _input) { + input = _input; + head = tail = 0; + line = position = 1; +} + +Reader::~Reader() { + assert(fclose(input) == 0); +} + +void Reader::fillBuffer() { + while ((tail + 1) % bufferSize != head) { + int v = getc(input); + if (v == EOF) break; + Buffer[tail] = (char)v; + tail = (tail + 1) % bufferSize; + } +} + +bool Reader::isEOF() { + fillBuffer(); + return head == tail; +} + +char Reader::read(bool move) { + fillBuffer(); + assert((head != tail) || (!move)); + if (head == tail) return 0; + char v = Buffer[head]; + if (move) { + if (isEOLN(v)) { + line++; + position = 1; + } else { + position++; + } + head = (head + 1) % bufferSize; + } + return v; +} + +// ---------------------------- Scanner's methods ----------------------------- + +Scanner::Scanner(const char* file, Lang _lang): lang(_lang) { + reader = new Reader(file); + end = endDefault; +} + +Scanner::Scanner(const char* file, void(*endf)(const char* msg, int line, int position), Lang _lang): lang(_lang) { + reader = new Reader(file); + end = endf; +} + +Scanner::Scanner(FILE* input, Lang _lang): lang(_lang) { + reader = new Reader(input); + end = endDefault; +} + +Scanner::Scanner(FILE* input, void(*endf)(const char* msg, int line, int position), Lang _lang): lang(_lang) { + reader = new Reader(input); + end = endf; +} + +Scanner::~Scanner() { + delete reader; +} + +void Scanner::error(const char* msg) { + int l = reader->getLine(); + int p = reader->getPosition(); + delete reader; + reader = NULL; + (*end)(msg, l, p); +} + +int Scanner::skipWhitespaces() { + int result = 0; + while (isWhitespace(reader->read())) { + reader->read(1); + result++; + } + return result; +} + + +int Scanner::skipWhitespacesUntilEOLN() { + int result = 0; + while (isWhitespace(reader->read()) && !isEOLN(reader->read())) { + reader->read(1); + result++; + } + return result; +} + + +// INTEGERS + +int Scanner::readInt(int min_value, int max_value) { + return (int)readLL(min_value, max_value); +} + +uint Scanner::readUInt(uint min_value, uint max_value) { + return (uint)readULL(min_value, max_value); +} + +inline bool lower_equal(ull a, bool sign_a, ull b, bool sign_b) { + if (sign_a != sign_b) return sign_a; + if (sign_a) return a >= b; + return a <= b; +} +inline ull spec_abs(ll x) { + if (x < 0) return (-(x + 1)) + 1; + return x; +} + +ll Scanner::readLL(ll min_value, ll max_value) { + assert(min_value <= max_value); + bool sign; + ull val; + readULL(max(spec_abs(min_value), spec_abs(max_value)), val, sign); + ll v = val; + if (!(lower_equal(spec_abs(min_value), min_value < 0, v, sign) && + lower_equal(v, sign, spec_abs(max_value), max_value < 0))) + error(msgOutOfRangeInt[lang]); + if (sign) v *= -1; + return v; +} + +ull Scanner::readULL(ull min_value, ull max_value) { + assert(min_value <= max_value); + bool sign; + ull val; + readULL(max_value, val, sign); + if (sign) error(msgOutOfRangeInt[lang]); + if (!(min_value <= val)) + error(msgOutOfRangeInt[lang]); + return val; +} + +// REAL NUMBERS + +float Scanner::readFloat(float min_value, float max_value) { + return (float)readLDouble(min_value, max_value); +} + +double Scanner::readDouble(double min_value, double max_value) { + return (double)readLDouble(min_value, max_value); +} + +long double Scanner::readLDouble(long double min_value, long double max_value) { + assert(min_value <= max_value); + bool sign; + ldb val; + readLDB(val, sign); + if (sign) val *= -1; + if (!(min_value <= val && val <= max_value)) + error(msgOutOfRangeReal[lang]); + return val; +} + +// STRINGS + +int Scanner::readString(char* s, int size) { + int x = 0; + while ( x < size - 1 && !isEOF() && !isWhitespace(reader->read())) + s[x++] = reader->read(1); + s[x]=0; + return x; +} + +int Scanner::readLine(char* s, int size) { + int x = 0; + while ( x < size - 1 && !isEOLN(reader->read()) && !isEOF()) + s[x++] = reader->read(1); + s[x] = 0; + if (isEOLN(reader->read())) reader->read(1); + return x; +} + +char Scanner::readChar() { + if (reader->isEOF()) error(msgNoChar[lang]); + return reader->read(1); +} + +// WHITESPACES + +void Scanner::readEof() { + if (!reader->isEOF()) error(msgNotEof[lang]); +} + +void Scanner::readEoln() { + if (!isEOLN(reader->read())) error(msgNotEoln[lang]); + reader->read(1); +} + +void Scanner::readEofOrEoln() { + if (isEOLN(reader->read())) { + reader->read(1); + } else if (!reader->isEOF()) { + error(msgNotEofOrEoln[lang]); + } +} + + +void Scanner::readSpace() { + if (!isSpace(reader->read())) error(msgNotSpace[lang]); + reader->read(1); +} + +void Scanner::readTab() { + if (!isTab(reader->read())) error(msgNotTab[lang]); + reader->read(1); +} + +bool Scanner::isEOF() { + return reader->isEOF(); +} + + +// PROTECTED + +void Scanner::readULL(ull limit, ull &val, bool &sign) { + sign = 0; + val = 0; + sign = isMinus(reader->read()); + if (sign) reader->read(1); + int zeros = 0; + int valDigits = 0; + while ('0' == reader->read()) { + zeros++; + valDigits++; + reader->read(1); + if (zeros > 1) error(msgLeadingZeros[lang]); + } + int limDigits = 0; + ull tmp = limit; + while (tmp) { + limDigits++; + tmp /= 10; + } + if (!limDigits) limDigits = 1; + while (isdigit(reader->read())) { + valDigits++; + if (valDigits > limDigits) error(msgOutOfRangeInt[lang]); + char x = reader->read(1); + if (valDigits == limDigits) { + if (limit / 10 < val) error(msgOutOfRangeInt[lang]); + if (limit / 10 == val && limit % 10 < (ull)(x - '0')) error(msgOutOfRangeInt[lang]); + } + val = val * 10 + x - '0'; + } + if (val > 0 && zeros) error(msgLeadingZeros[lang]); + if (sign && zeros) error(msgMinusZero[lang]); + if (!valDigits) error(msgNoNumber[lang]); +} + +void Scanner::readLDB(ldb &val, bool &sign) { + sign = 0; + val = 0; + sign = isMinus(reader->read()); + if (sign) reader->read(1); + int zeros = 0; + int valDigits = 0; + while ('0' == reader->read()) { + zeros++; + valDigits++; + reader->read(1); + if (zeros > 1) error(msgLeadingZeros[lang]); + } + if (zeros && isdigit(reader->read())) error(msgLeadingZeros[lang]); + while (isdigit(reader->read())) { + valDigits++; + char x = reader->read(1); + val = val * 10.0 + x - '0'; + } + if (!valDigits) error(msgNoNumber[lang]); + if (isDot(reader->read())) { + reader->read(1); + ldb dec = 1; + int dotDigits = 0; + while (isdigit(reader->read())) { + dotDigits++; + if (dotDigits > realNumbersLimit) error(msgRealNumberLimit[lang]); + char x = reader->read(1); + dec /= 10.0; + val += dec * (x - '0'); + } + if (!dotDigits) error(msgBadRealNumberFormat[lang]); + } +} + +} // namespace oi + +#endif // OI_LIB_OI_H_ diff --git a/src/sinol_make/commands/inwer/__init__.py b/src/sinol_make/commands/inwer/__init__.py index 2f942adf..edbeb588 100644 --- a/src/sinol_make/commands/inwer/__init__.py +++ b/src/sinol_make/commands/inwer/__init__.py @@ -84,7 +84,7 @@ def verify_and_print_table(self) -> Dict[str, TestResult]: :return: dictionary of TestResult objects """ results = {} - sorted_tests = sorted(self.tests, key=lambda x: x[0]) + sorted_tests = sorted(self.tests, key=lambda test: package_util.get_group(test, self.task_id)) executions: List[InwerExecution] = [] for test in sorted_tests: results[test] = TestResult(test, self.task_id)