diff --git a/judger/uoj_judger/include/testlib.h b/judger/uoj_judger/include/testlib.h index c4e0354..26117e7 100644 --- a/judger/uoj_judger/include/testlib.h +++ b/judger/uoj_judger/include/testlib.h @@ -15,17 +15,17 @@ * * Also read about wnext() to generate off-center random distribution. * - * See https://github.com/MikeMirzayanov/testlib/ to get latest version or bug tracker. + * See http://code.google.com/p/testlib/ to get latest version or bug tracker. */ #ifndef _TESTLIB_H_ #define _TESTLIB_H_ /* - * Copyright (c) 2005-2022 + * Copyright (c) 2005-2013 */ -#define VERSION "0.9.38-SNAPSHOT" +#define VERSION "0.9.5" /* * Mike Mirzayanov @@ -62,174 +62,129 @@ * writes output to program via stdout (use cout, printf, etc). */ -const char *latestFeatures[] = { - "For checker added --group and --testset command line params (like for validator), use checker.group() or checker.testset() to get values", - "Added quitpi(points_info, message) function to return with _points exit code 7 and given points_info", - "rnd.partition(size, sum[, min_part=1]) returns random (unsorted) partition which is a representation of the given `sum` as a sum of `size` positive integers (or >=min_part if specified)", - "rnd.distinct(size, n) and rnd.distinct(size, from, to)", - "opt(\"some_missing_key\") returns false now", - "has_opt(key)", - "Abort validator on validator.testset()/validator.group() if registered without using command line", - "Print integer range violations in a human readable way like `violates the range [1, 10^9]`", - "Opts supported: use them like n = opt(\"n\"), in a command line you can use an exponential notation", - "Reformatted", - "Use setTestCase(i) or unsetTestCase() to support test cases (you can use it in any type of program: generator, interactor, validator or checker)", - "Fixed issue #87: readStrictDouble accepts \"-0.00\"", - "Fixed issue #83: added InStream::quitif(condition, ...)", - "Fixed issue #79: fixed missed guard against repeated header include", - "Fixed issue #80: fixed UB in case of huge quitf message", - "Fixed issue #84: added readXs(size, indexBase = 1)", - "Fixed stringstream repeated usage issue", - "Fixed compilation in g++ (for std=c++03)", - "Batch of println functions (support collections, iterator ranges)", - "Introduced rnd.perm(size, first = 0) to generate a `first`-indexed permutation", - "Allow any whitespace in readInts-like functions for non-validators", - "Ignore 4+ command line arguments ifdef EJUDGE", - "Speed up of vtos", - "Show line number in validators in case of incorrect format", - "Truncate huge checker/validator/interactor message", - "Fixed issue with readTokenTo of very long tokens, now aborts with _pe/_fail depending of a stream type", - "Introduced InStream::ensure/ensuref checking a condition, returns wa/fail depending of a stream type", - "Fixed compilation in VS 2015+", - "Introduced space-separated read functions: readWords/readTokens, multilines read functions: readStrings/readLines", - "Introduced space-separated read functions: readInts/readIntegers/readLongs/readUnsignedLongs/readDoubles/readReals/readStrictDoubles/readStrictReals", - "Introduced split/tokenize functions to separate string by given char", - "Introduced InStream::readUnsignedLong and InStream::readLong with unsigned long long paramerters", - "Supported --testOverviewLogFileName for validator: bounds hits + features", - "Fixed UB (sequence points) in random_t", - "POINTS_EXIT_CODE returned back to 7 (instead of 0)", - "Removed disable buffers for interactive problems, because it works unexpectedly in wine", - "InStream over string: constructor of InStream from base InStream to inherit policies and std::string", - "Added expectedButFound quit function, examples: expectedButFound(_wa, 10, 20), expectedButFound(_fail, ja, pa, \"[n=%d,m=%d]\", n, m)", - "Fixed incorrect interval parsing in patterns", - "Use registerGen(argc, argv, 1) to develop new generator, use registerGen(argc, argv, 0) to compile old generators (originally created for testlib under 0.8.7)", - "Introduced disableFinalizeGuard() to switch off finalization checkings", - "Use join() functions to format a range of items as a single string (separated by spaces or other separators)", - "Use -DENABLE_UNEXPECTED_EOF to enable special exit code (by default, 8) in case of unexpected eof. It is good idea to use it in interactors", - "Use -DUSE_RND_AS_BEFORE_087 to compile in compatibility mode with random behavior of versions before 0.8.7", - "Fixed bug with nan in stringToDouble", - "Fixed issue around overloads for size_t on x64", - "Added attribute 'points' to the XML output in case of result=_points", - "Exit codes can be customized via macros, e.g. -DPE_EXIT_CODE=14", - "Introduced InStream function readWordTo/readTokenTo/readStringTo/readLineTo for faster reading", - "Introduced global functions: format(), englishEnding(), upperCase(), lowerCase(), compress()", - "Manual buffer in InStreams, some IO speed improvements", - "Introduced quitif(bool, const char* pattern, ...) which delegates to quitf() in case of first argument is true", - "Introduced guard against missed quitf() in checker or readEof() in validators", - "Supported readStrictReal/readStrictDouble - to use in validators to check strictly float numbers", - "Supported registerInteraction(argc, argv)", - "Print checker message to the stderr instead of stdout", - "Supported TResult _points to output calculated score, use quitp(...) functions", - "Fixed to be compilable on Mac", - "PC_BASE_EXIT_CODE=50 in case of defined TESTSYS", - "Fixed issues 19-21, added __attribute__ format printf", - "Some bug fixes", - "ouf.readInt(1, 100) and similar calls return WA", - "Modified random_t to avoid integer overflow", - "Truncated checker output [patch by Stepan Gatilov]", - "Renamed class random -> class random_t", - "Supported name parameter for read-and-validation methods, like readInt(1, 2, \"n\")", - "Fixed bug in readDouble()", - "Improved ensuref(), fixed nextLine to work in case of EOF, added startTest()", - "Supported \"partially correct\", example: quitf(_pc(13), \"result=%d\", result)", - "Added shuffle(begin, end), use it instead of random_shuffle(begin, end)", - "Added readLine(const string& ptrn), fixed the logic of readLine() in the validation mode", - "Package extended with samples of generators and validators", - "Written the documentation for classes and public methods in testlib.h", - "Implemented random routine to support generators, use registerGen() to switch it on", - "Implemented strict mode to validate tests, use registerValidation() to switch it on", - "Now ncmp.cpp and wcmp.cpp are return WA if answer is suffix or prefix of the output", - "Added InStream::readLong() and removed InStream::readLongint()", - "Now no footer added to each report by default (use directive FOOTER to switch on)", - "Now every checker has a name, use setName(const char* format, ...) to set it", - "Now it is compatible with TTS (by Kittens Computing)", - "Added \'ensure(condition, message = \"\")\' feature, it works like assert()", - "Fixed compatibility with MS C++ 7.1", - "Added footer with exit code information", - "Added compatibility with EJUDGE (compile with EJUDGE directive)", - "Added compatibility with Contester (compile with CONTESTER directive)" -}; +const char* latestFeatures[] = { + "Removed disable buffers for interactive problems, because it works unexpectedly in wine", + "InStream over string: constructor of InStream from base InStream to inherit policies and std::string", + "Added expectedButFound quit function, examples: expectedButFound(_wa, 10, 20), expectedButFound(_fail, ja, pa, \"[n=%d,m=%d]\", n, m)", + "Fixed incorrect interval parsing in patterns", + "Use registerGen(argc, argv, 1) to develop new generator, use registerGen(argc, argv, 0) to compile old generators (originally created for testlib under 0.8.7)", + "Introduced disableFinalizeGuard() to switch off finalization checkings", + "Use join() functions to format a range of items as a single string (separated by spaces or other separators)", + "Use -DENABLE_UNEXPECTED_EOF to enable special exit code (by default, 8) in case of unexpected eof. It is good idea to use it in interactors", + "Use -DUSE_RND_AS_BEFORE_087 to compile in compatibility mode with random behavior of versions before 0.8.7", + "Fixed bug with nan in stringToDouble", + "Fixed issue around overloads for size_t on x64", + "Added attribute 'points' to the XML output in case of result=_points", + "Exit codes can be customized via macros, e.g. -DPE_EXIT_CODE=14", + "Introduced InStream function readWordTo/readTokenTo/readStringTo/readLineTo for faster reading", + "Introduced global functions: format(), englishEnding(), upperCase(), lowerCase(), compress()", + "Manual buffer in InStreams, some IO speed improvements", + "Introduced quitif(bool, const char* pattern, ...) which delegates to quitf() in case of first argument is true", + "Introduced guard against missed quitf() in checker or readEof() in validators", + "Supported readStrictReal/readStrictDouble - to use in validators to check strictly float numbers", + "Supported registerInteraction(argc, argv)", + "Print checker message to the stderr instead of stdout", + "Supported TResult _points to output calculated score, use quitp(...) functions", + "Fixed to be compilable on Mac", + "PC_BASE_EXIT_CODE=50 in case of defined TESTSYS", + "Fixed issues 19-21, added __attribute__ format printf", + "Some bug fixes", + "ouf.readInt(1, 100) and similar calls return WA", + "Modified random_t to avoid integer overflow", + "Truncated checker output [patch by Stepan Gatilov]", + "Renamed class random -> class random_t", + "Supported name parameter for read-and-validation methods, like readInt(1, 2, \"n\")", + "Fixed bug in readDouble()", + "Improved ensuref(), fixed nextLine to work in case of EOF, added startTest()", + "Supported \"partially correct\", example: quitf(_pc(13), \"result=%d\", result)", + "Added shuffle(begin, end), use it instead of random_shuffle(begin, end)", + "Added readLine(const string& ptrn), fixed the logic of readLine() in the validation mode", + "Package extended with samples of generators and validators", + "Written the documentation for classes and public methods in testlib.h", + "Implemented random routine to support generators, use registerGen() to switch it on", + "Implemented strict mode to validate tests, use registerValidation() to switch it on", + "Now ncmp.cpp and wcmp.cpp are return WA if answer is suffix or prefix of the output", + "Added InStream::readLong() and removed InStream::readLongint()", + "Now no footer added to each report by default (use directive FOOTER to switch on)", + "Now every checker has a name, use setName(const char* format, ...) to set it", + "Now it is compatible with TTS (by Kittens Computing)", + "Added \'ensure(condition, message = \"\")\' feature, it works like assert()", + "Fixed compatibility with MS C++ 7.1", + "Added footer with exit code information", + "Added compatibility with EJUDGE (compile with EJUDGE directive)" + }; #ifdef _MSC_VER #define _CRT_SECURE_NO_DEPRECATE #define _CRT_SECURE_NO_WARNINGS -#define _CRT_NO_VA_START_VALIDATION #endif /* Overrides random() for Borland C++. */ +#ifdef __EMSCRIPTEN__ +#define srand __srand_deprecated +#define rand __rand_deprecated +#endif #define random __random_deprecated #include #include #include #include #undef random +#ifdef __EMSCRIPTEN__ +#undef rand +#undef srand +#endif #include #include #include #include -#include -#include #include -#include #include #include #include #include #include + #include -#if (_WIN32 || __WIN32__ || _WIN64 || __WIN64__ || __CYGWIN__) -# if !defined(_MSC_VER) || _MSC_VER > 1400 -# define NOMINMAX 1 +#ifdef __EMSCRIPTEN__ +#include +#endif + +#if ( _WIN32 || __WIN32__ || _WIN64 || __WIN64__ ) +# if !defined(_MSC_VER) || _MSC_VER>1400 # include # else # define WORD unsigned short -# include # endif # include # define ON_WINDOWS -# if defined(_MSC_VER) && _MSC_VER > 1400 -# pragma warning( disable : 4127 ) -# pragma warning( disable : 4146 ) -# pragma warning( disable : 4458 ) -# endif #else # define WORD unsigned short -# include #endif -#if defined(FOR_WINDOWS) && defined(FOR_LINUX) -#error Only one target system is allowed +#ifdef linux +#include #endif #ifndef LLONG_MIN #define LLONG_MIN (-9223372036854775807LL - 1) #endif -#ifndef ULLONG_MAX -#define ULLONG_MAX (18446744073709551615) -#endif - #define LF ((char)10) #define CR ((char)13) #define TAB ((char)9) #define SPACE ((char)' ') -#define EOFC (255) +#define EOFC ((char)26) #ifndef OK_EXIT_CODE -# ifdef CONTESTER -# define OK_EXIT_CODE 0xAC -# else -# define OK_EXIT_CODE 0 -# endif +# define OK_EXIT_CODE 0 #endif #ifndef WA_EXIT_CODE # ifdef EJUDGE # define WA_EXIT_CODE 5 -# elif defined(CONTESTER) -# define WA_EXIT_CODE 0xAB # else # define WA_EXIT_CODE 1 # endif @@ -238,8 +193,6 @@ const char *latestFeatures[] = { #ifndef PE_EXIT_CODE # ifdef EJUDGE # define PE_EXIT_CODE 4 -# elif defined(CONTESTER) -# define PE_EXIT_CODE 0xAA # else # define PE_EXIT_CODE 2 # endif @@ -248,8 +201,6 @@ const char *latestFeatures[] = { #ifndef FAIL_EXIT_CODE # ifdef EJUDGE # define FAIL_EXIT_CODE 6 -# elif defined(CONTESTER) -# define FAIL_EXIT_CODE 0xA3 # else # define FAIL_EXIT_CODE 3 # endif @@ -264,7 +215,7 @@ const char *latestFeatures[] = { #endif #ifndef POINTS_EXIT_CODE -# define POINTS_EXIT_CODE 7 +# define POINTS_EXIT_CODE 0 #endif #ifndef UNEXPECTED_EOF_EXIT_CODE @@ -301,7 +252,7 @@ const char *latestFeatures[] = { # define NORETURN #endif -static char __testlib_format_buffer[16777216]; +static char __testlib_format_buffer[1024]; static int __testlib_format_buffer_usage_count = 0; #define FMT_TO_RESULT(fmt, cstr, result) std::string result; \ @@ -310,45 +261,35 @@ static int __testlib_format_buffer_usage_count = 0; __testlib_format_buffer_usage_count++; \ va_list ap; \ va_start(ap, fmt); \ - vsnprintf(__testlib_format_buffer, sizeof(__testlib_format_buffer), cstr, ap); \ + std::vsprintf(__testlib_format_buffer, cstr, ap); \ va_end(ap); \ - __testlib_format_buffer[sizeof(__testlib_format_buffer) - 1] = 0; \ result = std::string(__testlib_format_buffer); \ __testlib_format_buffer_usage_count--; \ const long long __TESTLIB_LONGLONG_MAX = 9223372036854775807LL; -bool __testlib_hasTestCase; -int __testlib_testCase = -1; - -void setTestCase(int testCase) { - __testlib_hasTestCase = true; - __testlib_testCase = testCase; -} - -void unsetTestCase() { - __testlib_hasTestCase = false; - __testlib_testCase = -1; -} - -NORETURN static void __testlib_fail(const std::string &message); +NORETURN static void __testlib_fail(const std::string& message); template -static inline T __testlib_abs(const T &x) { +static inline T __testlib_abs(const T& x) +{ return x > 0 ? x : -x; } template -static inline T __testlib_min(const T &a, const T &b) { +static inline T __testlib_min(const T& a, const T& b) +{ return a < b ? a : b; } template -static inline T __testlib_max(const T &a, const T &b) { +static inline T __testlib_max(const T& a, const T& b) +{ return a > b ? a : b; } -static bool __testlib_prelimIsNaN(double r) { +static bool __testlib_prelimIsNaN(double r) +{ volatile double ra = r; #ifndef __BORLANDC__ return ((ra != ra) == true) && ((ra == ra) == false) && ((1.0 > ra) == false) && ((1.0 < ra) == false); @@ -357,7 +298,8 @@ static bool __testlib_prelimIsNaN(double r) { #endif } -static std::string removeDoubleTrailingZeroes(std::string value) { +static std::string removeDoubleTrailingZeroes(std::string value) +{ while (!value.empty() && value[value.length() - 1] == '0' && value.find('.') != std::string::npos) value = value.substr(0, value.length() - 1); return value + '0'; @@ -366,106 +308,77 @@ static std::string removeDoubleTrailingZeroes(std::string value) { #ifdef __GNUC__ __attribute__ ((format (printf, 1, 2))) #endif -std::string format(const char *fmt, ...) { +std::string format(const char* fmt, ...) +{ FMT_TO_RESULT(fmt, fmt, result); return result; } -std::string format(const std::string fmt, ...) { +std::string format(const std::string& fmt, ...) +{ FMT_TO_RESULT(fmt, fmt.c_str(), result); return result; } -static std::string __testlib_part(const std::string &s); +static std::string __testlib_part(const std::string& s); -static bool __testlib_isNaN(double r) { +static bool __testlib_isNaN(double r) +{ __TESTLIB_STATIC_ASSERT(sizeof(double) == sizeof(long long)); volatile double ra = r; long long llr1, llr2; - std::memcpy((void *) &llr1, (void *) &ra, sizeof(double)); + std::memcpy((void*)&llr1, (void*)&ra, sizeof(double)); ra = -ra; - std::memcpy((void *) &llr2, (void *) &ra, sizeof(double)); - long long llnan = 0xFFF8000000000000LL; + std::memcpy((void*)&llr2, (void*)&ra, sizeof(double)); + long long llnan = 0xFFF8000000000000ll; return __testlib_prelimIsNaN(r) || llnan == llr1 || llnan == llr2; } -static double __testlib_nan() { +static double __testlib_nan() +{ __TESTLIB_STATIC_ASSERT(sizeof(double) == sizeof(long long)); -#ifndef NAN - long long llnan = 0xFFF8000000000000LL; + long long llnan = 0xFFF8000000000000ll; double nan; std::memcpy(&nan, &llnan, sizeof(double)); return nan; -#else - return NAN; -#endif } -static bool __testlib_isInfinite(double r) { +static bool __testlib_isInfinite(double r) +{ volatile double ra = r; - return (ra > 1E300 || ra < -1E300); + return (ra > 1E100 || ra < -1E100); } -#ifdef __GNUC__ -__attribute__((const)) -#endif -inline bool doubleCompare(double expected, double result, double MAX_DOUBLE_ERROR) { - MAX_DOUBLE_ERROR += 1E-15; - if (__testlib_isNaN(expected)) { - return __testlib_isNaN(result); - } else if (__testlib_isInfinite(expected)) { - if (expected > 0) { - return result > 0 && __testlib_isInfinite(result); - } else { - return result < 0 && __testlib_isInfinite(result); - } - } else if (__testlib_isNaN(result) || __testlib_isInfinite(result)) { - return false; - } else if (__testlib_abs(result - expected) <= MAX_DOUBLE_ERROR) { - return true; - } else { - double minv = __testlib_min(expected * (1.0 - MAX_DOUBLE_ERROR), - expected * (1.0 + MAX_DOUBLE_ERROR)); - double maxv = __testlib_max(expected * (1.0 - MAX_DOUBLE_ERROR), - expected * (1.0 + MAX_DOUBLE_ERROR)); - return result >= minv && result <= maxv; - } -} - -#ifdef __GNUC__ -__attribute__((const)) -#endif -inline double doubleDelta(double expected, double result) { - double absolute = __testlib_abs(result - expected); - - if (__testlib_abs(expected) > 1E-9) { - double relative = __testlib_abs(absolute / expected); - return __testlib_min(absolute, relative); - } else - return absolute; -} - -static void __testlib_set_binary(std::FILE *file) { - if (NULL != file) { +static void __testlib_set_binary(std::FILE* file) +{ #ifdef O_BINARY -# ifdef _MSC_VER + if (NULL != file) + { +#ifndef __BORLANDC__ _setmode(_fileno(file), O_BINARY); -# elseif - setmode(fileno(file), O_BINARY); -# endif #else - if (file == stdin) - freopen(NULL, "rb", file); - if (file == stdout || file == stderr) - freopen(NULL, "wb", file); + setmode(fileno(file), O_BINARY); #endif } +#endif } -#if __cplusplus > 199711L || defined(_MSC_VER) -/* opts */ -void prepareOpts(int argc, char* argv[]); +static FILE *__testlib_fopen(const char *name, const char *mode) +{ +#ifdef __EMSCRIPTEN__ + EM_ASM( + try { + FS.stat('/cwd'); + } catch (e) { + FS.mkdir('/cwd'); + FS.mount(NODEFS, { root: '.' }, '/cwd'); + } + ); + return fopen((std::string("/cwd/") + name).c_str(), mode); +#else + return fopen(name, mode); #endif +} /* * Very simple regex-like pattern. @@ -497,29 +410,22 @@ void prepareOpts(int argc, char* argv[]); * new string by pattern. * * Simpler way to read token and check it for pattern matching is "inf.readToken("[a-z]+")". - * - * All spaces are ignored in regex, unless escaped with \. For example, ouf.readLine("NO SOLUTION") - * will expect "NOSOLUTION", the correct call should be ouf.readLine("NO\\ SOLUTION") or - * ouf.readLine(R"(NO\ SOLUTION)") if you prefer raw string literals from C++11. */ class random_t; -class pattern { +class pattern +{ public: /* Create pattern instance by string. */ pattern(std::string s); - /* Generate new string by pattern and given random_t. */ - std::string next(random_t &rnd) const; - + std::string next(random_t& rnd) const; /* Checks if given string match the pattern. */ - bool matches(const std::string &s) const; - + bool matches(const std::string& s) const; /* Returns source string of the pattern. */ std::string src() const; - private: - bool matches(const std::string &s, size_t pos) const; + bool matches(const std::string& s, size_t pos) const; std::string s; std::vector children; @@ -540,7 +446,8 @@ private: * Random generates uniformly distributed values if another strategy is * not specified explicitly. */ -class random_t { +class random_t +{ private: unsigned long long seed; static const unsigned long long multiplier; @@ -548,20 +455,20 @@ private: static const unsigned long long mask; static const int lim; - long long nextBits(int bits) { - if (bits <= 48) { + long long nextBits(int bits) + { + if (bits <= 48) + { seed = (seed * multiplier + addend) & mask; - return (long long) (seed >> (48 - bits)); - } else { + return (long long)(seed >> (48 - bits)); + } + else + { if (bits > 63) __testlib_fail("random_t::nextBits(int bits): n must be less than 64"); int lowerBitCount = (random_t::version == 0 ? 31 : 32); - - long long left = (nextBits(31) << 32); - long long right = nextBits(lowerBitCount); - - return left ^ right; + return ((nextBits(31) << 32) ^ nextBits(lowerBitCount)); } } @@ -570,38 +477,41 @@ public: /* New random_t with fixed seed. */ random_t() - : seed(3905348978240129619LL) { + : seed(3905348978240129619LL) + { } /* Sets seed by command line. */ - void setSeed(int argc, char *argv[]) { + void setSeed(int argc, char* argv[]) + { random_t p; seed = 3905348978240129619LL; - for (int i = 1; i < argc; i++) { + for (int i = 1; i < argc; i++) + { std::size_t le = std::strlen(argv[i]); for (std::size_t j = 0; j < le; j++) - seed = seed * multiplier + (unsigned int) (argv[i][j]) + addend; + seed = seed * multiplier + (unsigned int)(argv[i][j]) + addend; seed += multiplier / addend; } seed = seed & mask; } - /* Sets seed by given value. */ - void setSeed(long long _seed) { + /* Sets seed by given value. */ + void setSeed(long long _seed) + { _seed = (_seed ^ multiplier) & mask; seed = _seed; } #ifndef __BORLANDC__ - /* Random string value by given pattern (see pattern documentation). */ - std::string next(const std::string &ptrn) { + std::string next(const std::string& ptrn) + { pattern p(ptrn); return p.next(*this); } - #else /* Random string value by given pattern (see pattern documentation). */ std::string next(std::string ptrn) @@ -612,15 +522,16 @@ public: #endif /* Random value in range [0, n-1]. */ - int next(int n) { + int next(int n) + { if (n <= 0) __testlib_fail("random_t::next(int n): n must be positive"); if ((n & -n) == n) // n is a power of 2 - return (int) ((n * (long long) nextBits(31)) >> 31); + return (int)((n * (long long)nextBits(31)) >> 31); const long long limit = INT_MAX / n * n; - + long long bits; do { bits = nextBits(31); @@ -630,19 +541,21 @@ public: } /* Random value in range [0, n-1]. */ - unsigned int next(unsigned int n) { + unsigned int next(unsigned int n) + { if (n >= INT_MAX) __testlib_fail("random_t::next(unsigned int n): n must be less INT_MAX"); - return (unsigned int) next(int(n)); + return (unsigned int)next(int(n)); } /* Random value in range [0, n-1]. */ - long long next(long long n) { + long long next(long long n) + { if (n <= 0) __testlib_fail("random_t::next(long long n): n must be positive"); const long long limit = __TESTLIB_LONGLONG_MAX / n * n; - + long long bits; do { bits = nextBits(63); @@ -652,80 +565,89 @@ public: } /* Random value in range [0, n-1]. */ - unsigned long long next(unsigned long long n) { - if (n >= (unsigned long long) (__TESTLIB_LONGLONG_MAX)) + unsigned long long next(unsigned long long n) + { + if (n >= (unsigned long long)(__TESTLIB_LONGLONG_MAX)) __testlib_fail("random_t::next(unsigned long long n): n must be less LONGLONG_MAX"); - return (unsigned long long) next((long long) (n)); + return (unsigned long long)next((long long)(n)); } /* Random value in range [0, n-1]. */ - long next(long n) { - return (long) next((long long) (n)); + long next(long n) + { + return (long)next((long long)(n)); } /* Random value in range [0, n-1]. */ - unsigned long next(unsigned long n) { - if (n >= (unsigned long) (LONG_MAX)) + unsigned long next(unsigned long n) + { + if (n >= (unsigned long)(LONG_MAX)) __testlib_fail("random_t::next(unsigned long n): n must be less LONG_MAX"); - return (unsigned long) next((unsigned long long) (n)); + return (unsigned long)next((unsigned long long)(n)); } /* Returns random value in range [from,to]. */ - int next(int from, int to) { - return int(next((long long) to - from + 1) + from); + int next(int from, int to) + { + return int(next((long long)to - from + 1) + from); } /* Returns random value in range [from,to]. */ - unsigned int next(unsigned int from, unsigned int to) { - return (unsigned int) (next((long long) to - from + 1) + from); + unsigned int next(unsigned int from, unsigned int to) + { + return (unsigned int)(next((long long)to - from + 1) + from); } /* Returns random value in range [from,to]. */ - long long next(long long from, long long to) { + long long next(long long from, long long to) + { return next(to - from + 1) + from; } /* Returns random value in range [from,to]. */ - unsigned long long next(unsigned long long from, unsigned long long to) { + unsigned long long next(unsigned long long from, unsigned long long to) + { if (from > to) __testlib_fail("random_t::next(unsigned long long from, unsigned long long to): from can't not exceed to"); return next(to - from + 1) + from; } /* Returns random value in range [from,to]. */ - long next(long from, long to) { + long next(long from, long to) + { return next(to - from + 1) + from; } /* Returns random value in range [from,to]. */ - unsigned long next(unsigned long from, unsigned long to) { + unsigned long next(unsigned long from, unsigned long to) + { if (from > to) __testlib_fail("random_t::next(unsigned long from, unsigned long to): from can't not exceed to"); return next(to - from + 1) + from; } /* Random double value in range [0, 1). */ - double next() { - long long left = ((long long) (nextBits(26)) << 27); - long long right = nextBits(27); - return (double) (left + right) / (double) (1LL << 53); + double next() + { + return (double)(((long long)(nextBits(26)) << 27) + nextBits(27)) / (double)(1LL << 53); } /* Random double value in range [0, n). */ - double next(double n) { + double next(double n) + { return n * next(); } /* Random double value in range [from, to). */ - double next(double from, double to) { - if (from > to) - __testlib_fail("random_t::next(double from, double to): from can't not exceed to"); + double next(double from, double to) + { return next(to - from) + from; } /* Returns random element from container. */ - template - typename Container::value_type any(const Container &c) { + template + typename Container::value_type any(const Container& c) + { size_t size = c.size(); if (size <= 0) __testlib_fail("random_t::any(const Container& c): c.size() must be positive"); @@ -733,8 +655,9 @@ public: } /* Returns random element from iterator range. */ - template - typename Iter::value_type any(const Iter &begin, const Iter &end) { + template + typename Iter::value_type any(const Iter& begin, const Iter& end) + { int size = int(end - begin); if (size <= 0) __testlib_fail("random_t::any(const Iter& begin, const Iter& end): range must have positive length"); @@ -745,7 +668,8 @@ public: #ifdef __GNUC__ __attribute__ ((format (printf, 2, 3))) #endif - std::string next(const char *format, ...) { + std::string next(const char* format, ...) + { FMT_TO_RESULT(format, format, ptrn); return next(ptrn); } @@ -758,23 +682,27 @@ public: * * If type < 0, than "max" function replaces with "min". */ - int wnext(int n, int type) { + int wnext(int n, int type) + { if (n <= 0) __testlib_fail("random_t::wnext(int n, int type): n must be positive"); - - if (abs(type) < random_t::lim) { + + if (abs(type) < random_t::lim) + { int result = next(n); for (int i = 0; i < +type; i++) result = __testlib_max(result, next(n)); - + for (int i = 0; i < -type; i++) result = __testlib_min(result, next(n)); return result; - } else { + } + else + { double p; - + if (type > 0) p = std::pow(next() + 0.0, 1.0 / (type + 1)); else @@ -783,166 +711,179 @@ public: return int(n * p); } } - + /* See wnext(int, int). It uses the same algorithms. */ - long long wnext(long long n, int type) { + long long wnext(long long n, int type) + { if (n <= 0) __testlib_fail("random_t::wnext(long long n, int type): n must be positive"); - - if (abs(type) < random_t::lim) { + + if (abs(type) < random_t::lim) + { long long result = next(n); for (int i = 0; i < +type; i++) result = __testlib_max(result, next(n)); - + for (int i = 0; i < -type; i++) result = __testlib_min(result, next(n)); return result; - } else { + } + else + { double p; - + if (type > 0) p = std::pow(next() + 0.0, 1.0 / (type + 1)); else - p = std::pow(next() + 0.0, -type + 1); + p = std::pow(next() + 0.0, - type + 1); - return __testlib_min(__testlib_max((long long) (double(n) * p), 0LL), n - 1LL); + return __testlib_min(__testlib_max((long long)(double(n) * p), 0LL), n - 1LL); } } - + /* See wnext(int, int). It uses the same algorithms. */ - double wnext(int type) { - if (abs(type) < random_t::lim) { + double wnext(int type) + { + if (abs(type) < random_t::lim) + { double result = next(); for (int i = 0; i < +type; i++) result = __testlib_max(result, next()); - + for (int i = 0; i < -type; i++) result = __testlib_min(result, next()); return result; - } else { + } + else + { double p; - + if (type > 0) p = std::pow(next() + 0.0, 1.0 / (type + 1)); else - p = std::pow(next() + 0.0, -type + 1); + p = std::pow(next() + 0.0, - type + 1); return p; } } - + /* See wnext(int, int). It uses the same algorithms. */ - double wnext(double n, int type) { + double wnext(double n, int type) + { if (n <= 0) __testlib_fail("random_t::wnext(double n, int type): n must be positive"); - if (abs(type) < random_t::lim) { + if (abs(type) < random_t::lim) + { double result = next(); for (int i = 0; i < +type; i++) result = __testlib_max(result, next()); - + for (int i = 0; i < -type; i++) result = __testlib_min(result, next()); return n * result; - } else { + } + else + { double p; - + if (type > 0) p = std::pow(next() + 0.0, 1.0 / (type + 1)); else - p = std::pow(next() + 0.0, -type + 1); + p = std::pow(next() + 0.0, - type + 1); return n * p; } } /* See wnext(int, int). It uses the same algorithms. */ - unsigned int wnext(unsigned int n, int type) { + unsigned int wnext(unsigned int n, int type) + { if (n >= INT_MAX) __testlib_fail("random_t::wnext(unsigned int n, int type): n must be less INT_MAX"); - return (unsigned int) wnext(int(n), type); + return (unsigned int)wnext(int(n), type); } - + /* See wnext(int, int). It uses the same algorithms. */ - unsigned long long wnext(unsigned long long n, int type) { - if (n >= (unsigned long long) (__TESTLIB_LONGLONG_MAX)) + unsigned long long wnext(unsigned long long n, int type) + { + if (n >= (unsigned long long)(__TESTLIB_LONGLONG_MAX)) __testlib_fail("random_t::wnext(unsigned long long n, int type): n must be less LONGLONG_MAX"); - return (unsigned long long) wnext((long long) (n), type); + return (unsigned long long)wnext((long long)(n), type); } /* See wnext(int, int). It uses the same algorithms. */ - long wnext(long n, int type) { - return (long) wnext((long long) (n), type); + long wnext(long n, int type) + { + return (long)wnext((long long)(n), type); } - + /* See wnext(int, int). It uses the same algorithms. */ - unsigned long wnext(unsigned long n, int type) { - if (n >= (unsigned long) (LONG_MAX)) + unsigned long wnext(unsigned long n, int type) + { + if (n >= (unsigned long)(LONG_MAX)) __testlib_fail("random_t::wnext(unsigned long n, int type): n must be less LONG_MAX"); - return (unsigned long) wnext((unsigned long long) (n), type); + return (unsigned long)wnext((unsigned long long)(n), type); } /* Returns weighted random value in range [from, to]. */ - int wnext(int from, int to, int type) { - if (from > to) - __testlib_fail("random_t::wnext(int from, int to, int type): from can't not exceed to"); + int wnext(int from, int to, int type) + { return wnext(to - from + 1, type) + from; } - + /* Returns weighted random value in range [from, to]. */ - int wnext(unsigned int from, unsigned int to, int type) { - if (from > to) - __testlib_fail("random_t::wnext(unsigned int from, unsigned int to, int type): from can't not exceed to"); + int wnext(unsigned int from, unsigned int to, int type) + { return int(wnext(to - from + 1, type) + from); } - + /* Returns weighted random value in range [from, to]. */ - long long wnext(long long from, long long to, int type) { - if (from > to) - __testlib_fail("random_t::wnext(long long from, long long to, int type): from can't not exceed to"); + long long wnext(long long from, long long to, int type) + { return wnext(to - from + 1, type) + from; } - + /* Returns weighted random value in range [from, to]. */ - unsigned long long wnext(unsigned long long from, unsigned long long to, int type) { + unsigned long long wnext(unsigned long long from, unsigned long long to, int type) + { if (from > to) - __testlib_fail( - "random_t::wnext(unsigned long long from, unsigned long long to, int type): from can't not exceed to"); + __testlib_fail("random_t::wnext(unsigned long long from, unsigned long long to, int type): from can't not exceed to"); return wnext(to - from + 1, type) + from; } - + /* Returns weighted random value in range [from, to]. */ - long wnext(long from, long to, int type) { - if (from > to) - __testlib_fail("random_t::wnext(long from, long to, int type): from can't not exceed to"); + long wnext(long from, long to, int type) + { return wnext(to - from + 1, type) + from; } - + /* Returns weighted random value in range [from, to]. */ - unsigned long wnext(unsigned long from, unsigned long to, int type) { + unsigned long wnext(unsigned long from, unsigned long to, int type) + { if (from > to) __testlib_fail("random_t::wnext(unsigned long from, unsigned long to, int type): from can't not exceed to"); return wnext(to - from + 1, type) + from; } - + /* Returns weighted random double value in range [from, to). */ - double wnext(double from, double to, int type) { - if (from > to) - __testlib_fail("random_t::wnext(double from, double to, int type): from can't not exceed to"); + double wnext(double from, double to, int type) + { return wnext(to - from, type) + from; } /* Returns weighted random element from container. */ - template - typename Container::value_type wany(const Container &c, int type) { + template + typename Container::value_type wany(const Container& c, int type) + { size_t size = c.size(); if (size <= 0) __testlib_fail("random_t::wany(const Container& c, int type): c.size() must be positive"); @@ -950,139 +891,14 @@ public: } /* Returns weighted random element from iterator range. */ - template - typename Iter::value_type wany(const Iter &begin, const Iter &end, int type) { + template + typename Iter::value_type wany(const Iter& begin, const Iter& end, int type) + { int size = int(end - begin); if (size <= 0) - __testlib_fail( - "random_t::any(const Iter& begin, const Iter& end, int type): range must have positive length"); + __testlib_fail("random_t::any(const Iter& begin, const Iter& end, int type): range must have positive length"); return *(begin + wnext(size, type)); } - - /* Returns random permutation of the given size (values are between `first` and `first`+size-1)*/ - template - std::vector perm(T size, E first) { - if (size <= 0) - __testlib_fail("random_t::perm(T size, E first = 0): size must be positive"); - std::vector p(size); - E current = first; - for (T i = 0; i < size; i++) - p[i] = current++; - if (size > 1) - for (T i = 1; i < size; i++) - std::swap(p[i], p[next(i + 1)]); - return p; - } - - /* Returns random permutation of the given size (values are between 0 and size-1)*/ - template - std::vector perm(T size) { - return perm(size, T(0)); - } - - /* Returns `size` unordered (unsorted) distinct numbers between `from` and `to`. */ - template - std::vector distinct(int size, T from, T to) { - std::vector result; - if (size == 0) - return result; - - if (from > to) - __testlib_fail("random_t::distinct expected from <= to"); - - if (size < 0) - __testlib_fail("random_t::distinct expected size >= 0"); - - uint64_t n = to - from + 1; - if (uint64_t(size) > n) - __testlib_fail("random_t::distinct expected size <= to - from + 1"); - - double expected = 0.0; - for (int i = 1; i <= size; i++) - expected += double(n) / double(n - i + 1); - - if (expected < double(n)) { - std::set vals; - while (int(vals.size()) < size) { - T x = T(next(from, to)); - if (vals.insert(x).second) - result.push_back(x); - } - } else { - if (n > 1000000000) - __testlib_fail("random_t::distinct here expected to - from + 1 <= 1000000000"); - std::vector p(perm(int(n), from)); - result.insert(result.end(), p.begin(), p.begin() + size); - } - - return result; - } - - /* Returns `size` unordered (unsorted) distinct numbers between `0` and `upper`-1. */ - template - std::vector distinct(int size, T upper) { - if (size < 0) - __testlib_fail("random_t::distinct expected size >= 0"); - if (size == 0) - return std::vector(); - - if (upper <= 0) - __testlib_fail("random_t::distinct expected upper > 0"); - if (size > upper) - __testlib_fail("random_t::distinct expected size <= upper"); - - return distinct(size, T(0), upper - 1); - } - - /* Returns random (unsorted) partition which is a representation of sum as a sum of integers not less than min_part. */ - template - std::vector partition(int size, T sum, T min_part) { - if (size < 0) - __testlib_fail("random_t::partition: size < 0"); - if (size == 0 && sum != 0) - __testlib_fail("random_t::partition: size == 0 && sum != 0"); - if (min_part * size > sum) - __testlib_fail("random_t::partition: min_part * size > sum"); - if (size == 0 && sum == 0) - return std::vector(); - - T sum_ = sum; - sum -= min_part * size; - - std::vector septums(size); - std::vector d = distinct(size - 1, T(1), T(sum + size - 1)); - for (int i = 0; i + 1 < size; i++) - septums[i + 1] = d[i]; - sort(septums.begin(), septums.end()); - - std::vector result(size); - for (int i = 0; i + 1 < size; i++) - result[i] = septums[i + 1] - septums[i] - 1; - result[size - 1] = sum + size - 1 - septums.back(); - - for (std::size_t i = 0; i < result.size(); i++) - result[i] += min_part; - - T result_sum = 0; - for (std::size_t i = 0; i < result.size(); i++) - result_sum += result[i]; - if (result_sum != sum_) - __testlib_fail("random_t::partition: partition sum is expected to be the given sum"); - - if (*std::min_element(result.begin(), result.end()) < min_part) - __testlib_fail("random_t::partition: partition min is expected to be no less than the given min_part"); - - if (int(result.size()) != size || result.size() != (size_t) size) - __testlib_fail("random_t::partition: partition size is expected to be equal to the given size"); - - return result; - } - - /* Returns random (unsorted) partition which is a representation of sum as a sum of positive integers. */ - template - std::vector partition(int size, T sum) { - return partition(size, sum, T(1)); - } }; const int random_t::lim = 25; @@ -1092,18 +908,21 @@ const unsigned long long random_t::mask = (1LL << 48) - 1; int random_t::version = -1; /* Pattern implementation */ -bool pattern::matches(const std::string &s) const { +bool pattern::matches(const std::string& s) const +{ return matches(s, 0); } -static bool __pattern_isSlash(const std::string &s, size_t pos) { +static bool __pattern_isSlash(const std::string& s, size_t pos) +{ return s[pos] == '\\'; } #ifdef __GNUC__ __attribute__((pure)) #endif -static bool __pattern_isCommandChar(const std::string &s, size_t pos, char value) { +static bool __pattern_isCommandChar(const std::string& s, size_t pos, char value) +{ if (pos >= s.length()) return false; @@ -1116,7 +935,8 @@ static bool __pattern_isCommandChar(const std::string &s, size_t pos, char value return slashes % 2 == 0 && s[pos] == value; } -static char __pattern_getChar(const std::string &s, size_t &pos) { +static char __pattern_getChar(const std::string& s, size_t& pos) +{ if (__pattern_isSlash(s, pos)) pos += 2; else @@ -1128,10 +948,12 @@ static char __pattern_getChar(const std::string &s, size_t &pos) { #ifdef __GNUC__ __attribute__((pure)) #endif -static int __pattern_greedyMatch(const std::string &s, size_t pos, const std::vector chars) { +static int __pattern_greedyMatch(const std::string& s, size_t pos, const std::vector chars) +{ int result = 0; - while (pos < s.length()) { + while (pos < s.length()) + { char c = s[pos++]; if (!std::binary_search(chars.begin(), chars.end(), c)) break; @@ -1142,14 +964,17 @@ static int __pattern_greedyMatch(const std::string &s, size_t pos, const std::ve return result; } -std::string pattern::src() const { +std::string pattern::src() const +{ return s; } -bool pattern::matches(const std::string &s, size_t pos) const { +bool pattern::matches(const std::string& s, size_t pos) const +{ std::string result; - if (to > 0) { + if (to > 0) + { int size = __pattern_greedyMatch(s, pos, chars); if (size < from) return false; @@ -1158,29 +983,34 @@ bool pattern::matches(const std::string &s, size_t pos) const { pos += size; } - if (children.size() > 0) { + if (children.size() > 0) + { for (size_t child = 0; child < children.size(); child++) if (children[child].matches(s, pos)) return true; return false; - } else + } + else return pos == s.length(); } -std::string pattern::next(random_t &rnd) const { +std::string pattern::next(random_t& rnd) const +{ std::string result; result.reserve(20); if (to == INT_MAX) __testlib_fail("pattern::next(random_t& rnd): can't process character '*' for generation"); - if (to > 0) { + if (to > 0) + { int count = rnd.next(to - from + 1) + from; for (int i = 0; i < count; i++) result += chars[rnd.next(int(chars.size()))]; } - if (children.size() > 0) { + if (children.size() > 0) + { int child = rnd.next(int(children.size())); result += children[child].next(rnd); } @@ -1188,19 +1018,23 @@ std::string pattern::next(random_t &rnd) const { return result; } -static void __pattern_scanCounts(const std::string &s, size_t &pos, int &from, int &to) { - if (pos >= s.length()) { +static void __pattern_scanCounts(const std::string& s, size_t& pos, int& from, int& to) +{ + if (pos >= s.length()) + { from = to = 1; return; } - - if (__pattern_isCommandChar(s, pos, '{')) { + + if (__pattern_isCommandChar(s, pos, '{')) + { std::vector parts; std::string part; pos++; - while (pos < s.length() && !__pattern_isCommandChar(s, pos, '}')) { + while (pos < s.length() && !__pattern_isCommandChar(s, pos, '}')) + { if (__pattern_isCommandChar(s, pos, ',')) parts.push_back(part), part = "", pos++; else @@ -1220,7 +1054,8 @@ static void __pattern_scanCounts(const std::string &s, size_t &pos, int &from, i std::vector numbers; - for (size_t i = 0; i < parts.size(); i++) { + for (size_t i = 0; i < parts.size(); i++) + { if (parts[i].length() == 0) __testlib_fail("pattern: Illegal pattern (or part) \"" + s + "\""); int number; @@ -1236,45 +1071,53 @@ static void __pattern_scanCounts(const std::string &s, size_t &pos, int &from, i if (from > to) __testlib_fail("pattern: Illegal pattern (or part) \"" + s + "\""); - } else { - if (__pattern_isCommandChar(s, pos, '?')) { + } + else + { + if (__pattern_isCommandChar(s, pos, '?')) + { from = 0, to = 1, pos++; return; } - if (__pattern_isCommandChar(s, pos, '*')) { + if (__pattern_isCommandChar(s, pos, '*')) + { from = 0, to = INT_MAX, pos++; return; } - if (__pattern_isCommandChar(s, pos, '+')) { + if (__pattern_isCommandChar(s, pos, '+')) + { from = 1, to = INT_MAX, pos++; return; } - + from = to = 1; } } -static std::vector __pattern_scanCharSet(const std::string &s, size_t &pos) { +static std::vector __pattern_scanCharSet(const std::string& s, size_t& pos) +{ if (pos >= s.length()) __testlib_fail("pattern: Illegal pattern (or part) \"" + s + "\""); std::vector result; - if (__pattern_isCommandChar(s, pos, '[')) { + if (__pattern_isCommandChar(s, pos, '[')) + { pos++; bool negative = __pattern_isCommandChar(s, pos, '^'); - if (negative) - pos++; char prev = 0; - while (pos < s.length() && !__pattern_isCommandChar(s, pos, ']')) { - if (__pattern_isCommandChar(s, pos, '-') && prev != 0) { + while (pos < s.length() && !__pattern_isCommandChar(s, pos, ']')) + { + if (__pattern_isCommandChar(s, pos, '-') && prev != 0) + { pos++; - if (pos + 1 == s.length() || __pattern_isCommandChar(s, pos, ']')) { + if (pos + 1 == s.length() || __pattern_isCommandChar(s, pos, ']')) + { result.push_back(prev); prev = '-'; continue; @@ -1289,7 +1132,9 @@ static std::vector __pattern_scanCharSet(const std::string &s, size_t &pos result.push_back(next); prev = 0; - } else { + } + else + { if (prev != 0) result.push_back(prev); prev = __pattern_getChar(s, pos); @@ -1304,10 +1149,12 @@ static std::vector __pattern_scanCharSet(const std::string &s, size_t &pos pos++; - if (negative) { + if (negative) + { std::sort(result.begin(), result.end()); std::vector actuals; - for (int code = 0; code < 255; code++) { + for (int code = 0; code < 255; code++) + { char c = char(code); if (!std::binary_search(result.begin(), result.end(), c)) actuals.push_back(c); @@ -1316,13 +1163,15 @@ static std::vector __pattern_scanCharSet(const std::string &s, size_t &pos } std::sort(result.begin(), result.end()); - } else + } + else result.push_back(__pattern_getChar(s, pos)); return result; } -pattern::pattern(std::string s) : s(s), from(0), to(0) { +pattern::pattern(std::string s): s(s), from(0), to(0) +{ std::string t; for (size_t i = 0; i < s.length(); i++) if (!__pattern_isCommandChar(s, i, ' ')) @@ -1333,19 +1182,22 @@ pattern::pattern(std::string s) : s(s), from(0), to(0) { int firstClose = -1; std::vector seps; - for (size_t i = 0; i < s.length(); i++) { - if (__pattern_isCommandChar(s, i, '(')) { + for (size_t i = 0; i < s.length(); i++) + { + if (__pattern_isCommandChar(s, i, '(')) + { opened++; continue; } - if (__pattern_isCommandChar(s, i, ')')) { + if (__pattern_isCommandChar(s, i, ')')) + { opened--; if (opened == 0 && firstClose == -1) firstClose = int(i); continue; } - + if (opened < 0) __testlib_fail("pattern: Illegal pattern (or part) \"" + s + "\""); @@ -1356,19 +1208,26 @@ pattern::pattern(std::string s) : s(s), from(0), to(0) { if (opened != 0) __testlib_fail("pattern: Illegal pattern (or part) \"" + s + "\""); - if (seps.size() == 0 && firstClose + 1 == (int) s.length() - && __pattern_isCommandChar(s, 0, '(') && __pattern_isCommandChar(s, s.length() - 1, ')')) { + if (seps.size() == 0 && firstClose + 1 == (int)s.length() + && __pattern_isCommandChar(s, 0, '(') && __pattern_isCommandChar(s, s.length() - 1, ')')) + { children.push_back(pattern(s.substr(1, s.length() - 2))); - } else { - if (seps.size() > 0) { - seps.push_back(int(s.length())); + } + else + { + if (seps.size() > 0) + { + seps.push_back(s.length()); int last = 0; - for (size_t i = 0; i < seps.size(); i++) { + for (size_t i = 0; i < seps.size(); i++) + { children.push_back(pattern(s.substr(last, seps[i] - last))); last = seps[i] + 1; } - } else { + } + else + { size_t pos = 0; chars = __pattern_scanCharSet(s, pos); __pattern_scanCounts(s, pos, from, to); @@ -1377,49 +1236,34 @@ pattern::pattern(std::string s) : s(s), from(0), to(0) { } } } - /* End of pattern implementation */ -template -inline bool isEof(C c) { - return c == EOFC; +template +inline bool isEof(C c) +{ + return (c == EOF || c == EOFC); } -template -inline bool isEoln(C c) { +template +inline bool isEoln(C c) +{ return (c == LF || c == CR); } template -inline bool isBlanks(C c) { +inline bool isBlanks(C c) +{ return (c == LF || c == CR || c == SPACE || c == TAB); } -inline std::string trim(const std::string &s) { - if (s.empty()) - return s; - - int left = 0; - while (left < int(s.length()) && isBlanks(s[left])) - left++; - if (left >= int(s.length())) - return ""; - - int right = int(s.length()) - 1; - while (right >= 0 && isBlanks(s[right])) - right--; - if (right < 0) - return ""; - - return s.substr(left, right - left + 1); -} - -enum TMode { +enum TMode +{ _input, _output, _answer }; /* Outcomes 6-15 are reserved for future use. */ -enum TResult { +enum TResult +{ _ok = 0, _wa = 1, _pe = 2, @@ -1430,7 +1274,8 @@ enum TResult { _partially = 16 }; -enum TTestlibMode { +enum TTestlibMode +{ _unknown, _checker, _validator, _generator, _interactor }; @@ -1438,84 +1283,83 @@ enum TTestlibMode { /* Outcomes 6-15 are reserved for future use. */ const std::string outcomes[] = { - "accepted", - "wrong-answer", - "presentation-error", - "fail", - "fail", + "accepted", + "wrong-answer", + "presentation-error", + "fail", + "fail", #ifndef PCMS2 - "points", + "points", #else - "relative-scoring", + "relative-scoring", #endif - "reserved", - "reserved", - "unexpected-eof", - "reserved", - "reserved", - "reserved", - "reserved", - "reserved", - "reserved", - "reserved", - "partially-correct" + "reserved", + "reserved", + "unexpected-eof", + "reserved", + "reserved", + "reserved", + "reserved", + "reserved", + "reserved", + "reserved", + "partially-correct" }; -class InputStreamReader { +class InputStreamReader +{ public: - virtual int curChar() = 0; - - virtual int nextChar() = 0; - + virtual int curChar() = 0; + virtual int nextChar() = 0; virtual void skipChar() = 0; - virtual void unreadChar(int c) = 0; - virtual std::string getName() = 0; - virtual bool eof() = 0; - - virtual void close() = 0; - - virtual int getLine() = 0; - virtual ~InputStreamReader() = 0; }; -InputStreamReader::~InputStreamReader() { +InputStreamReader::~InputStreamReader() +{ // No operations. } -class StringInputStreamReader : public InputStreamReader { +class StringInputStreamReader: public InputStreamReader +{ private: std::string s; size_t pos; public: - StringInputStreamReader(const std::string &content) : s(content), pos(0) { + StringInputStreamReader(const std::string& content): s(content), pos(0) + { // No operations. } - int curChar() { + int curChar() + { if (pos >= s.length()) - return EOFC; + return EOF; else + { return s[pos]; + } } - int nextChar() { - if (pos >= s.length()) { - pos++; - return EOFC; - } else + int nextChar() + { + if (pos >= s.length()) + return EOF; + else return s[pos++]; } - void skipChar() { + void skipChar() + { pos++; } - void unreadChar(int c) { + void unreadChar(int c) + { if (pos == 0) __testlib_fail("FileFileInputStreamReader::unreadChar(int): pos == 0."); pos--; @@ -1523,144 +1367,108 @@ public: s[pos] = char(c); } - std::string getName() { + std::string getName() + { return __testlib_part(s); } - int getLine() { - return -1; - } - - bool eof() { + bool eof() + { return pos >= s.length(); } - - void close() { - // No operations. - } }; -class FileInputStreamReader : public InputStreamReader { +class FileInputStreamReader: public InputStreamReader +{ private: - std::FILE *file; + std::FILE* file; std::string name; - int line; - std::vector undoChars; - - inline int postprocessGetc(int getcResult) { - if (getcResult != EOF) - return getcResult; - else - return EOFC; - } - - int getc(FILE *file) { - int c; - if (undoChars.empty()) - c = ::getc(file); - else { - c = undoChars.back(); - undoChars.pop_back(); - } - - if (c == LF) - line++; - return c; - } - - int ungetc(int c/*, FILE* file*/) { - if (c == LF) - line--; - undoChars.push_back(c); - return c; - } public: - FileInputStreamReader(std::FILE *file, const std::string &name) : file(file), name(name), line(1) { + FileInputStreamReader(std::FILE* file, const std::string& name): file(file), name(name) + { // No operations. } - int curChar() { + int curChar() + { if (feof(file)) - return EOFC; - else { + return EOF; + else + { int c = getc(file); - ungetc(c/*, file*/); - return postprocessGetc(c); + ungetc(c, file); + return c; } } - int nextChar() { + int nextChar() + { if (feof(file)) - return EOFC; + return EOF; else - return postprocessGetc(getc(file)); + return getc(file); } - void skipChar() { + void skipChar() + { getc(file); } - void unreadChar(int c) { - ungetc(c/*, file*/); + void unreadChar(int c) + { + ungetc(c, file); } - std::string getName() { + std::string getName() + { return name; } - int getLine() { - return line; - } - - bool eof() { - if (NULL == file || feof(file)) + bool eof() + { + if (feof(file)) return true; - else { - int c = nextChar(); - if (c == EOFC || (c == EOF && feof(file))) - return true; - unreadChar(c); - return false; - } - } - - void close() { - if (NULL != file) { - fclose(file); - file = NULL; + else + { + int c = getc(file); + bool result = (c == EOF); + ungetc(c, file); + return result; } } }; -class BufferedFileInputStreamReader : public InputStreamReader { +class BufferedFileInputStreamReader: public InputStreamReader +{ private: static const size_t BUFFER_SIZE; - static const size_t MAX_UNREAD_COUNT; - - std::FILE *file; - std::string name; - int line; - - char *buffer; - bool *isEof; + static const size_t MAX_UNREAD_COUNT; + + std::FILE* file; + char* buffer; + bool* isEof; int bufferPos; size_t bufferSize; - bool refill() { + std::string name; + + bool refill() + { if (NULL == file) __testlib_fail("BufferedFileInputStreamReader: file == NULL (" + getName() + ")"); - if (bufferPos >= int(bufferSize)) { + if (bufferPos >= int(bufferSize)) + { size_t readSize = fread( - buffer + MAX_UNREAD_COUNT, - 1, - BUFFER_SIZE - MAX_UNREAD_COUNT, - file + buffer + MAX_UNREAD_COUNT, + 1, + BUFFER_SIZE - MAX_UNREAD_COUNT, + file ); if (readSize < BUFFER_SIZE - MAX_UNREAD_COUNT - && ferror(file)) + && ferror(file)) __testlib_fail("BufferedFileInputStreamReader: unable to read (" + getName() + ")"); bufferSize = MAX_UNREAD_COUNT + readSize; @@ -1668,104 +1476,95 @@ private: std::memset(isEof + MAX_UNREAD_COUNT, 0, sizeof(isEof[0]) * readSize); return readSize > 0; - } else + } + else return true; } - char increment() { - char c; - if ((c = buffer[bufferPos++]) == LF) - line++; - return c; - } - public: - BufferedFileInputStreamReader(std::FILE *file, const std::string &name) : file(file), name(name), line(1) { + BufferedFileInputStreamReader(std::FILE* file, const std::string& name): file(file), name(name) + { buffer = new char[BUFFER_SIZE]; isEof = new bool[BUFFER_SIZE]; bufferSize = MAX_UNREAD_COUNT; bufferPos = int(MAX_UNREAD_COUNT); } - ~BufferedFileInputStreamReader() { - if (NULL != buffer) { + ~BufferedFileInputStreamReader() + { + if (NULL != buffer) + { delete[] buffer; buffer = NULL; } - if (NULL != isEof) { + if (NULL != isEof) + { delete[] isEof; isEof = NULL; } } - int curChar() { + int curChar() + { if (!refill()) - return EOFC; + return EOF; - return isEof[bufferPos] ? EOFC : buffer[bufferPos]; + return isEof[bufferPos] ? EOF : buffer[bufferPos]; } - int nextChar() { + int nextChar() + { if (!refill()) - return EOFC; + return EOF; - return isEof[bufferPos] ? EOFC : increment(); + return isEof[bufferPos] ? EOF : buffer[bufferPos++]; } - void skipChar() { - increment(); + void skipChar() + { + bufferPos++; } - void unreadChar(int c) { + void unreadChar(int c) + { bufferPos--; if (bufferPos < 0) __testlib_fail("BufferedFileInputStreamReader::unreadChar(int): bufferPos < 0"); - isEof[bufferPos] = (c == EOFC); - buffer[bufferPos] = char(c); - if (c == LF) - line--; + isEof[bufferPos] = (c == EOF); + buffer[bufferPos] = char(c == EOF ? EOFC : c); } - std::string getName() { + std::string getName() + { return name; } - - int getLine() { - return line; - } - - bool eof() { - return !refill() || EOFC == curChar(); - } - - void close() { - if (NULL != file) { - fclose(file); - file = NULL; - } + + bool eof() + { + return !refill() || EOF == curChar(); } }; -const size_t BufferedFileInputStreamReader::BUFFER_SIZE = 2000000; -const size_t BufferedFileInputStreamReader::MAX_UNREAD_COUNT = BufferedFileInputStreamReader::BUFFER_SIZE / 2; +const size_t BufferedFileInputStreamReader::BUFFER_SIZE = 1000000; +const size_t BufferedFileInputStreamReader::MAX_UNREAD_COUNT = 100; /* * Streams to be used for reading data in checkers or validators. * Each read*() method moves pointer to the next character after the * read value. */ -struct InStream { +struct InStream +{ /* Do not use them. */ InStream(); - ~InStream(); /* Wrap std::string with InStream. */ - InStream(const InStream &baseStream, std::string content); + InStream(const InStream& baseStream, std::string content); - InputStreamReader *reader; - int lastLine; + InputStreamReader* reader; + std::FILE* file; std::string name; TMode mode; bool opened; @@ -1775,45 +1574,32 @@ struct InStream { int wordReserveSize; std::string _tmpReadToken; - int readManyIteration; - size_t maxFileSize; - size_t maxTokenLength; - size_t maxMessageLength; - void init(std::string fileName, TMode mode); + void init(std::FILE* f, TMode mode); - void init(std::FILE *f, TMode mode); - - /* Moves stream pointer to the first non-white-space character or EOF. */ + /* Moves stream pointer to the first non-white-space character or EOF. */ void skipBlanks(); - + /* Returns current character in the stream. Doesn't remove it from stream. */ char curChar(); - /* Moves stream pointer one character forward. */ void skipChar(); - /* Returns current character and moves pointer one character forward. */ char nextChar(); - + /* Returns current character and moves pointer one character forward. */ char readChar(); - /* As "readChar()" but ensures that the result is equal to given parameter. */ char readChar(char c); - /* As "readChar()" but ensures that the result is equal to the space (code=32). */ char readSpace(); - /* Puts back the character into the stream. */ void unreadChar(char c); /* Reopens stream, you should not use it. */ - void reset(std::FILE *file = NULL); - + void reset(); /* Checks that current position is EOF. If not it doesn't move stream pointer. */ bool eof(); - /* Moves pointer to the first non-white-space character and calls "eof()". */ bool seekEof(); @@ -1823,7 +1609,6 @@ struct InStream { * In strict mode expects "#13#10" for windows or "#10" for other platforms. */ bool eoln(); - /* Moves pointer to the first non-space and non-tab character and calls "eoln()". */ bool seekEoln(); @@ -1835,230 +1620,110 @@ struct InStream { * (strict mode is used in validators usually). */ std::string readWord(); - /* The same as "readWord()", it is preffered to use "readToken()". */ std::string readToken(); - /* The same as "readWord()", but ensures that token matches to given pattern. */ - std::string readWord(const std::string &ptrn, const std::string &variableName = ""); - - std::string readWord(const pattern &p, const std::string &variableName = ""); - - std::vector - readWords(int size, const std::string &ptrn, const std::string &variablesName = "", int indexBase = 1); - - std::vector - readWords(int size, const pattern &p, const std::string &variablesName = "", int indexBase = 1); - - std::vector readWords(int size, int indexBase = 1); - + std::string readWord(const std::string& ptrn, const std::string& variableName = ""); + std::string readWord(const pattern& p, const std::string& variableName = ""); /* The same as "readToken()", but ensures that token matches to given pattern. */ - std::string readToken(const std::string &ptrn, const std::string &variableName = ""); + std::string readToken(const std::string& ptrn, const std::string& variableName = ""); + std::string readToken(const pattern& p, const std::string& variableName = ""); - std::string readToken(const pattern &p, const std::string &variableName = ""); + void readWordTo(std::string& result); + void readWordTo(std::string& result, const pattern& p, const std::string& variableName = ""); + void readWordTo(std::string& result, const std::string& ptrn, const std::string& variableName = ""); - std::vector - readTokens(int size, const std::string &ptrn, const std::string &variablesName = "", int indexBase = 1); - - std::vector - readTokens(int size, const pattern &p, const std::string &variablesName = "", int indexBase = 1); - - std::vector readTokens(int size, int indexBase = 1); - - void readWordTo(std::string &result); - - void readWordTo(std::string &result, const pattern &p, const std::string &variableName = ""); - - void readWordTo(std::string &result, const std::string &ptrn, const std::string &variableName = ""); - - void readTokenTo(std::string &result); - - void readTokenTo(std::string &result, const pattern &p, const std::string &variableName = ""); - - void readTokenTo(std::string &result, const std::string &ptrn, const std::string &variableName = ""); + void readTokenTo(std::string& result); + void readTokenTo(std::string& result, const pattern& p, const std::string& variableName = ""); + void readTokenTo(std::string& result, const std::string& ptrn, const std::string& variableName = ""); /* * Reads new long long value. Ignores white-spaces into the non-strict mode * (strict mode is used in validators usually). */ long long readLong(); - - unsigned long long readUnsignedLong(); - - /* + /* * Reads new int. Ignores white-spaces into the non-strict mode * (strict mode is used in validators usually). */ int readInteger(); - - /* + /* * Reads new int. Ignores white-spaces into the non-strict mode * (strict mode is used in validators usually). */ int readInt(); /* As "readLong()" but ensures that value in the range [minv,maxv]. */ - long long readLong(long long minv, long long maxv, const std::string &variableName = ""); - - /* Reads space-separated sequence of long longs. */ - std::vector - readLongs(int size, long long minv, long long maxv, const std::string &variablesName = "", int indexBase = 1); - - /* Reads space-separated sequence of long longs. */ - std::vector readLongs(int size, int indexBase = 1); - - unsigned long long - readUnsignedLong(unsigned long long minv, unsigned long long maxv, const std::string &variableName = ""); - - std::vector - readUnsignedLongs(int size, unsigned long long minv, unsigned long long maxv, const std::string &variablesName = "", - int indexBase = 1); - - std::vector readUnsignedLongs(int size, int indexBase = 1); - - unsigned long long readLong(unsigned long long minv, unsigned long long maxv, const std::string &variableName = ""); - - std::vector - readLongs(int size, unsigned long long minv, unsigned long long maxv, const std::string &variablesName = "", - int indexBase = 1); - + long long readLong(long long minv, long long maxv, const std::string& variableName = ""); /* As "readInteger()" but ensures that value in the range [minv,maxv]. */ - int readInteger(int minv, int maxv, const std::string &variableName = ""); - + int readInteger(int minv, int maxv, const std::string& variableName = ""); /* As "readInt()" but ensures that value in the range [minv,maxv]. */ - int readInt(int minv, int maxv, const std::string &variableName = ""); - - /* Reads space-separated sequence of integers. */ - std::vector - readIntegers(int size, int minv, int maxv, const std::string &variablesName = "", int indexBase = 1); - - /* Reads space-separated sequence of integers. */ - std::vector readIntegers(int size, int indexBase = 1); - - /* Reads space-separated sequence of integers. */ - std::vector readInts(int size, int minv, int maxv, const std::string &variablesName = "", int indexBase = 1); - - /* Reads space-separated sequence of integers. */ - std::vector readInts(int size, int indexBase = 1); - + + int readInt(int minv, int maxv, const std::string& variableName = ""); /* * Reads new double. Ignores white-spaces into the non-strict mode * (strict mode is used in validators usually). */ double readReal(); - - /* + /* * Reads new double. Ignores white-spaces into the non-strict mode * (strict mode is used in validators usually). */ double readDouble(); - + /* As "readReal()" but ensures that value in the range [minv,maxv]. */ - double readReal(double minv, double maxv, const std::string &variableName = ""); - - std::vector - readReals(int size, double minv, double maxv, const std::string &variablesName = "", int indexBase = 1); - - std::vector readReals(int size, int indexBase = 1); - + double readReal(double minv, double maxv, const std::string& variableName = ""); /* As "readDouble()" but ensures that value in the range [minv,maxv]. */ - double readDouble(double minv, double maxv, const std::string &variableName = ""); - - std::vector - readDoubles(int size, double minv, double maxv, const std::string &variablesName = "", int indexBase = 1); - - std::vector readDoubles(int size, int indexBase = 1); - + double readDouble(double minv, double maxv, const std::string& variableName = ""); + /* * As "readReal()" but ensures that value in the range [minv,maxv] and * number of digit after the decimal point is in range [minAfterPointDigitCount,maxAfterPointDigitCount] * and number is in the form "[-]digit(s)[.digit(s)]". */ double readStrictReal(double minv, double maxv, - int minAfterPointDigitCount, int maxAfterPointDigitCount, - const std::string &variableName = ""); - - std::vector readStrictReals(int size, double minv, double maxv, - int minAfterPointDigitCount, int maxAfterPointDigitCount, - const std::string &variablesName = "", int indexBase = 1); - + int minAfterPointDigitCount, int maxAfterPointDigitCount, + const std::string& variableName = ""); /* * As "readDouble()" but ensures that value in the range [minv,maxv] and * number of digit after the decimal point is in range [minAfterPointDigitCount,maxAfterPointDigitCount] * and number is in the form "[-]digit(s)[.digit(s)]". */ double readStrictDouble(double minv, double maxv, - int minAfterPointDigitCount, int maxAfterPointDigitCount, - const std::string &variableName = ""); - - std::vector readStrictDoubles(int size, double minv, double maxv, - int minAfterPointDigitCount, int maxAfterPointDigitCount, - const std::string &variablesName = "", int indexBase = 1); - + int minAfterPointDigitCount, int maxAfterPointDigitCount, + const std::string& variableName = ""); + /* As readLine(). */ std::string readString(); - - /* Read many lines. */ - std::vector readStrings(int size, int indexBase = 1); - /* See readLine(). */ - void readStringTo(std::string &result); - + void readStringTo(std::string& result); /* The same as "readLine()/readString()", but ensures that line matches to the given pattern. */ - std::string readString(const pattern &p, const std::string &variableName = ""); - + std::string readString(const pattern& p, const std::string& variableName = ""); /* The same as "readLine()/readString()", but ensures that line matches to the given pattern. */ - std::string readString(const std::string &ptrn, const std::string &variableName = ""); - - /* Read many lines. */ - std::vector - readStrings(int size, const pattern &p, const std::string &variableName = "", int indexBase = 1); - - /* Read many lines. */ - std::vector - readStrings(int size, const std::string &ptrn, const std::string &variableName = "", int indexBase = 1); - + std::string readString(const std::string& ptrn, const std::string& variableName = ""); /* The same as "readLine()/readString()", but ensures that line matches to the given pattern. */ - void readStringTo(std::string &result, const pattern &p, const std::string &variableName = ""); - + void readStringTo(std::string& result, const pattern& p, const std::string& variableName = ""); /* The same as "readLine()/readString()", but ensures that line matches to the given pattern. */ - void readStringTo(std::string &result, const std::string &ptrn, const std::string &variableName = ""); + void readStringTo(std::string& result, const std::string& ptrn, const std::string& variableName = ""); /* * Reads line from the current position to EOLN or EOF. Moves stream pointer to * the first character of the new line (if possible). */ std::string readLine(); - - /* Read many lines. */ - std::vector readLines(int size, int indexBase = 1); - /* See readLine(). */ - void readLineTo(std::string &result); - + void readLineTo(std::string& result); /* The same as "readLine()", but ensures that line matches to the given pattern. */ - std::string readLine(const pattern &p, const std::string &variableName = ""); - + std::string readLine(const pattern& p, const std::string& variableName = ""); /* The same as "readLine()", but ensures that line matches to the given pattern. */ - std::string readLine(const std::string &ptrn, const std::string &variableName = ""); - - /* Read many lines. */ - std::vector - readLines(int size, const pattern &p, const std::string &variableName = "", int indexBase = 1); - - /* Read many lines. */ - std::vector - readLines(int size, const std::string &ptrn, const std::string &variableName = "", int indexBase = 1); - + std::string readLine(const std::string& ptrn, const std::string& variableName = ""); /* The same as "readLine()", but ensures that line matches to the given pattern. */ - void readLineTo(std::string &result, const pattern &p, const std::string &variableName = ""); - + void readLineTo(std::string& result, const pattern& p, const std::string& variableName = ""); /* The same as "readLine()", but ensures that line matches to the given pattern. */ - void readLineTo(std::string &result, const std::string &ptrn, const std::string &variableName = ""); + void readLineTo(std::string& result, const std::string& ptrn, const std::string& variableName = ""); /* Reads EOLN or fails. Use it in validators. Calls "eoln()" method internally. */ void readEoln(); - /* Reads EOF or fails. Use it in validators. Calls "eof()" method internally. */ void readEof(); @@ -2066,59 +1731,34 @@ struct InStream { * Quit-functions aborts program with and : * input/answer streams replace any result to FAIL. */ - NORETURN void quit(TResult result, const char *msg); + NORETURN void quit(TResult result, const char* msg); /* * Quit-functions aborts program with and : * input/answer streams replace any result to FAIL. */ - NORETURN void quitf(TResult result, const char *msg, ...); - - /* - * Quit-functions aborts program with and : - * input/answer streams replace any result to FAIL. - */ - void quitif(bool condition, TResult result, const char *msg, ...); + NORETURN void quitf(TResult result, const char* msg, ...); /* * Quit-functions aborts program with and : * input/answer streams replace any result to FAIL. */ NORETURN void quits(TResult result, std::string msg); - /* - * Checks condition and aborts a program if codition is false. - * Returns _wa for ouf and _fail on any other streams. - */ -#ifdef __GNUC__ - __attribute__ ((format (printf, 3, 4))) -#endif - void ensuref(bool cond, const char *format, ...); - - void __testlib_ensure(bool cond, std::string message); - void close(); - const static int NO_INDEX = INT_MAX; - const static char OPEN_BRACKET = char(11); - const static char CLOSE_BRACKET = char(17); - - const static WORD LightGray = 0x07; - const static WORD LightRed = 0x0c; - const static WORD LightCyan = 0x0b; - const static WORD LightGreen = 0x0a; - const static WORD LightYellow = 0x0e; + const static WORD LightGray = 0x07; + const static WORD LightRed = 0x0c; + const static WORD LightCyan = 0x0b; + const static WORD LightGreen = 0x0a; + const static WORD LightYellow = 0x0e; static void textColor(WORD color); - - static void quitscr(WORD color, const char *msg); - + static void quitscr(WORD color, const char* msg); static void quitscrS(WORD color, std::string msg); - - void xmlSafeWrite(std::FILE *file, const char *msg); + void xmlSafeWrite(std::FILE * file, const char* msg); private: - InStream(const InStream &); - - InStream &operator=(const InStream &); + InStream(const InStream&); + InStream& operator =(const InStream&); }; InStream inf; @@ -2131,175 +1771,29 @@ random_t rnd; TTestlibMode testlibMode = _unknown; double __testlib_points = std::numeric_limits::infinity(); -struct ValidatorBoundsHit { - static const double EPS; - bool minHit; - bool maxHit; - - ValidatorBoundsHit(bool minHit = false, bool maxHit = false) : minHit(minHit), maxHit(maxHit) { - }; - - ValidatorBoundsHit merge(const ValidatorBoundsHit &validatorBoundsHit) { - return ValidatorBoundsHit( - __testlib_max(minHit, validatorBoundsHit.minHit), - __testlib_max(maxHit, validatorBoundsHit.maxHit) - ); - } -}; - -const double ValidatorBoundsHit::EPS = 1E-12; - -class Validator { -private: - bool _initialized; - std::string _testset; - std::string _group; - - std::string _testOverviewLogFileName; - std::map _boundsHitByVariableName; - std::set _features; - std::set _hitFeatures; - - bool isVariableNameBoundsAnalyzable(const std::string &variableName) { - for (size_t i = 0; i < variableName.length(); i++) - if ((variableName[i] >= '0' && variableName[i] <= '9') || variableName[i] < ' ') - return false; - return true; - } - - bool isFeatureNameAnalyzable(const std::string &featureName) { - for (size_t i = 0; i < featureName.length(); i++) - if (featureName[i] < ' ') - return false; - return true; - } - -public: - Validator() : _initialized(false), _testset("tests"), _group() { - } - - void initialize() { - _initialized = true; - } - - std::string testset() const { - if (!_initialized) - __testlib_fail("Validator should be initialized with registerValidation(argc, argv) instead of registerValidation() to support validator.testset()"); - return _testset; - } - - std::string group() const { - if (!_initialized) - __testlib_fail("Validator should be initialized with registerValidation(argc, argv) instead of registerValidation() to support validator.group()"); - return _group; - } - - std::string testOverviewLogFileName() const { - return _testOverviewLogFileName; - } - - void setTestset(const char *const testset) { - _testset = testset; - } - - void setGroup(const char *const group) { - _group = group; - } - - void setTestOverviewLogFileName(const char *const testOverviewLogFileName) { - _testOverviewLogFileName = testOverviewLogFileName; - } - - void addBoundsHit(const std::string &variableName, ValidatorBoundsHit boundsHit) { - if (isVariableNameBoundsAnalyzable(variableName)) { - _boundsHitByVariableName[variableName] - = boundsHit.merge(_boundsHitByVariableName[variableName]); - } - } - - std::string getBoundsHitLog() { - std::string result; - for (std::map::iterator i = _boundsHitByVariableName.begin(); - i != _boundsHitByVariableName.end(); - i++) { - result += "\"" + i->first + "\":"; - if (i->second.minHit) - result += " min-value-hit"; - if (i->second.maxHit) - result += " max-value-hit"; - result += "\n"; - } - return result; - } - - std::string getFeaturesLog() { - std::string result; - for (std::set::iterator i = _features.begin(); - i != _features.end(); - i++) { - result += "feature \"" + *i + "\":"; - if (_hitFeatures.count(*i)) - result += " hit"; - result += "\n"; - } - return result; - } - - void writeTestOverviewLog() { - if (!_testOverviewLogFileName.empty()) { - std::string fileName(_testOverviewLogFileName); - _testOverviewLogFileName = ""; - FILE *testOverviewLogFile = fopen(fileName.c_str(), "w"); - if (NULL == testOverviewLogFile) - __testlib_fail("Validator::writeTestOverviewLog: can't test overview log to (" + fileName + ")"); - fprintf(testOverviewLogFile, "%s%s", getBoundsHitLog().c_str(), getFeaturesLog().c_str()); - if (fclose(testOverviewLogFile)) - __testlib_fail( - "Validator::writeTestOverviewLog: can't close test overview log file (" + fileName + ")"); - } - } - - void addFeature(const std::string &feature) { - if (_features.count(feature)) - __testlib_fail("Feature " + feature + " registered twice."); - if (!isFeatureNameAnalyzable(feature)) - __testlib_fail("Feature name '" + feature + "' contains restricted characters."); - - _features.insert(feature); - } - - void feature(const std::string &feature) { - if (!isFeatureNameAnalyzable(feature)) - __testlib_fail("Feature name '" + feature + "' contains restricted characters."); - - if (!_features.count(feature)) - __testlib_fail("Feature " + feature + " didn't registered via addFeature(feature)."); - - _hitFeatures.insert(feature); - } -} validator; - -struct TestlibFinalizeGuard { +struct TestlibFinalizeGuard +{ static bool alive; int quitCount, readEofCount; - TestlibFinalizeGuard() : quitCount(0), readEofCount(0) { + TestlibFinalizeGuard() : quitCount(0), readEofCount(0) + { // No operations. } - ~TestlibFinalizeGuard() { + ~TestlibFinalizeGuard() + { bool _alive = alive; alive = false; - if (_alive) { + if (_alive) + { if (testlibMode == _checker && quitCount == 0) __testlib_fail("Checker must end with quit or quitf call."); if (testlibMode == _validator && readEofCount == 0 && quitCount == 0) __testlib_fail("Validator must end with readEof call."); } - - validator.writeTestOverviewLog(); } }; @@ -2309,7 +1803,8 @@ TestlibFinalizeGuard testlibFinalizeGuard; /* * Call it to disable checks on finalization. */ -void disableFinalizeGuard() { +void disableFinalizeGuard() +{ TestlibFinalizeGuard::alive = false; } @@ -2320,141 +1815,47 @@ std::fstream tout; /* implementation */ -#if __cplusplus > 199711L || defined(_MSC_VER) -template -static std::string vtos(const T &t, std::true_type) { - if (t == 0) - return "0"; - else { - T n(t); - bool negative = n < 0; - std::string s; - while (n != 0) { - T digit = n % 10; - if (digit < 0) - digit = -digit; - s += char('0' + digit); - n /= 10; - } - std::reverse(s.begin(), s.end()); - return negative ? "-" + s : s; - } -} - -template -static std::string vtos(const T &t, std::false_type) { - std::string s; - static std::stringstream ss; - ss.str(std::string()); - ss.clear(); - ss << t; - ss >> s; - return s; -} - -template -static std::string vtos(const T &t) { - return vtos(t, std::is_integral()); -} - -/* signed case. */ -template -static std::string toHumanReadableString(const T &n, std::false_type) { - if (n == 0) - return vtos(n); - int trailingZeroCount = 0; - T n_ = n; - while (n_ % 10 == 0) - n_ /= 10, trailingZeroCount++; - if (trailingZeroCount >= 7) { - if (n_ == 1) - return "10^" + vtos(trailingZeroCount); - else if (n_ == -1) - return "-10^" + vtos(trailingZeroCount); - else - return vtos(n_) + "*10^" + vtos(trailingZeroCount); - } else - return vtos(n); -} - -/* unsigned case. */ -template -static std::string toHumanReadableString(const T &n, std::true_type) { - if (n == 0) - return vtos(n); - int trailingZeroCount = 0; - T n_ = n; - while (n_ % 10 == 0) - n_ /= 10, trailingZeroCount++; - if (trailingZeroCount >= 7) { - if (n_ == 1) - return "10^" + vtos(trailingZeroCount); - else - return vtos(n_) + "*10^" + vtos(trailingZeroCount); - } else - return vtos(n); -} - -template -static std::string toHumanReadableString(const T &n) { - return toHumanReadableString(n, std::is_unsigned()); -} -#else -template +template static std::string vtos(const T& t) { std::string s; - static std::stringstream ss; - ss.str(std::string()); - ss.clear(); + std::stringstream ss; ss << t; ss >> s; return s; } -template -static std::string toHumanReadableString(const T &n) { - return vtos(n); -} -#endif - -template -static std::string toString(const T &t) { +template +static std::string toString(const T& t) +{ return vtos(t); } -InStream::InStream() { - reader = NULL; - lastLine = -1; - opened = false; +InStream::InStream() +{ + file = NULL; name = ""; mode = _input; strict = false; stdfile = false; wordReserveSize = 4; - readManyIteration = NO_INDEX; - maxFileSize = 128 * 1024 * 1024; // 128MB. - maxTokenLength = 32 * 1024 * 1024; // 32MB. - maxMessageLength = 32000; } -InStream::InStream(const InStream &baseStream, std::string content) { +InStream::InStream(const InStream& baseStream, std::string content) +{ + file = (FILE*)0xbadfeed; + stdfile = false; reader = new StringInputStreamReader(content); - lastLine = -1; opened = true; strict = baseStream.strict; - stdfile = false; mode = baseStream.mode; name = "based on " + baseStream.name; - readManyIteration = NO_INDEX; - maxFileSize = 128 * 1024 * 1024; // 128MB. - maxTokenLength = 32 * 1024 * 1024; // 32MB. - maxMessageLength = 32000; } -InStream::~InStream() { - if (NULL != reader) { - reader->close(); +InStream::~InStream() +{ + if (NULL != reader) + { delete reader; reader = NULL; } @@ -2463,7 +1864,8 @@ InStream::~InStream() { #ifdef __GNUC__ __attribute__((const)) #endif -int resultExitCode(TResult r) { +int resultExitCode(TResult r) +{ if (r == _ok) return OK_EXIT_CODE; if (r == _wa) @@ -2487,156 +1889,69 @@ int resultExitCode(TResult r) { return FAIL_EXIT_CODE; } -void InStream::textColor( -#if !(defined(ON_WINDOWS) && (!defined(_MSC_VER) || _MSC_VER > 1400)) && defined(__GNUC__) - __attribute__((unused)) -#endif - WORD color -) { -#if defined(ON_WINDOWS) && (!defined(_MSC_VER) || _MSC_VER > 1400) +void InStream::textColor(WORD color) +{ +#if defined(ON_WINDOWS) && (!defined(_MSC_VER) || _MSC_VER>1400) HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleTextAttribute(handle, color); #endif -#if !defined(ON_WINDOWS) && defined(__GNUC__) - if (isatty(2)) - { - switch (color) - { - case LightRed: - fprintf(stderr, "\033[1;31m"); - break; - case LightCyan: - fprintf(stderr, "\033[1;36m"); - break; - case LightGreen: - fprintf(stderr, "\033[1;32m"); - break; - case LightYellow: - fprintf(stderr, "\033[1;33m"); - break; - case LightGray: - default: - fprintf(stderr, "\033[0m"); - } - } + +#ifdef linux + char *shell_path = getenv("SHELL"); + if (shell_path && strcmp(shell_path, "/bin/bash") == 0 && isatty(2)) + { + switch (color) + { + case 0: + fprintf(stderr, "\e[0m"); + break; + case LightRed: + fprintf(stderr, "\e[0;31m"); + break; + case LightGreen: + fprintf(stderr, "\e[0;32m"); + break; + case LightYellow: + fprintf(stderr, "\e[0;33m"); + break; + case LightCyan: + fprintf(stderr, "\e[0;36m"); + break; + case LightGray: + fprintf(stderr, "\e[0m"); + break; + } + } #endif } -NORETURN void halt(int exitCode) { +NORETURN void halt(int exitCode) +{ #ifdef FOOTER InStream::textColor(InStream::LightGray); std::fprintf(stderr, "Checker: \"%s\"\n", checkerName.c_str()); std::fprintf(stderr, "Exit code: %d\n", exitCode); InStream::textColor(InStream::LightGray); +#endif +#ifdef linux + InStream::textColor(0); #endif std::exit(exitCode); } -static bool __testlib_shouldCheckDirt(TResult result) { - return result == _ok || result == _points || result >= _partially; -} - -static std::string __testlib_appendMessage(const std::string &message, const std::string &extra) { - int openPos = -1, closePos = -1; - for (size_t i = 0; i < message.length(); i++) { - if (message[i] == InStream::OPEN_BRACKET) { - if (openPos == -1) - openPos = int(i); - else - openPos = INT_MAX; - } - if (message[i] == InStream::CLOSE_BRACKET) { - if (closePos == -1) - closePos = int(i); - else - closePos = INT_MAX; - } - } - if (openPos != -1 && openPos != INT_MAX - && closePos != -1 && closePos != INT_MAX - && openPos < closePos) { - size_t index = message.find(extra, openPos); - if (index == std::string::npos || int(index) >= closePos) { - std::string result(message); - result.insert(closePos, ", " + extra); - return result; - } - return message; - } - - return message + " " + InStream::OPEN_BRACKET + extra + InStream::CLOSE_BRACKET; -} - -static std::string __testlib_toPrintableMessage(const std::string &message) { - int openPos = -1, closePos = -1; - for (size_t i = 0; i < message.length(); i++) { - if (message[i] == InStream::OPEN_BRACKET) { - if (openPos == -1) - openPos = int(i); - else - openPos = INT_MAX; - } - if (message[i] == InStream::CLOSE_BRACKET) { - if (closePos == -1) - closePos = int(i); - else - closePos = INT_MAX; - } - } - if (openPos != -1 && openPos != INT_MAX - && closePos != -1 && closePos != INT_MAX - && openPos < closePos) { - std::string result(message); - result[openPos] = '('; - result[closePos] = ')'; - return result; - } - - return message; -} - -NORETURN void InStream::quit(TResult result, const char *msg) { +NORETURN void InStream::quit(TResult result, const char* msg) +{ if (TestlibFinalizeGuard::alive) testlibFinalizeGuard.quitCount++; - std::string message(msg); - message = trim(message); + if (mode != _output && result != _fail) + quits(_fail, std::string(msg) + " (" + name + ")"); - if (__testlib_hasTestCase) { - if (result != _ok) - message = __testlib_appendMessage(message, "test case " + vtos(__testlib_testCase)); - else { - if (__testlib_testCase == 1) - message = __testlib_appendMessage(message, vtos(__testlib_testCase) + " test case"); - else - message = __testlib_appendMessage(message, vtos(__testlib_testCase) + " test cases"); - } - } - - // You can change maxMessageLength. - // Example: 'inf.maxMessageLength = 1024 * 1024;'. - if (message.length() > maxMessageLength) { - std::string warn = "message length exceeds " + vtos(maxMessageLength) - + ", the message is truncated: "; - message = warn + message.substr(0, maxMessageLength - warn.length()); - } - -#ifndef ENABLE_UNEXPECTED_EOF - if (result == _unexpected_eof) - result = _pe; -#endif - - if (mode != _output && result != _fail) { - if (mode == _input && testlibMode == _validator && lastLine != -1) - quits(_fail, __testlib_appendMessage(__testlib_appendMessage(message, name), "line " + vtos(lastLine))); - else - quits(_fail, __testlib_appendMessage(message, name)); - } - - std::FILE *resultFile; + std::FILE * resultFile; std::string errorName; - - if (__testlib_shouldCheckDirt(result)) { + + if (result == _ok) + { if (testlibMode != _interactor && !ouf.seekEof()) quit(_dirt, "Extra information in the output file"); } @@ -2644,83 +1959,92 @@ NORETURN void InStream::quit(TResult result, const char *msg) { int pctype = result - _partially; bool isPartial = false; - switch (result) { - case _ok: - errorName = "ok "; - quitscrS(LightGreen, errorName); - break; - case _wa: - errorName = "wrong answer "; - quitscrS(LightRed, errorName); - break; - case _pe: - errorName = "wrong output format "; - quitscrS(LightRed, errorName); - break; - case _fail: - errorName = "FAIL "; - quitscrS(LightRed, errorName); - break; - case _dirt: - errorName = "wrong output format "; - quitscrS(LightCyan, errorName); - result = _pe; - break; - case _points: - errorName = "points "; + switch (result) + { + case _ok: + errorName = "ok "; + quitscrS(LightGreen, errorName); + break; + case _wa: + errorName = "wrong answer "; + quitscrS(LightRed, errorName); + break; + case _pe: + errorName = "wrong output format "; + quitscrS(LightRed, errorName); + break; + case _fail: + errorName = "FAIL "; + quitscrS(LightRed, errorName); + break; + case _dirt: + errorName = "wrong output format "; + quitscrS(LightCyan, errorName); + result = _pe; + break; + case _points: + errorName = "points "; + quitscrS(LightYellow, errorName); + break; + case _unexpected_eof: +#ifdef ENABLE_UNEXPECTED_EOF + errorName = "unexpected eof "; +#else + errorName = "wrong output format "; +#endif + quitscrS(LightCyan, errorName); + break; + default: + if (result >= _partially) + { + errorName = format("partially correct (%d) ", pctype); + isPartial = true; quitscrS(LightYellow, errorName); - break; - case _unexpected_eof: - errorName = "unexpected eof "; - quitscrS(LightCyan, errorName); - break; - default: - if (result >= _partially) { - errorName = format("partially correct (%d) ", pctype); - isPartial = true; - quitscrS(LightYellow, errorName); - } else - quit(_fail, "What is the code ??? "); + } + else + quit(_fail, "What is the code ??? "); } - if (resultName != "") { - resultFile = std::fopen(resultName.c_str(), "w"); - if (resultFile == NULL) { - resultName = ""; + if (resultName != "") + { + resultFile = __testlib_fopen(resultName.c_str(), "w"); + if (resultFile == NULL) quit(_fail, "Can not write to the result file"); - } - if (appesMode) { + if (appesMode) + { std::fprintf(resultFile, ""); if (isPartial) - std::fprintf(resultFile, "", - outcomes[(int) _partially].c_str(), pctype); - else { + std::fprintf(resultFile, "", outcomes[(int)_partially].c_str(), pctype); + else + { if (result != _points) - std::fprintf(resultFile, "", outcomes[(int) result].c_str()); - else { + std::fprintf(resultFile, "", outcomes[(int)result].c_str()); + else + { if (__testlib_points == std::numeric_limits::infinity()) quit(_fail, "Expected points, but infinity found"); std::string stringPoints = removeDoubleTrailingZeroes(format("%.10f", __testlib_points)); - std::fprintf(resultFile, "", - outcomes[(int) result].c_str(), stringPoints.c_str()); + std::fprintf(resultFile, "", outcomes[(int)result].c_str(), stringPoints.c_str()); } } - xmlSafeWrite(resultFile, __testlib_toPrintableMessage(message).c_str()); + xmlSafeWrite(resultFile, msg); std::fprintf(resultFile, "\n"); - } else - std::fprintf(resultFile, "%s", __testlib_toPrintableMessage(message).c_str()); - if (NULL == resultFile || fclose(resultFile) != 0) { - resultName = ""; - quit(_fail, "Can not write to the result file"); } + else + std::fprintf(resultFile, "%s", msg); + if (NULL == resultFile || fclose(resultFile) != 0) + quit(_fail, "Can not write to the result file"); } - quitscr(LightGray, __testlib_toPrintableMessage(message).c_str()); + quitscr(LightGray, msg); std::fprintf(stderr, "\n"); - inf.close(); - ouf.close(); - ans.close(); + if (inf.file) + fclose(inf.file); + if (ouf.file) + fclose(ouf.file); + if (ans.file) + fclose(ans.file); if (tout.is_open()) tout.close(); @@ -2733,47 +2057,46 @@ NORETURN void InStream::quit(TResult result, const char *msg) { } #ifdef __GNUC__ -__attribute__ ((format (printf, 3, 4))) + __attribute__ ((format (printf, 3, 4))) #endif -NORETURN void InStream::quitf(TResult result, const char *msg, ...) { +NORETURN void InStream::quitf(TResult result, const char* msg, ...) +{ FMT_TO_RESULT(msg, msg, message); InStream::quit(result, message.c_str()); } -#ifdef __GNUC__ -__attribute__ ((format (printf, 4, 5))) -#endif -void InStream::quitif(bool condition, TResult result, const char *msg, ...) { - if (condition) { - FMT_TO_RESULT(msg, msg, message); - InStream::quit(result, message.c_str()); - } -} - -NORETURN void InStream::quits(TResult result, std::string msg) { +NORETURN void InStream::quits(TResult result, std::string msg) +{ InStream::quit(result, msg.c_str()); } -void InStream::xmlSafeWrite(std::FILE *file, const char *msg) { +void InStream::xmlSafeWrite(std::FILE * file, const char* msg) +{ size_t lmsg = strlen(msg); - for (size_t i = 0; i < lmsg; i++) { - if (msg[i] == '&') { + for (size_t i = 0; i < lmsg; i++) + { + if (msg[i] == '&') + { std::fprintf(file, "%s", "&"); continue; } - if (msg[i] == '<') { + if (msg[i] == '<') + { std::fprintf(file, "%s", "<"); continue; } - if (msg[i] == '>') { + if (msg[i] == '>') + { std::fprintf(file, "%s", ">"); continue; } - if (msg[i] == '"') { + if (msg[i] == '"') + { std::fprintf(file, "%s", """); continue; } - if (0 <= msg[i] && msg[i] <= 31) { + if (0 <= msg[i] && msg[i] <= 31) + { std::fprintf(file, "%c", '.'); continue; } @@ -2781,143 +2104,150 @@ void InStream::xmlSafeWrite(std::FILE *file, const char *msg) { } } -void InStream::quitscrS(WORD color, std::string msg) { +void InStream::quitscrS(WORD color, std::string msg) +{ quitscr(color, msg.c_str()); } -void InStream::quitscr(WORD color, const char *msg) { - if (resultName == "") { +void InStream::quitscr(WORD color, const char* msg) +{ + if (resultName == "") + { textColor(color); std::fprintf(stderr, "%s", msg); textColor(LightGray); } } -void InStream::reset(std::FILE *file) { +void InStream::reset() +{ if (opened && stdfile) quit(_fail, "Can't reset standard handle"); if (opened) close(); - if (!stdfile && NULL == file) - if (NULL == (file = std::fopen(name.c_str(), "rb"))) { + if (!stdfile) + if (NULL == (file = __testlib_fopen(name.c_str(), "rb"))) + { if (mode == _output) - quits(_pe, std::string("Output file not found: \"") + name + "\""); - - if (mode == _answer) - quits(_fail, std::string("Answer file not found: \"") + name + "\""); + quits(_pe, std::string("File not found: \"") + name + "\""); } - if (NULL != file) { - opened = true; - __testlib_set_binary(file); + opened = true; - if (stdfile) - reader = new FileInputStreamReader(file, name); - else - reader = new BufferedFileInputStreamReader(file, name); - } else { - opened = false; - reader = NULL; - } + __testlib_set_binary(file); + + if (stdfile) + reader = new FileInputStreamReader(file, name); + else + reader = new BufferedFileInputStreamReader(file, name); } -void InStream::init(std::string fileName, TMode mode) { +void InStream::init(std::string fileName, TMode mode) +{ opened = false; - name = fileName; - stdfile = false; + if (fileName == "/dev/stdin") + { + name = "stdin"; + this->file = stdin; + stdfile = true; + } + else + { + name = fileName; + stdfile = false; + } this->mode = mode; - - std::ifstream stream; - stream.open(fileName.c_str(), std::ios::in); - if (stream.is_open()) { - std::streampos start = stream.tellg(); - stream.seekg(0, std::ios::end); - std::streampos end = stream.tellg(); - size_t fileSize = size_t(end - start); - stream.close(); - - // You can change maxFileSize. - // Example: 'inf.maxFileSize = 256 * 1024 * 1024;'. - if (fileSize > maxFileSize) - quitf(_pe, "File size exceeds %d bytes, size is %d", int(maxFileSize), int(fileSize)); - } - reset(); } -void InStream::init(std::FILE *f, TMode mode) { +void InStream::init(std::FILE* f, TMode mode) +{ opened = false; + name = "untitled"; - this->mode = mode; - + if (f == stdin) name = "stdin", stdfile = true; + if (f == stdout) name = "stdout", stdfile = true; + if (f == stderr) name = "stderr", stdfile = true; - reset(f); + this->file = f; + this->mode = mode; + + reset(); } -char InStream::curChar() { - return char(reader->curChar()); +char InStream::curChar() +{ + int c = reader->curChar(); + return char(c != EOF ? c : EOFC); } -char InStream::nextChar() { - return char(reader->nextChar()); +char InStream::nextChar() +{ + int c = reader->nextChar(); + return char(c != EOF ? c : EOFC); } -char InStream::readChar() { +char InStream::readChar() +{ return nextChar(); } -char InStream::readChar(char c) { - lastLine = reader->getLine(); +char InStream::readChar(char c) +{ char found = readChar(); - if (c != found) { + if (c != found) + { if (!isEoln(found)) - quit(_pe, ("Unexpected character '" + std::string(1, found) + "', but '" + std::string(1, c) + - "' expected").c_str()); + quit(_pe, ("Unexpected character '" + std::string(1, found) + "', but '" + std::string(1, c) + "' expected").c_str()); else - quit(_pe, ("Unexpected character " + ("#" + vtos(int(found))) + ", but '" + std::string(1, c) + - "' expected").c_str()); + quit(_pe, ("Unexpected character " + ("#" + vtos(int(found))) + ", but '" + std::string(1, c) + "' expected").c_str()); } return found; } -char InStream::readSpace() { +char InStream::readSpace() +{ return readChar(' '); } -void InStream::unreadChar(char c) { +void InStream::unreadChar(char c) +{ reader->unreadChar(c); } -void InStream::skipChar() { +void InStream::skipChar() +{ reader->skipChar(); } -void InStream::skipBlanks() { +void InStream::skipBlanks() +{ while (isBlanks(reader->curChar())) reader->skipChar(); } -std::string InStream::readWord() { +std::string InStream::readWord() +{ readWordTo(_tmpReadToken); return _tmpReadToken; } -void InStream::readWordTo(std::string &result) { +void InStream::readWordTo(std::string& result) +{ if (!strict) skipBlanks(); - lastLine = reader->getLine(); int cur = reader->nextChar(); - if (cur == EOFC) + if (cur == EOF) quit(_unexpected_eof, "Unexpected end of file - token expected"); if (isBlanks(cur)) @@ -2925,15 +2255,9 @@ void InStream::readWordTo(std::string &result) { result.clear(); - while (!(isBlanks(cur) || cur == EOFC)) { + while (!(isBlanks(cur) || cur == EOF)) + { result += char(cur); - - // You can change maxTokenLength. - // Example: 'inf.maxTokenLength = 128 * 1024 * 1024;'. - if (result.length() > maxTokenLength) - quitf(_pe, "Length of token exceeds %d, token is '%s...'", int(maxTokenLength), - __testlib_part(result).c_str()); - cur = reader->nextChar(); } @@ -2943,143 +2267,84 @@ void InStream::readWordTo(std::string &result) { quit(_unexpected_eof, "Unexpected end of file or white-space - token expected"); } -std::string InStream::readToken() { +std::string InStream::readToken() +{ return readWord(); } -void InStream::readTokenTo(std::string &result) { +void InStream::readTokenTo(std::string& result) +{ readWordTo(result); } -static std::string __testlib_part(const std::string &s) { - std::string t; - for (size_t i = 0; i < s.length(); i++) - if (s[i] != '\0') - t += s[i]; - else - t += '~'; - if (t.length() <= 64) - return t; +static std::string __testlib_part(const std::string& s) +{ + if (s.length() <= 64) + return s; else - return t.substr(0, 30) + "..." + t.substr(s.length() - 31, 31); + return s.substr(0, 30) + "..." + s.substr(s.length() - 31, 31); } -#define __testlib_readMany(readMany, readOne, typeName, space) \ - if (size < 0) \ - quit(_fail, #readMany ": size should be non-negative."); \ - if (size > 100000000) \ - quit(_fail, #readMany ": size should be at most 100000000."); \ - \ - std::vector result(size); \ - readManyIteration = indexBase; \ - \ - for (int i = 0; i < size; i++) \ - { \ - result[i] = readOne; \ - readManyIteration++; \ - if (strict && space && i + 1 < size) \ - readSpace(); \ - } \ - \ - readManyIteration = NO_INDEX; \ - return result; \ - - -std::string InStream::readWord(const pattern &p, const std::string &variableName) { +std::string InStream::readWord(const pattern& p, const std::string& variableName) +{ readWordTo(_tmpReadToken); - if (!p.matches(_tmpReadToken)) { - if (readManyIteration == NO_INDEX) { - if (variableName.empty()) - quit(_wa, - ("Token \"" + __testlib_part(_tmpReadToken) + "\" doesn't correspond to pattern \"" + p.src() + - "\"").c_str()); - else - quit(_wa, ("Token parameter [name=" + variableName + "] equals to \"" + __testlib_part(_tmpReadToken) + - "\", doesn't correspond to pattern \"" + p.src() + "\"").c_str()); - } else { - if (variableName.empty()) - quit(_wa, ("Token element [index=" + vtos(readManyIteration) + "] equals to \"" + - __testlib_part(_tmpReadToken) + "\" doesn't correspond to pattern \"" + p.src() + - "\"").c_str()); - else - quit(_wa, ("Token element " + variableName + "[" + vtos(readManyIteration) + "] equals to \"" + - __testlib_part(_tmpReadToken) + "\", doesn't correspond to pattern \"" + p.src() + - "\"").c_str()); - } + if (!p.matches(_tmpReadToken)) + { + if (variableName.empty()) + quit(_wa, ("Token \"" + __testlib_part(_tmpReadToken) + "\" doesn't correspond to pattern \"" + p.src() + "\"").c_str()); + else + quit(_wa, ("Token parameter [name=" + variableName + "] equals to \"" + __testlib_part(_tmpReadToken) + "\", doesn't correspond to pattern \"" + p.src() + "\"").c_str()); } return _tmpReadToken; } -std::vector -InStream::readWords(int size, const pattern &p, const std::string &variablesName, int indexBase) { - __testlib_readMany(readWords, readWord(p, variablesName), std::string, true); -} - -std::vector InStream::readWords(int size, int indexBase) { - __testlib_readMany(readWords, readWord(), std::string, true); -} - -std::string InStream::readWord(const std::string &ptrn, const std::string &variableName) { +std::string InStream::readWord(const std::string& ptrn, const std::string& variableName) +{ return readWord(pattern(ptrn), variableName); } -std::vector -InStream::readWords(int size, const std::string &ptrn, const std::string &variablesName, int indexBase) { - pattern p(ptrn); - __testlib_readMany(readWords, readWord(p, variablesName), std::string, true); -} - -std::string InStream::readToken(const pattern &p, const std::string &variableName) { +std::string InStream::readToken(const pattern& p, const std::string& variableName) +{ return readWord(p, variableName); } -std::vector -InStream::readTokens(int size, const pattern &p, const std::string &variablesName, int indexBase) { - __testlib_readMany(readTokens, readToken(p, variablesName), std::string, true); -} - -std::vector InStream::readTokens(int size, int indexBase) { - __testlib_readMany(readTokens, readToken(), std::string, true); -} - -std::string InStream::readToken(const std::string &ptrn, const std::string &variableName) { +std::string InStream::readToken(const std::string& ptrn, const std::string& variableName) +{ return readWord(ptrn, variableName); } -std::vector -InStream::readTokens(int size, const std::string &ptrn, const std::string &variablesName, int indexBase) { - pattern p(ptrn); - __testlib_readMany(readTokens, readWord(p, variablesName), std::string, true); -} - -void InStream::readWordTo(std::string &result, const pattern &p, const std::string &variableName) { +void InStream::readWordTo(std::string& result, const pattern& p, const std::string& variableName) +{ readWordTo(result); - if (!p.matches(result)) { + if (!p.matches(result)) + { if (variableName.empty()) - quit(_wa, ("Token \"" + __testlib_part(result) + "\" doesn't correspond to pattern \"" + p.src() + - "\"").c_str()); + quit(_wa, ("Token \"" + __testlib_part(result) + "\" doesn't correspond to pattern \"" + p.src() + "\"").c_str()); else - quit(_wa, ("Token parameter [name=" + variableName + "] equals to \"" + __testlib_part(result) + - "\", doesn't correspond to pattern \"" + p.src() + "\"").c_str()); + quit(_wa, ("Token parameter [name=" + variableName + "] equals to \"" + __testlib_part(result) + "\", doesn't correspond to pattern \"" + p.src() + "\"").c_str()); } } -void InStream::readWordTo(std::string &result, const std::string &ptrn, const std::string &variableName) { +void InStream::readWordTo(std::string& result, const std::string& ptrn, const std::string& variableName) +{ return readWordTo(result, pattern(ptrn), variableName); } -void InStream::readTokenTo(std::string &result, const pattern &p, const std::string &variableName) { +void InStream::readTokenTo(std::string& result, const pattern& p, const std::string& variableName) +{ return readWordTo(result, p, variableName); } -void InStream::readTokenTo(std::string &result, const std::string &ptrn, const std::string &variableName) { +void InStream::readTokenTo(std::string& result, const std::string& ptrn, const std::string& variableName) +{ return readWordTo(result, ptrn, variableName); } #ifdef __GNUC__ __attribute__((pure)) #endif -static inline bool equals(long long integer, const char *s) { +static inline bool equals(long long integer, const char* s) +{ if (integer == LLONG_MIN) return strcmp(s, "-9223372036854775808") == 0; @@ -3100,7 +2365,8 @@ static inline bool equals(long long integer, const char *s) { if (length == 0) return false; - while (integer > 0) { + while (integer > 0) + { int digit = int(integer % 10); if (s[length - 1] != '0' + digit) @@ -3113,35 +2379,8 @@ static inline bool equals(long long integer, const char *s) { return length == 0; } -#ifdef __GNUC__ -__attribute__((pure)) -#endif -static inline bool equals(unsigned long long integer, const char *s) { - if (integer == ULLONG_MAX) - return strcmp(s, "18446744073709551615") == 0; - - if (integer == 0ULL) - return strcmp(s, "0") == 0; - - size_t length = strlen(s); - - if (length == 0) - return false; - - while (integer > 0) { - int digit = int(integer % 10); - - if (s[length - 1] != '0' + digit) - return false; - - length--; - integer /= 10; - } - - return length == 0; -} - -static inline double stringToDouble(InStream &in, const char *buffer) { +static inline double stringToDouble(InStream& in, const char* buffer) +{ double retval; size_t length = strlen(buffer); @@ -3152,10 +2391,12 @@ static inline double stringToDouble(InStream &in, const char *buffer) { int digitCount = 0; int eCount = 0; - for (size_t i = 0; i < length; i++) { + for (size_t i = 0; i < length; i++) + { if (('0' <= buffer[i] && buffer[i] <= '9') || buffer[i] == '.' - || buffer[i] == 'e' || buffer[i] == 'E' - || buffer[i] == '-' || buffer[i] == '+') { + || buffer[i] == 'e' || buffer[i] == 'E' + || buffer[i] == '-' || buffer[i] == '+') + { if ('0' <= buffer[i] && buffer[i] <= '9') digitCount++; if (buffer[i] == 'e' || buffer[i] == 'E') @@ -3166,7 +2407,8 @@ static inline double stringToDouble(InStream &in, const char *buffer) { plusCount++; if (buffer[i] == '.') decimalPointCount++; - } else + } + else in.quit(_pe, ("Expected double, but \"" + __testlib_part(buffer) + "\" found").c_str()); } @@ -3174,35 +2416,28 @@ static inline double stringToDouble(InStream &in, const char *buffer) { if (digitCount == 0 || minusCount > 2 || plusCount > 2 || decimalPointCount > 1 || eCount > 1) in.quit(_pe, ("Expected double, but \"" + __testlib_part(buffer) + "\" found").c_str()); - char *suffix = new char[length + 1]; - std::memset(suffix, 0, length + 1); + char* suffix = new char[length + 1]; int scanned = std::sscanf(buffer, "%lf%s", &retval, suffix); bool empty = strlen(suffix) == 0; delete[] suffix; - if (scanned == 1 || (scanned == 2 && empty)) { - if (__testlib_isNaN(retval)) + if (scanned == 1 || (scanned == 2 && empty)) + { + if (__testlib_isNaN(retval) || __testlib_isInfinite(retval)) in.quit(_pe, ("Expected double, but \"" + __testlib_part(buffer) + "\" found").c_str()); return retval; - } else + } + else in.quit(_pe, ("Expected double, but \"" + __testlib_part(buffer) + "\" found").c_str()); } -static inline double stringToDouble(InStream &in, const std::string& buffer) { - for (size_t i = 0; i < buffer.length(); i++) - if (buffer[i] == '\0') - in.quit(_pe, ("Expected double, but \"" + __testlib_part(buffer) + "\" found (it contains \\0)").c_str()); - return stringToDouble(in, buffer.c_str()); -} - -static inline double stringToStrictDouble(InStream &in, const char *buffer, - int minAfterPointDigitCount, int maxAfterPointDigitCount) { +static inline double stringToStrictDouble(InStream& in, const char* buffer, int minAfterPointDigitCount, int maxAfterPointDigitCount) +{ if (minAfterPointDigitCount < 0) in.quit(_fail, "stringToStrictDouble: minAfterPointDigitCount should be non-negative."); - + if (minAfterPointDigitCount > maxAfterPointDigitCount) - in.quit(_fail, - "stringToStrictDouble: minAfterPointDigitCount should be less or equal to maxAfterPointDigitCount."); + in.quit(_fail, "stringToStrictDouble: minAfterPointDigitCount should be less or equal to maxAfterPointDigitCount."); double retval; @@ -3214,9 +2449,11 @@ static inline double stringToStrictDouble(InStream &in, const char *buffer, if (buffer[0] != '-' && (buffer[0] < '0' || buffer[0] > '9')) in.quit(_pe, ("Expected strict double, but \"" + __testlib_part(buffer) + "\" found").c_str()); - int pointPos = -1; - for (size_t i = 1; i + 1 < length; i++) { - if (buffer[i] == '.') { + int pointPos = -1; + for (size_t i = 1; i + 1 < length; i++) + { + if (buffer[i] == '.') + { if (pointPos > -1) in.quit(_pe, ("Expected strict double, but \"" + __testlib_part(buffer) + "\" found").c_str()); pointPos = int(i); @@ -3231,57 +2468,50 @@ static inline double stringToStrictDouble(InStream &in, const char *buffer, int afterDigitsCount = (pointPos == -1 ? 0 : int(length) - pointPos - 1); if (afterDigitsCount < minAfterPointDigitCount || afterDigitsCount > maxAfterPointDigitCount) in.quit(_pe, ("Expected strict double with number of digits after point in range [" - + vtos(minAfterPointDigitCount) - + "," - + vtos(maxAfterPointDigitCount) - + "], but \"" + __testlib_part(buffer) + "\" found").c_str() + + vtos(minAfterPointDigitCount) + + "," + + vtos(maxAfterPointDigitCount) + + "], but \"" + __testlib_part(buffer) + "\" found").c_str() ); int firstDigitPos = -1; for (size_t i = 0; i < length; i++) - if (buffer[i] >= '0' && buffer[i] <= '9') { + if (buffer[i] >= '0' && buffer[i] <= '9') + { firstDigitPos = int(i); break; } - if (firstDigitPos > 1 || firstDigitPos == -1) + if (firstDigitPos > 1 || firstDigitPos == -1) in.quit(_pe, ("Expected strict double, but \"" + __testlib_part(buffer) + "\" found").c_str()); if (buffer[firstDigitPos] == '0' && firstDigitPos + 1 < int(length) - && buffer[firstDigitPos + 1] >= '0' && buffer[firstDigitPos + 1] <= '9') + && buffer[firstDigitPos + 1] >= '0' && buffer[firstDigitPos + 1] <= '9') in.quit(_pe, ("Expected strict double, but \"" + __testlib_part(buffer) + "\" found").c_str()); - char *suffix = new char[length + 1]; - std::memset(suffix, 0, length + 1); + char* suffix = new char[length + 1]; int scanned = std::sscanf(buffer, "%lf%s", &retval, suffix); bool empty = strlen(suffix) == 0; delete[] suffix; - if (scanned == 1 || (scanned == 2 && empty)) { + if (scanned == 1 || (scanned == 2 && empty)) + { if (__testlib_isNaN(retval) || __testlib_isInfinite(retval)) in.quit(_pe, ("Expected double, but \"" + __testlib_part(buffer) + "\" found").c_str()); - if (buffer[0] == '-' && retval >= 0) - in.quit(_pe, ("Redundant minus in \"" + __testlib_part(buffer) + "\" found").c_str()); return retval; - } else + } + else in.quit(_pe, ("Expected double, but \"" + __testlib_part(buffer) + "\" found").c_str()); } -static inline double stringToStrictDouble(InStream &in, const std::string& buffer, - int minAfterPointDigitCount, int maxAfterPointDigitCount) { - for (size_t i = 0; i < buffer.length(); i++) - if (buffer[i] == '\0') - in.quit(_pe, ("Expected double, but \"" + __testlib_part(buffer) + "\" found (it contains \\0)").c_str()); - return stringToStrictDouble(in, buffer.c_str(), minAfterPointDigitCount, maxAfterPointDigitCount); -} - -static inline long long stringToLongLong(InStream &in, const char *buffer) { +static inline long long stringToLongLong(InStream& in, const char* buffer) +{ if (strcmp(buffer, "-9223372036854775808") == 0) return LLONG_MIN; bool minus = false; size_t length = strlen(buffer); - + if (length > 1 && buffer[0] == '-') minus = true; @@ -3291,9 +2521,10 @@ static inline long long stringToLongLong(InStream &in, const char *buffer) { long long retval = 0LL; int zeroes = 0; - bool processingZeroes = true; - - for (int i = (minus ? 1 : 0); i < int(length); i++) { + int processingZeroes = true; + + for (int i = (minus ? 1 : 0); i < int(length); i++) + { if (buffer[i] == '0' && processingZeroes) zeroes++; else @@ -3306,7 +2537,7 @@ static inline long long stringToLongLong(InStream &in, const char *buffer) { if (retval < 0) in.quit(_pe, ("Expected integer, but \"" + __testlib_part(buffer) + "\" found").c_str()); - + if ((zeroes > 0 && (retval != 0 || minus)) || zeroes > 1) in.quit(_pe, ("Expected integer, but \"" + __testlib_part(buffer) + "\" found").c_str()); @@ -3321,392 +2552,199 @@ static inline long long stringToLongLong(InStream &in, const char *buffer) { in.quit(_pe, ("Expected int64, but \"" + __testlib_part(buffer) + "\" found").c_str()); } -static inline long long stringToLongLong(InStream &in, const std::string& buffer) { - for (size_t i = 0; i < buffer.length(); i++) - if (buffer[i] == '\0') - in.quit(_pe, ("Expected integer, but \"" + __testlib_part(buffer) + "\" found (it contains \\0)").c_str()); - return stringToLongLong(in, buffer.c_str()); -} - -static inline unsigned long long stringToUnsignedLongLong(InStream &in, const char *buffer) { - size_t length = strlen(buffer); - - if (length > 20) - in.quit(_pe, ("Expected unsigned integer, but \"" + __testlib_part(buffer) + "\" found").c_str()); - if (length > 1 && buffer[0] == '0') - in.quit(_pe, ("Expected unsigned integer, but \"" + __testlib_part(buffer) + "\" found").c_str()); - - unsigned long long retval = 0LL; - for (int i = 0; i < int(length); i++) { - if (buffer[i] < '0' || buffer[i] > '9') - in.quit(_pe, ("Expected unsigned integer, but \"" + __testlib_part(buffer) + "\" found").c_str()); - retval = retval * 10 + (buffer[i] - '0'); - } - - if (length < 19) - return retval; - - if (length == 20 && strcmp(buffer, "18446744073709551615") > 0) - in.quit(_pe, ("Expected unsigned int64, but \"" + __testlib_part(buffer) + "\" found").c_str()); - - if (equals(retval, buffer)) - return retval; - else - in.quit(_pe, ("Expected unsigned int64, but \"" + __testlib_part(buffer) + "\" found").c_str()); -} - -static inline long long stringToUnsignedLongLong(InStream &in, const std::string& buffer) { - for (size_t i = 0; i < buffer.length(); i++) - if (buffer[i] == '\0') - in.quit(_pe, ("Expected unsigned integer, but \"" + __testlib_part(buffer) + "\" found (it contains \\0)").c_str()); - return stringToUnsignedLongLong(in, buffer.c_str()); -} - -int InStream::readInteger() { +int InStream::readInteger() +{ if (!strict && seekEof()) quit(_unexpected_eof, "Unexpected end of file - int32 expected"); readWordTo(_tmpReadToken); - - long long value = stringToLongLong(*this, _tmpReadToken); + + long long value = stringToLongLong(*this, _tmpReadToken.c_str()); if (value < INT_MIN || value > INT_MAX) quit(_pe, ("Expected int32, but \"" + __testlib_part(_tmpReadToken) + "\" found").c_str()); - + return int(value); } -long long InStream::readLong() { +long long InStream::readLong() +{ if (!strict && seekEof()) quit(_unexpected_eof, "Unexpected end of file - int64 expected"); readWordTo(_tmpReadToken); - - return stringToLongLong(*this, _tmpReadToken); + return stringToLongLong(*this, _tmpReadToken.c_str()); } -unsigned long long InStream::readUnsignedLong() { - if (!strict && seekEof()) - quit(_unexpected_eof, "Unexpected end of file - int64 expected"); - - readWordTo(_tmpReadToken); - - return stringToUnsignedLongLong(*this, _tmpReadToken); -} - -long long InStream::readLong(long long minv, long long maxv, const std::string &variableName) { +long long InStream::readLong(long long minv, long long maxv, const std::string& variableName) +{ long long result = readLong(); - if (result < minv || result > maxv) { - if (readManyIteration == NO_INDEX) { - if (variableName.empty()) - quit(_wa, ("Integer " + vtos(result) + " violates the range [" + toHumanReadableString(minv) + ", " + toHumanReadableString(maxv) + - "]").c_str()); - else - quit(_wa, ("Integer parameter [name=" + std::string(variableName) + "] equals to " + vtos(result) + - ", violates the range [" + toHumanReadableString(minv) + ", " + toHumanReadableString(maxv) + "]").c_str()); - } else { - if (variableName.empty()) - quit(_wa, ("Integer element [index=" + vtos(readManyIteration) + "] equals to " + vtos(result) + - ", violates the range [" + toHumanReadableString(minv) + ", " + toHumanReadableString(maxv) + "]").c_str()); - else - quit(_wa, - ("Integer element " + std::string(variableName) + "[" + vtos(readManyIteration) + "] equals to " + - vtos(result) + ", violates the range [" + toHumanReadableString(minv) + ", " + toHumanReadableString(maxv) + "]").c_str()); - } + if (result < minv || result > maxv) + { + if (variableName.empty()) + quit(_wa, ("Integer " + vtos(result) + " violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str()); + else + quit(_wa, ("Integer parameter [name=" + variableName + "] equals to " + vtos(result) + ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str()); } - if (strict && !variableName.empty()) - validator.addBoundsHit(variableName, ValidatorBoundsHit(minv == result, maxv == result)); - return result; } -std::vector -InStream::readLongs(int size, long long minv, long long maxv, const std::string &variablesName, int indexBase) { - __testlib_readMany(readLongs, readLong(minv, maxv, variablesName), long long, true) -} - -std::vector InStream::readLongs(int size, int indexBase) { - __testlib_readMany(readLongs, readLong(), long long, true) -} - -unsigned long long -InStream::readUnsignedLong(unsigned long long minv, unsigned long long maxv, const std::string &variableName) { - unsigned long long result = readUnsignedLong(); - - if (result < minv || result > maxv) { - if (readManyIteration == NO_INDEX) { - if (variableName.empty()) - quit(_wa, - ("Unsigned integer " + vtos(result) + " violates the range [" + toHumanReadableString(minv) + ", " + toHumanReadableString(maxv) + - "]").c_str()); - else - quit(_wa, - ("Unsigned integer parameter [name=" + std::string(variableName) + "] equals to " + vtos(result) + - ", violates the range [" + toHumanReadableString(minv) + ", " + toHumanReadableString(maxv) + "]").c_str()); - } else { - if (variableName.empty()) - quit(_wa, - ("Unsigned integer element [index=" + vtos(readManyIteration) + "] equals to " + vtos(result) + - ", violates the range [" + toHumanReadableString(minv) + ", " + toHumanReadableString(maxv) + "]").c_str()); - else - quit(_wa, ("Unsigned integer element " + std::string(variableName) + "[" + vtos(readManyIteration) + - "] equals to " + vtos(result) + ", violates the range [" + toHumanReadableString(minv) + ", " + toHumanReadableString(maxv) + - "]").c_str()); - } - } - - if (strict && !variableName.empty()) - validator.addBoundsHit(variableName, ValidatorBoundsHit(minv == result, maxv == result)); - - return result; -} - -std::vector InStream::readUnsignedLongs(int size, unsigned long long minv, unsigned long long maxv, - const std::string &variablesName, int indexBase) { - __testlib_readMany(readUnsignedLongs, readUnsignedLong(minv, maxv, variablesName), unsigned long long, true) -} - -std::vector InStream::readUnsignedLongs(int size, int indexBase) { - __testlib_readMany(readUnsignedLongs, readUnsignedLong(), unsigned long long, true) -} - -unsigned long long -InStream::readLong(unsigned long long minv, unsigned long long maxv, const std::string &variableName) { - return readUnsignedLong(minv, maxv, variableName); -} - -int InStream::readInt() { +int InStream::readInt() +{ return readInteger(); } -int InStream::readInt(int minv, int maxv, const std::string &variableName) { +int InStream::readInt(int minv, int maxv, const std::string &variableName) +{ int result = readInt(); - if (result < minv || result > maxv) { - if (readManyIteration == NO_INDEX) { - if (variableName.empty()) - quit(_wa, ("Integer " + vtos(result) + " violates the range [" + toHumanReadableString(minv) + ", " + toHumanReadableString(maxv) + - "]").c_str()); - else - quit(_wa, ("Integer parameter [name=" + std::string(variableName) + "] equals to " + vtos(result) + - ", violates the range [" + toHumanReadableString(minv) + ", " + toHumanReadableString(maxv) + "]").c_str()); - } else { - if (variableName.empty()) - quit(_wa, ("Integer element [index=" + vtos(readManyIteration) + "] equals to " + vtos(result) + - ", violates the range [" + toHumanReadableString(minv) + ", " + toHumanReadableString(maxv) + "]").c_str()); - else - quit(_wa, - ("Integer element " + std::string(variableName) + "[" + vtos(readManyIteration) + "] equals to " + - vtos(result) + ", violates the range [" + toHumanReadableString(minv) + ", " + toHumanReadableString(maxv) + "]").c_str()); - } - } - - if (strict && !variableName.empty()) - validator.addBoundsHit(variableName, ValidatorBoundsHit(minv == result, maxv == result)); + if (result < minv || result > maxv) + { + if (variableName.empty()) + quit(_wa, ("Integer " + vtos(result) + " violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str()); + else + quit(_wa, ("Integer parameter [name=" + std::string(variableName) + "] equals to " + vtos(result) + ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str()); + } return result; } -int InStream::readInteger(int minv, int maxv, const std::string &variableName) { - return readInt(minv, maxv, variableName); +int InStream::readInteger(int minv, int maxv, const std::string& variableName) +{ + return readInt(minv, maxv, variableName.c_str()); } -std::vector InStream::readInts(int size, int minv, int maxv, const std::string &variablesName, int indexBase) { - __testlib_readMany(readInts, readInt(minv, maxv, variablesName), int, true) -} - -std::vector InStream::readInts(int size, int indexBase) { - __testlib_readMany(readInts, readInt(), int, true) -} - -std::vector InStream::readIntegers(int size, int minv, int maxv, const std::string &variablesName, int indexBase) { - __testlib_readMany(readIntegers, readInt(minv, maxv, variablesName), int, true) -} - -std::vector InStream::readIntegers(int size, int indexBase) { - __testlib_readMany(readIntegers, readInt(), int, true) -} - -double InStream::readReal() { +double InStream::readReal() +{ if (!strict && seekEof()) quit(_unexpected_eof, "Unexpected end of file - double expected"); - return stringToDouble(*this, readWord()); + return stringToDouble(*this, readWord().c_str()); } -double InStream::readDouble() { +double InStream::readDouble() +{ return readReal(); } -double InStream::readReal(double minv, double maxv, const std::string &variableName) { +double InStream::readReal(double minv, double maxv, const std::string& variableName) +{ double result = readReal(); - if (result < minv || result > maxv) { - if (readManyIteration == NO_INDEX) { - if (variableName.empty()) - quit(_wa, ("Double " + vtos(result) + " violates the range [" + vtos(minv) + ", " + vtos(maxv) + - "]").c_str()); - else - quit(_wa, ("Double parameter [name=" + std::string(variableName) + "] equals to " + vtos(result) + - ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str()); - } else { - if (variableName.empty()) - quit(_wa, ("Double element [index=" + vtos(readManyIteration) + "] equals to " + vtos(result) + - ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str()); - else - quit(_wa, - ("Double element " + std::string(variableName) + "[" + vtos(readManyIteration) + "] equals to " + - vtos(result) + ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str()); - } + if (result < minv || result > maxv) + { + if (variableName.empty()) + quit(_wa, ("Double " + vtos(result) + " violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str()); + else + quit(_wa, ("Double parameter [name=" + variableName + "] equals to " + vtos(result) + ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str()); } - if (strict && !variableName.empty()) - validator.addBoundsHit(variableName, ValidatorBoundsHit( - doubleDelta(minv, result) < ValidatorBoundsHit::EPS, - doubleDelta(maxv, result) < ValidatorBoundsHit::EPS - )); - return result; } -std::vector -InStream::readReals(int size, double minv, double maxv, const std::string &variablesName, int indexBase) { - __testlib_readMany(readReals, readReal(minv, maxv, variablesName), double, true) -} - -std::vector InStream::readReals(int size, int indexBase) { - __testlib_readMany(readReals, readReal(), double, true) -} - -double InStream::readDouble(double minv, double maxv, const std::string &variableName) { +double InStream::readDouble(double minv, double maxv, const std::string& variableName) +{ return readReal(minv, maxv, variableName); -} - -std::vector -InStream::readDoubles(int size, double minv, double maxv, const std::string &variablesName, int indexBase) { - __testlib_readMany(readDoubles, readDouble(minv, maxv, variablesName), double, true) -} - -std::vector InStream::readDoubles(int size, int indexBase) { - __testlib_readMany(readDoubles, readDouble(), double, true) -} +} double InStream::readStrictReal(double minv, double maxv, - int minAfterPointDigitCount, int maxAfterPointDigitCount, - const std::string &variableName) { + int minAfterPointDigitCount, int maxAfterPointDigitCount, + const std::string& variableName) +{ if (!strict && seekEof()) quit(_unexpected_eof, "Unexpected end of file - strict double expected"); - double result = stringToStrictDouble(*this, readWord(), minAfterPointDigitCount, maxAfterPointDigitCount); + double result = stringToStrictDouble(*this, readWord().c_str(), + minAfterPointDigitCount, maxAfterPointDigitCount); - if (result < minv || result > maxv) { - if (readManyIteration == NO_INDEX) { - if (variableName.empty()) - quit(_wa, ("Strict double " + vtos(result) + " violates the range [" + vtos(minv) + ", " + vtos(maxv) + - "]").c_str()); - else - quit(_wa, - ("Strict double parameter [name=" + std::string(variableName) + "] equals to " + vtos(result) + - ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str()); - } else { - if (variableName.empty()) - quit(_wa, ("Strict double element [index=" + vtos(readManyIteration) + "] equals to " + vtos(result) + - ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str()); - else - quit(_wa, ("Strict double element " + std::string(variableName) + "[" + vtos(readManyIteration) + - "] equals to " + vtos(result) + ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + - "]").c_str()); - } + if (result < minv || result > maxv) + { + if (variableName.empty()) + quit(_wa, ("Strict double " + vtos(result) + " violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str()); + else + quit(_wa, ("Strict double parameter [name=" + variableName + "] equals to " + vtos(result) + ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str()); } - if (strict && !variableName.empty()) - validator.addBoundsHit(variableName, ValidatorBoundsHit( - doubleDelta(minv, result) < ValidatorBoundsHit::EPS, - doubleDelta(maxv, result) < ValidatorBoundsHit::EPS - )); - return result; } -std::vector InStream::readStrictReals(int size, double minv, double maxv, - int minAfterPointDigitCount, int maxAfterPointDigitCount, - const std::string &variablesName, int indexBase) { - __testlib_readMany(readStrictReals, - readStrictReal(minv, maxv, minAfterPointDigitCount, maxAfterPointDigitCount, variablesName), - double, true) -} - double InStream::readStrictDouble(double minv, double maxv, - int minAfterPointDigitCount, int maxAfterPointDigitCount, - const std::string &variableName) { + int minAfterPointDigitCount, int maxAfterPointDigitCount, + const std::string& variableName) +{ return readStrictReal(minv, maxv, - minAfterPointDigitCount, maxAfterPointDigitCount, - variableName); + minAfterPointDigitCount, maxAfterPointDigitCount, + variableName); } -std::vector InStream::readStrictDoubles(int size, double minv, double maxv, - int minAfterPointDigitCount, int maxAfterPointDigitCount, - const std::string &variablesName, int indexBase) { - __testlib_readMany(readStrictDoubles, - readStrictDouble(minv, maxv, minAfterPointDigitCount, maxAfterPointDigitCount, variablesName), - double, true) -} - -bool InStream::eof() { - if (!strict && NULL == reader) +bool InStream::eof() +{ + if (!strict && NULL == file) return true; return reader->eof(); } -bool InStream::seekEof() { - if (!strict && NULL == reader) +bool InStream::seekEof() +{ + if (NULL == file) return true; skipBlanks(); return eof(); } -bool InStream::eoln() { - if (!strict && NULL == reader) +bool InStream::eoln() +{ + if (!strict && NULL == file) return true; int c = reader->nextChar(); - if (!strict) { - if (c == EOFC) + if (!strict) + { + if (c == EOF) return true; - if (c == CR) { + if (c == CR) + { c = reader->nextChar(); - if (c != LF) { + if (c != LF) + { reader->unreadChar(c); reader->unreadChar(CR); return false; - } else + } + else return true; } - + if (c == LF) return true; reader->unreadChar(c); return false; - } else { + } + else + { bool returnCr = false; -#if (defined(ON_WINDOWS) && !defined(FOR_LINUX)) || defined(FOR_WINDOWS) - if (c != CR) { +#ifdef CR_MUST_IN_EOL + if (c != CR) + { reader->unreadChar(c); return false; - } else { + } + else + { if (!returnCr) returnCr = true; c = reader->nextChar(); } -#endif - if (c != LF) { +#endif + if (c != LF) + { reader->unreadChar(c); if (returnCr) reader->unreadChar(CR); @@ -3717,14 +2755,14 @@ bool InStream::eoln() { } } -void InStream::readEoln() { - lastLine = reader->getLine(); +void InStream::readEoln() +{ if (!eoln()) quit(_pe, "Expected EOLN"); } -void InStream::readEof() { - lastLine = reader->getLine(); +void InStream::readEof() +{ if (!eof()) quit(_pe, "Expected EOF"); @@ -3732,44 +2770,45 @@ void InStream::readEof() { testlibFinalizeGuard.readEofCount++; } -bool InStream::seekEoln() { - if (!strict && NULL == reader) +bool InStream::seekEoln() +{ + if (NULL == file) return true; - + int cur; - do { + do + { cur = reader->nextChar(); - } while (cur == SPACE || cur == TAB); + } + while (cur == SPACE || cur == TAB); reader->unreadChar(cur); return eoln(); } -void InStream::nextLine() { +void InStream::nextLine() +{ readLine(); } -void InStream::readStringTo(std::string &result) { - if (NULL == reader) +void InStream::readStringTo(std::string& result) +{ + if (NULL == file) quit(_pe, "Expected line"); result.clear(); + int cur; - for (;;) { - int cur = reader->curChar(); + for (;;) + { + cur = reader->curChar(); - if (cur == LF || cur == EOFC) + if (isEoln(cur)) break; - if (cur == CR) { - cur = reader->nextChar(); - if (reader->curChar() == LF) { - reader->unreadChar(cur); - break; - } - } + if (cur == EOF) + break; - lastLine = reader->getLine(); result += char(reader->nextChar()); } @@ -3779,136 +2818,97 @@ void InStream::readStringTo(std::string &result) { eoln(); } -std::string InStream::readString() { +std::string InStream::readString() +{ readStringTo(_tmpReadToken); return _tmpReadToken; } -std::vector InStream::readStrings(int size, int indexBase) { - __testlib_readMany(readStrings, readString(), std::string, false) -} - -void InStream::readStringTo(std::string &result, const pattern &p, const std::string &variableName) { +void InStream::readStringTo(std::string& result, const pattern& p, const std::string& variableName) +{ readStringTo(result); - if (!p.matches(result)) { - if (readManyIteration == NO_INDEX) { - if (variableName.empty()) - quit(_wa, ("Line \"" + __testlib_part(result) + "\" doesn't correspond to pattern \"" + p.src() + - "\"").c_str()); - else - quit(_wa, ("Line [name=" + variableName + "] equals to \"" + __testlib_part(result) + - "\", doesn't correspond to pattern \"" + p.src() + "\"").c_str()); - } else { - if (variableName.empty()) - quit(_wa, - ("Line element [index=" + vtos(readManyIteration) + "] equals to \"" + __testlib_part(result) + - "\" doesn't correspond to pattern \"" + p.src() + "\"").c_str()); - else - quit(_wa, - ("Line element " + std::string(variableName) + "[" + vtos(readManyIteration) + "] equals to \"" + - __testlib_part(result) + "\", doesn't correspond to pattern \"" + p.src() + "\"").c_str()); - } + if (!p.matches(result)) + { + if (variableName.empty()) + quit(_wa, ("Line \"" + __testlib_part(result) + "\" doesn't correspond to pattern \"" + p.src() + "\"").c_str()); + else + quit(_wa, ("Line [name=" + variableName + "] equals to \"" + __testlib_part(result) + "\", doesn't correspond to pattern \"" + p.src() + "\"").c_str()); } } -void InStream::readStringTo(std::string &result, const std::string &ptrn, const std::string &variableName) { +void InStream::readStringTo(std::string& result, const std::string& ptrn, const std::string& variableName) +{ readStringTo(result, pattern(ptrn), variableName); } -std::string InStream::readString(const pattern &p, const std::string &variableName) { +std::string InStream::readString(const pattern& p, const std::string& variableName) +{ readStringTo(_tmpReadToken, p, variableName); return _tmpReadToken; } -std::vector -InStream::readStrings(int size, const pattern &p, const std::string &variablesName, int indexBase) { - __testlib_readMany(readStrings, readString(p, variablesName), std::string, false) -} - -std::string InStream::readString(const std::string &ptrn, const std::string &variableName) { +std::string InStream::readString(const std::string& ptrn, const std::string& variableName) +{ readStringTo(_tmpReadToken, ptrn, variableName); return _tmpReadToken; } -std::vector -InStream::readStrings(int size, const std::string &ptrn, const std::string &variablesName, int indexBase) { - pattern p(ptrn); - __testlib_readMany(readStrings, readString(p, variablesName), std::string, false) -} - -void InStream::readLineTo(std::string &result) { +void InStream::readLineTo(std::string& result) +{ readStringTo(result); } -std::string InStream::readLine() { +std::string InStream::readLine() +{ return readString(); } -std::vector InStream::readLines(int size, int indexBase) { - __testlib_readMany(readLines, readString(), std::string, false) -} - -void InStream::readLineTo(std::string &result, const pattern &p, const std::string &variableName) { +void InStream::readLineTo(std::string& result, const pattern& p, const std::string& variableName) +{ readStringTo(result, p, variableName); } -void InStream::readLineTo(std::string &result, const std::string &ptrn, const std::string &variableName) { +void InStream::readLineTo(std::string& result, const std::string& ptrn, const std::string& variableName) +{ readStringTo(result, ptrn, variableName); } -std::string InStream::readLine(const pattern &p, const std::string &variableName) { +std::string InStream::readLine(const pattern& p, const std::string& variableName) +{ return readString(p, variableName); } -std::vector -InStream::readLines(int size, const pattern &p, const std::string &variablesName, int indexBase) { - __testlib_readMany(readLines, readString(p, variablesName), std::string, false) -} - -std::string InStream::readLine(const std::string &ptrn, const std::string &variableName) { +std::string InStream::readLine(const std::string& ptrn, const std::string& variableName) +{ return readString(ptrn, variableName); } -std::vector -InStream::readLines(int size, const std::string &ptrn, const std::string &variablesName, int indexBase) { - pattern p(ptrn); - __testlib_readMany(readLines, readString(p, variablesName), std::string, false) -} - -#ifdef __GNUC__ -__attribute__ ((format (printf, 3, 4))) -#endif -void InStream::ensuref(bool cond, const char *format, ...) { - if (!cond) { - FMT_TO_RESULT(format, format, message); - this->__testlib_ensure(cond, message); - } -} - -void InStream::__testlib_ensure(bool cond, std::string message) { - if (!cond) - this->quit(_wa, message.c_str()); -} - -void InStream::close() { - if (NULL != reader) { - reader->close(); +void InStream::close() +{ + if (opened) + fclose(file); + + if (NULL != reader) + { delete reader; reader = NULL; } - + opened = false; } -NORETURN void quit(TResult result, const std::string &msg) { +NORETURN void quit(TResult result, const std::string& msg) +{ ouf.quit(result, msg.c_str()); } -NORETURN void quit(TResult result, const char *msg) { +NORETURN void quit(TResult result, const char* msg) +{ ouf.quit(result, msg); } -NORETURN void __testlib_quitp(double points, const char *message) { +NORETURN void __testlib_quitp(double points, const char* message) +{ __testlib_points = points; std::string stringPoints = removeDoubleTrailingZeroes(format("%.10f", points)); @@ -3921,49 +2921,27 @@ NORETURN void __testlib_quitp(double points, const char *message) { quit(_points, quitMessage.c_str()); } -NORETURN void __testlib_quitp(int points, const char *message) { - __testlib_points = points; - std::string stringPoints = format("%d", points); - - std::string quitMessage; - if (NULL == message || 0 == strlen(message)) - quitMessage = stringPoints; - else - quitMessage = stringPoints + " " + message; - - quit(_points, quitMessage.c_str()); -} - -NORETURN void quitp(float points, const std::string &message = "") { +NORETURN void quitp(float points, const std::string& message = "") +{ __testlib_quitp(double(points), message.c_str()); } -NORETURN void quitp(double points, const std::string &message = "") { +NORETURN void quitp(double points, const std::string& message = "") +{ __testlib_quitp(points, message.c_str()); } -NORETURN void quitp(long double points, const std::string &message = "") { +NORETURN void quitp(long double points, const std::string& message = "") +{ __testlib_quitp(double(points), message.c_str()); } -NORETURN void quitp(int points, const std::string &message = "") { - __testlib_quitp(points, message.c_str()); -} - -NORETURN void quitpi(const std::string &points_info, const std::string &message = "") { - if (points_info.find(' ') != std::string::npos) - quit(_fail, "Parameter 'points_info' can't contain spaces"); - if (message.empty()) - quit(_points, ("points_info=" + points_info).c_str()); - else - quit(_points, ("points_info=" + points_info + " " + message).c_str()); -} - template #ifdef __GNUC__ __attribute__ ((format (printf, 2, 3))) #endif -NORETURN void quitp(F points, const char *format, ...) { +NORETURN void quitp(F points, const char* format, ...) +{ FMT_TO_RESULT(format, format, message); quitp(points, message); } @@ -3971,7 +2949,8 @@ NORETURN void quitp(F points, const char *format, ...) { #ifdef __GNUC__ __attribute__ ((format (printf, 2, 3))) #endif -NORETURN void quitf(TResult result, const char *format, ...) { +NORETURN void quitf(TResult result, const char* format, ...) +{ FMT_TO_RESULT(format, format, message); quit(result, message); } @@ -3979,34 +2958,39 @@ NORETURN void quitf(TResult result, const char *format, ...) { #ifdef __GNUC__ __attribute__ ((format (printf, 3, 4))) #endif -void quitif(bool condition, TResult result, const char *format, ...) { - if (condition) { +void quitif(bool condition, TResult result, const char* format, ...) +{ + if (condition) + { FMT_TO_RESULT(format, format, message); quit(result, message); } } -NORETURN void __testlib_help() { +NORETURN void __testlib_help() +{ InStream::textColor(InStream::LightCyan); - std::fprintf(stderr, "TESTLIB %s, https://github.com/MikeMirzayanov/testlib/ ", VERSION); - std::fprintf(stderr, "by Mike Mirzayanov, copyright(c) 2005-2020\n"); + std::fprintf(stderr, "TESTLIB %s, http://code.google.com/p/testlib/ ", VERSION); + std::fprintf(stderr, "by Mike Mirzayanov, copyright(c) 2005-2013\n"); std::fprintf(stderr, "Checker name: \"%s\"\n", checkerName.c_str()); InStream::textColor(InStream::LightGray); std::fprintf(stderr, "\n"); std::fprintf(stderr, "Latest features: \n"); - for (size_t i = 0; i < sizeof(latestFeatures) / sizeof(char *); i++) { + for (size_t i = 0; i < sizeof(latestFeatures) / sizeof(char*); i++) + { std::fprintf(stderr, "*) %s\n", latestFeatures[i]); } std::fprintf(stderr, "\n"); std::fprintf(stderr, "Program must be run with the following arguments: \n"); - std::fprintf(stderr, " [--testset testset] [--group group] [ [<-appes>]]\n\n"); + std::fprintf(stderr, " [ [<-appes>]]\n\n"); std::exit(FAIL_EXIT_CODE); } -static void __testlib_ensuresPreconditions() { +static void __testlib_ensuresPreconditions() +{ // testlib assumes: sizeof(int) = 4. __TESTLIB_STATIC_ASSERT(sizeof(int) == 4); @@ -4016,9 +3000,6 @@ static void __testlib_ensuresPreconditions() { // testlib assumes: sizeof(long long) = 8. __TESTLIB_STATIC_ASSERT(sizeof(long long) == 8); - // testlib assumes: sizeof(double) = 8. - __TESTLIB_STATIC_ASSERT(sizeof(double) == 8); - // testlib assumes: no -ffast-math. if (!__testlib_isNaN(+__testlib_nan())) quit(_fail, "Function __testlib_isNaN is not working correctly: possible reason is '-ffast-math'"); @@ -4026,7 +3007,8 @@ static void __testlib_ensuresPreconditions() { quit(_fail, "Function __testlib_isNaN is not working correctly: possible reason is '-ffast-math'"); } -void registerGen(int argc, char *argv[], int randomGeneratorVersion) { +void registerGen(int argc, char* argv[], int randomGeneratorVersion) +{ if (randomGeneratorVersion < 0 || randomGeneratorVersion > 1) quitf(_fail, "Random generator version is expected to be 0 or 1."); random_t::version = randomGeneratorVersion; @@ -4036,10 +3018,6 @@ void registerGen(int argc, char *argv[], int randomGeneratorVersion) { testlibMode = _generator; __testlib_set_binary(stdin); rnd.setSeed(argc, argv); - -#if __cplusplus > 199711L || defined(_MSC_VER) - prepareOpts(argc, argv); -#endif } #ifdef USE_RND_AS_BEFORE_087 @@ -4048,32 +3026,18 @@ void registerGen(int argc, char* argv[]) registerGen(argc, argv, 0); } #else -#ifdef __GNUC__ -#if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 4)) -__attribute__ ((deprecated("Use registerGen(argc, argv, 0) or registerGen(argc, argv, 1)." -" The third parameter stands for the random generator version." -" If you are trying to compile old generator use macro -DUSE_RND_AS_BEFORE_087 or registerGen(argc, argv, 0)." -" Version 1 has been released on Spring, 2013. Use it to write new generators."))) -#else -__attribute__ ((deprecated)) -#endif -#endif -#ifdef _MSC_VER -__declspec(deprecated("Use registerGen(argc, argv, 0) or registerGen(argc, argv, 1)." - " The third parameter stands for the random generator version." - " If you are trying to compile old generator use macro -DUSE_RND_AS_BEFORE_087 or registerGen(argc, argv, 0)." - " Version 1 has been released on Spring, 2013. Use it to write new generators.")) -#endif -void registerGen(int argc, char *argv[]) { +void registerGen(int argc, char* argv[]) +{ std::fprintf(stderr, "Use registerGen(argc, argv, 0) or registerGen(argc, argv, 1)." - " The third parameter stands for the random generator version." - " If you are trying to compile old generator use macro -DUSE_RND_AS_BEFORE_087 or registerGen(argc, argv, 0)." - " Version 1 has been released on Spring, 2013. Use it to write new generators.\n\n"); + " The third parameter stands for the random generator version." + " If you are trying to compile old generator use macro -DUSE_RND_AS_BEFORE_087 or registerGen(argc, argv, 0)." + " Version 1 has been released on Spring, 2013. Use it to write new generators.\n\n"); registerGen(argc, argv, 0); } #endif -void registerInteraction(int argc, char *argv[]) { +void registerInteraction(int argc, char* argv[]) +{ __testlib_ensuresPreconditions(); testlibMode = _interactor; @@ -4081,50 +3045,59 @@ void registerInteraction(int argc, char *argv[]) { if (argc > 1 && !strcmp("--help", argv[1])) __testlib_help(); - - if (argc < 3 || argc > 6) { + + if (argc < 3 || argc > 6) + { quit(_fail, std::string("Program must be run with the following arguments: ") + - std::string(" [ [ [<-appes>]]]") + - "\nUse \"--help\" to get help information"); + std::string(" [ [ [<-appes>]]]") + + "\nUse \"--help\" to get help information"); } - if (argc <= 4) { + if (argc <= 4) + { resultName = ""; appesMode = false; } -#ifndef EJUDGE - if (argc == 5) { + if (argc == 5) + { resultName = argv[4]; appesMode = false; } - if (argc == 6) { - if (strcmp("-APPES", argv[5]) && strcmp("-appes", argv[5])) { + if (argc == 6) + { + if (strcmp("-APPES", argv[5]) && strcmp("-appes", argv[5])) + { quit(_fail, std::string("Program must be run with the following arguments: ") + " [ [<-appes>]]"); - } else { + } + else + { resultName = argv[4]; appesMode = true; } } -#endif inf.init(argv[1], _input); - tout.open(argv[2], std::ios_base::out); - if (tout.fail() || !tout.is_open()) - quit(_fail, std::string("Can not write to the test-output-file '") + argv[2] + std::string("'")); + if (strcmp(argv[2], "stdout") != 0) + { + tout.open(argv[2], std::ios_base::out); + if (tout.fail() || !tout.is_open()) + quit(_fail, std::string("Can not write to the test-output-file '") + argv[2] + std::string("'")); + } ouf.init(stdin, _output); - + if (argc >= 4) ans.init(argv[3], _answer); else ans.name = "unopened answer stream"; } -void registerValidation() { +void registerValidation() +{ __testlib_ensuresPreconditions(); testlibMode = _validator; @@ -4134,152 +3107,67 @@ void registerValidation() { inf.strict = true; } -void registerValidation(int argc, char *argv[]) { - registerValidation(); - validator.initialize(); - - for (int i = 1; i < argc; i++) { - if (!strcmp("--testset", argv[i])) { - if (i + 1 < argc && strlen(argv[i + 1]) > 0) - validator.setTestset(argv[++i]); - else - quit(_fail, std::string("Validator must be run with the following arguments: ") + - "[--testset testset] [--group group] [--testOverviewLogFileName fileName]"); - } - if (!strcmp("--group", argv[i])) { - if (i + 1 < argc) - validator.setGroup(argv[++i]); - else - quit(_fail, std::string("Validator must be run with the following arguments: ") + - "[--testset testset] [--group group] [--testOverviewLogFileName fileName]"); - } - if (!strcmp("--testOverviewLogFileName", argv[i])) { - if (i + 1 < argc) - validator.setTestOverviewLogFileName(argv[++i]); - else - quit(_fail, std::string("Validator must be run with the following arguments: ") + - "[--testset testset] [--group group] [--testOverviewLogFileName fileName]"); - } - } -} - -void addFeature(const std::string &feature) { - if (testlibMode != _validator) - quit(_fail, "Features are supported in validators only."); - validator.addFeature(feature); -} - -void feature(const std::string &feature) { - if (testlibMode != _validator) - quit(_fail, "Features are supported in validators only."); - validator.feature(feature); -} - -class Checker { -private: - bool _initialized; - std::string _testset; - std::string _group; - -public: - Checker() : _initialized(false), _testset("tests"), _group() { - } - - void initialize() { - _initialized = true; - } - - std::string testset() const { - if (!_initialized) - __testlib_fail("Checker should be initialized with registerTestlibCmd(argc, argv) instead of registerTestlibCmd() to support checker.testset()"); - return _testset; - } - - std::string group() const { - if (!_initialized) - __testlib_fail("Checker should be initialized with registerTestlibCmd(argc, argv) instead of registerTestlibCmd() to support checker.group()"); - return _group; - } - - void setTestset(const char *const testset) { - _testset = testset; - } - - void setGroup(const char *const group) { - _group = group; - } -} checker; - -void registerTestlibCmd(int argc, char *argv[]) { +void registerTestlibCmd(int argc, char* argv[]) +{ __testlib_ensuresPreconditions(); testlibMode = _checker; - __testlib_set_binary(stdin); + __testlib_set_binary(stdin); - std::vector args(1, argv[0]); - checker.initialize(); - - for (int i = 1; i < argc; i++) { - if (!strcmp("--testset", argv[i])) { - if (i + 1 < argc && strlen(argv[i + 1]) > 0) - checker.setTestset(argv[++i]); - else - quit(_fail, std::string("Expected testset after --testset command line parameter")); - } else if (!strcmp("--group", argv[i])) { - if (i + 1 < argc) - checker.setGroup(argv[++i]); - else - quit(_fail, std::string("Expected group after --group command line parameter")); - } else - args.push_back(argv[i]); - } - - argc = int(args.size()); - if (argc > 1 && "--help" == args[1]) + if (argc > 1 && !strcmp("--help", argv[1])) __testlib_help(); - if (argc < 4 || argc > 6) { + if (argc < 4 || argc > 6) + { quit(_fail, std::string("Program must be run with the following arguments: ") + - std::string("[--testset testset] [--group group] [ [<-appes>]]") + - "\nUse \"--help\" to get help information"); + std::string(" [ [<-appes>]]") + + "\nUse \"--help\" to get help information"); } - if (argc == 4) { + if (argc == 4) + { resultName = ""; appesMode = false; } - if (argc == 5) { - resultName = args[4]; + if (argc == 5) + { + resultName = argv[4]; appesMode = false; } - if (argc == 6) { - if ("-APPES" != args[5] && "-appes" != args[5]) { + if (argc == 6) + { + if (strcmp("-APPES", argv[5]) && strcmp("-appes", argv[5])) + { quit(_fail, std::string("Program must be run with the following arguments: ") + " [ [<-appes>]]"); - } else { - resultName = args[4]; + } + else + { + resultName = argv[4]; appesMode = true; } } - inf.init(args[1], _input); - ouf.init(args[2], _output); - ans.init(args[3], _answer); + inf.init(argv[1], _input); + ouf.init(argv[2], _output); + ans.init(argv[3], _answer); } -void registerTestlib(int argc, ...) { - if (argc < 3 || argc > 5) +void registerTestlib(int argc, ...) +{ + if (argc < 3 || argc > 5) quit(_fail, std::string("Program must be run with the following arguments: ") + - " [ [<-appes>]]"); - - char **argv = new char *[argc + 1]; + " [ [<-appes>]]"); + char** argv = new char*[argc + 1]; + va_list ap; va_start(ap, argc); argv[0] = NULL; - for (int i = 0; i < argc; i++) { + for (int i = 0; i < argc; i++) + { argv[i + 1] = va_arg(ap, char*); } va_end(ap); @@ -4288,42 +3176,99 @@ void registerTestlib(int argc, ...) { delete[] argv; } -static inline void __testlib_ensure(bool cond, const std::string &msg) { +#ifdef __GNUC__ +__attribute__((const)) +#endif +inline bool doubleCompare(double expected, double result, double MAX_DOUBLE_ERROR) +{ + if (__testlib_isNaN(expected)) + { + return __testlib_isNaN(result); + } + else + if (__testlib_isInfinite(expected)) + { + if (expected > 0) + { + return result > 0 && __testlib_isInfinite(result); + } + else + { + return result < 0 && __testlib_isInfinite(result); + } + } + else + if (__testlib_isNaN(result) || __testlib_isInfinite(result)) + { + return false; + } + else + if (__testlib_abs(result - expected) <= MAX_DOUBLE_ERROR + 1E-15) + { + return true; + } + else + { + double minv = __testlib_min(expected * (1.0 - MAX_DOUBLE_ERROR), + expected * (1.0 + MAX_DOUBLE_ERROR)); + double maxv = __testlib_max(expected * (1.0 - MAX_DOUBLE_ERROR), + expected * (1.0 + MAX_DOUBLE_ERROR)); + return result + 1E-15 >= minv && result <= maxv + 1E-15; + } +} + +#ifdef __GNUC__ +__attribute__((const)) +#endif +inline double doubleDelta(double expected, double result) +{ + double absolute = __testlib_abs(result - expected); + + if (__testlib_abs(expected) > 1E-9) + { + double relative = __testlib_abs(absolute / expected); + return __testlib_min(absolute, relative); + } + else + return absolute; +} + +static inline void __testlib_ensure(bool cond, const std::string& msg) +{ if (!cond) quit(_fail, msg.c_str()); } -#ifdef __GNUC__ -__attribute__((unused)) -#endif -static inline void __testlib_ensure(bool cond, const char *msg) { +static inline void __testlib_ensure(bool cond, const char* msg) +{ if (!cond) quit(_fail, msg); } #define ensure(cond) __testlib_ensure(cond, "Condition failed: \"" #cond "\"") -#define STRINGIZE_DETAIL(x) #x -#define STRINGIZE(x) STRINGIZE_DETAIL(x) -#define ensure_ext(cond) __testlib_ensure(cond, "Line " STRINGIZE(__LINE__) ": Condition failed: \"" #cond "\"") #ifdef __GNUC__ __attribute__ ((format (printf, 2, 3))) #endif -inline void ensuref(bool cond, const char *format, ...) { - if (!cond) { +inline void ensuref(bool cond, const char* format, ...) +{ + if (!cond) + { FMT_TO_RESULT(format, format, message); __testlib_ensure(cond, message); } } -NORETURN static void __testlib_fail(const std::string &message) { +NORETURN static void __testlib_fail(const std::string& message) +{ quitf(_fail, "%s", message.c_str()); } #ifdef __GNUC__ __attribute__ ((format (printf, 1, 2))) #endif -void setName(const char *format, ...) { +void setName(const char* format, ...) +{ FMT_TO_RESULT(format, format, name); checkerName = name; } @@ -4334,9 +3279,10 @@ void setName(const char *format, ...) { * * This implementation uses testlib random_t to produce random numbers, so * it is stable. - */ + */ template -void shuffle(_RandomAccessIter __first, _RandomAccessIter __last) { +void shuffle(_RandomAccessIter __first, _RandomAccessIter __last) +{ if (__first == __last) return; for (_RandomAccessIter __i = __first + 1; __i != __last; ++__i) std::iter_swap(__i, __first + rnd.next(int(__i - __first) + 1)); @@ -4344,10 +3290,8 @@ void shuffle(_RandomAccessIter __first, _RandomAccessIter __last) { template -#if defined(__GNUC__) && !defined(__clang__) -__attribute__ ((error("Don't use random_shuffle(), use shuffle() instead"))) -#endif -void random_shuffle(_RandomAccessIter, _RandomAccessIter) { +void random_shuffle(_RandomAccessIter , _RandomAccessIter ) +{ quitf(_fail, "Don't use random_shuffle(), use shuffle() instead"); } @@ -4357,64 +3301,52 @@ void random_shuffle(_RandomAccessIter, _RandomAccessIter) { # define RAND_THROW_STATEMENT #endif -#if defined(__GNUC__) && !defined(__clang__) - -__attribute__ ((error("Don't use rand(), use rnd.next() instead"))) -#endif -#ifdef _MSC_VER -# pragma warning( disable : 4273 ) -#endif int rand() RAND_THROW_STATEMENT { quitf(_fail, "Don't use rand(), use rnd.next() instead"); - + /* This line never runs. */ //throw "Don't use rand(), use rnd.next() instead"; } -#if defined(__GNUC__) && !defined(__clang__) - -__attribute__ ((error("Don't use srand(), you should use " -"'registerGen(argc, argv, 1);' to initialize generator seed " -"by hash code of the command line params. The third parameter " -"is randomGeneratorVersion (currently the latest is 1)."))) -#endif -#ifdef _MSC_VER -# pragma warning( disable : 4273 ) -#endif void srand(unsigned int seed) RAND_THROW_STATEMENT { - quitf(_fail, "Don't use srand(), you should use " - "'registerGen(argc, argv, 1);' to initialize generator seed " - "by hash code of the command line params. The third parameter " - "is randomGeneratorVersion (currently the latest is 1) [ignored seed=%u].", seed); + quitf(_fail, "Don't use srand(), you should use " + "'registerGen(argc, argv, 1);' to initialize generator seed " + "by hash code of the command line params. The third parameter " + "is randomGeneratorVersion (currently the latest is 1) [ignored seed=%d].", seed); } -void startTest(int test) { +void startTest(int test) +{ const std::string testFileName = vtos(test); if (NULL == freopen(testFileName.c_str(), "wt", stdout)) __testlib_fail("Unable to write file '" + testFileName + "'"); } -inline std::string upperCase(std::string s) { +inline std::string upperCase(std::string s) +{ for (size_t i = 0; i < s.length(); i++) if ('a' <= s[i] && s[i] <= 'z') s[i] = char(s[i] - 'a' + 'A'); return s; } -inline std::string lowerCase(std::string s) { +inline std::string lowerCase(std::string s) +{ for (size_t i = 0; i < s.length(); i++) if ('A' <= s[i] && s[i] <= 'Z') s[i] = char(s[i] - 'A' + 'a'); return s; } -inline std::string compress(const std::string &s) { +inline std::string compress(const std::string& s) +{ return __testlib_part(s); } -inline std::string englishEnding(int x) { +inline std::string englishEnding(int x) +{ x %= 100; if (x / 10 == 1) return "th"; @@ -4427,11 +3359,33 @@ inline std::string englishEnding(int x) { return "th"; } -template -std::string join(_ForwardIterator first, _ForwardIterator last, _Separator separator) { +inline std::string trim(const std::string& s) +{ + if (s.empty()) + return s; + + int left = 0; + while (left < int(s.length()) && isBlanks(s[left])) + left++; + if (left >= int(s.length())) + return ""; + + int right = int(s.length()) - 1; + while (right >= 0 && isBlanks(s[right])) + right--; + if (right < 0) + return ""; + + return s.substr(left, right - left + 1); +} + +template +std::string join(_ForwardIterator first, _ForwardIterator last, _Separator separator) +{ std::stringstream ss; bool repeated = false; - for (_ForwardIterator i = first; i != last; i++) { + for (_ForwardIterator i = first; i != last; i++) + { if (repeated) ss << separator; else @@ -4441,668 +3395,105 @@ std::string join(_ForwardIterator first, _ForwardIterator last, _Separator separ return ss.str(); } -template -std::string join(_ForwardIterator first, _ForwardIterator last) { +template +std::string join(_ForwardIterator first, _ForwardIterator last) +{ return join(first, last, ' '); } -template -std::string join(const _Collection &collection, _Separator separator) { +template +std::string join(const _Collection& collection, _Separator separator) +{ return join(collection.begin(), collection.end(), separator); } -template -std::string join(const _Collection &collection) { +template +std::string join(const _Collection& collection) +{ return join(collection, ' '); } -/** - * Splits string s by character separator returning exactly k+1 items, - * where k is the number of separator occurences. - */ -std::vector split(const std::string &s, char separator) { - std::vector result; - std::string item; - for (size_t i = 0; i < s.length(); i++) - if (s[i] == separator) { - result.push_back(item); - item = ""; - } else - item += s[i]; - result.push_back(item); - return result; -} - -/** - * Splits string s by character separators returning exactly k+1 items, - * where k is the number of separator occurences. - */ -std::vector split(const std::string &s, const std::string &separators) { - if (separators.empty()) - return std::vector(1, s); - - std::vector isSeparator(256); - for (size_t i = 0; i < separators.size(); i++) - isSeparator[(unsigned char) (separators[i])] = true; - - std::vector result; - std::string item; - for (size_t i = 0; i < s.length(); i++) - if (isSeparator[(unsigned char) (s[i])]) { - result.push_back(item); - item = ""; - } else - item += s[i]; - result.push_back(item); - return result; -} - -/** - * Splits string s by character separator returning non-empty items. - */ -std::vector tokenize(const std::string &s, char separator) { - std::vector result; - std::string item; - for (size_t i = 0; i < s.length(); i++) - if (s[i] == separator) { - if (!item.empty()) - result.push_back(item); - item = ""; - } else - item += s[i]; - if (!item.empty()) - result.push_back(item); - return result; -} - -/** - * Splits string s by character separators returning non-empty items. - */ -std::vector tokenize(const std::string &s, const std::string &separators) { - if (separators.empty()) - return std::vector(1, s); - - std::vector isSeparator(256); - for (size_t i = 0; i < separators.size(); i++) - isSeparator[(unsigned char) (separators[i])] = true; - - std::vector result; - std::string item; - for (size_t i = 0; i < s.length(); i++) - if (isSeparator[(unsigned char) (s[i])]) { - if (!item.empty()) - result.push_back(item); - item = ""; - } else - item += s[i]; - - if (!item.empty()) - result.push_back(item); - - return result; -} - -NORETURN void __testlib_expectedButFound(TResult result, std::string expected, std::string found, const char *prepend) { +NORETURN void __testlib_expectedButFound(TResult result, std::string expected, std::string found, const char* prepend) +{ std::string message; if (strlen(prepend) != 0) message = format("%s: expected '%s', but found '%s'", - compress(prepend).c_str(), compress(expected).c_str(), compress(found).c_str()); + compress(prepend).c_str(), compress(expected).c_str(), compress(found).c_str()); else message = format("expected '%s', but found '%s'", - compress(expected).c_str(), compress(found).c_str()); + compress(expected).c_str(), compress(found).c_str()); quit(result, message); } -NORETURN void __testlib_expectedButFound(TResult result, double expected, double found, const char *prepend) { +NORETURN void __testlib_expectedButFound(TResult result, double expected, double found, const char* prepend) +{ std::string expectedString = removeDoubleTrailingZeroes(format("%.12f", expected)); std::string foundString = removeDoubleTrailingZeroes(format("%.12f", found)); __testlib_expectedButFound(result, expectedString, foundString, prepend); } -template +template #ifdef __GNUC__ __attribute__ ((format (printf, 4, 5))) #endif -NORETURN void expectedButFound(TResult result, T expected, T found, const char *prependFormat = "", ...) { +NORETURN void expectedButFound(TResult result, T expected, T found, const char* prependFormat = "", ...) +{ FMT_TO_RESULT(prependFormat, prependFormat, prepend); std::string expectedString = vtos(expected); std::string foundString = vtos(found); __testlib_expectedButFound(result, expectedString, foundString, prepend.c_str()); } -template<> +template <> #ifdef __GNUC__ __attribute__ ((format (printf, 4, 5))) #endif -NORETURN void -expectedButFound(TResult result, std::string expected, std::string found, const char *prependFormat, ...) { +NORETURN void expectedButFound(TResult result, std::string expected, std::string found, const char* prependFormat, ...) +{ FMT_TO_RESULT(prependFormat, prependFormat, prepend); __testlib_expectedButFound(result, expected, found, prepend.c_str()); } -template<> +template <> #ifdef __GNUC__ __attribute__ ((format (printf, 4, 5))) #endif -NORETURN void expectedButFound(TResult result, double expected, double found, const char *prependFormat, ...) { +NORETURN void expectedButFound(TResult result, double expected, double found, const char* prependFormat, ...) +{ FMT_TO_RESULT(prependFormat, prependFormat, prepend); std::string expectedString = removeDoubleTrailingZeroes(format("%.12f", expected)); std::string foundString = removeDoubleTrailingZeroes(format("%.12f", found)); __testlib_expectedButFound(result, expectedString, foundString, prepend.c_str()); } -template<> +template <> #ifdef __GNUC__ __attribute__ ((format (printf, 4, 5))) #endif -NORETURN void -expectedButFound(TResult result, const char *expected, const char *found, const char *prependFormat, - ...) { +NORETURN void expectedButFound(TResult result, const char* expected, const char* found, const char* prependFormat, ...) +{ FMT_TO_RESULT(prependFormat, prependFormat, prepend); __testlib_expectedButFound(result, std::string(expected), std::string(found), prepend.c_str()); } -template<> +template <> #ifdef __GNUC__ __attribute__ ((format (printf, 4, 5))) #endif -NORETURN void expectedButFound(TResult result, float expected, float found, const char *prependFormat, ...) { +NORETURN void expectedButFound(TResult result, float expected, float found, const char* prependFormat, ...) +{ FMT_TO_RESULT(prependFormat, prependFormat, prepend); __testlib_expectedButFound(result, double(expected), double(found), prepend.c_str()); } -template<> +template <> #ifdef __GNUC__ __attribute__ ((format (printf, 4, 5))) #endif -NORETURN void -expectedButFound(TResult result, long double expected, long double found, const char *prependFormat, ...) { +NORETURN void expectedButFound(TResult result, long double expected, long double found, const char* prependFormat, ...) +{ FMT_TO_RESULT(prependFormat, prependFormat, prepend); __testlib_expectedButFound(result, double(expected), double(found), prepend.c_str()); } -#if __cplusplus > 199711L || defined(_MSC_VER) -template -struct is_iterable { - template - static char test(typename U::iterator *x); - - template - static long test(U *x); - - static const bool value = sizeof(test(0)) == 1; -}; - -template -struct __testlib_enable_if { -}; - -template -struct __testlib_enable_if { - typedef T type; -}; - -template -typename __testlib_enable_if::value, void>::type __testlib_print_one(const T &t) { - std::cout << t; -} - -template -typename __testlib_enable_if::value, void>::type __testlib_print_one(const T &t) { - bool first = true; - for (typename T::const_iterator i = t.begin(); i != t.end(); i++) { - if (first) - first = false; - else - std::cout << " "; - std::cout << *i; - } -} - -template<> -typename __testlib_enable_if::value, void>::type -__testlib_print_one(const std::string &t) { - std::cout << t; -} - -template -void __println_range(A begin, B end) { - bool first = true; - for (B i = B(begin); i != end; i++) { - if (first) - first = false; - else - std::cout << " "; - __testlib_print_one(*i); - } - std::cout << std::endl; -} - -template -struct is_iterator { - static T makeT(); - - typedef void *twoptrs[2]; - - static twoptrs &test(...); - - template - static typename R::iterator_category *test(R); - - template - static void *test(R *); - - static const bool value = sizeof(test(makeT())) == sizeof(void *); -}; - -template -struct is_iterator::value>::type> { - static const bool value = false; -}; - -template -typename __testlib_enable_if::value, void>::type println(const A &a, const B &b) { - __testlib_print_one(a); - std::cout << " "; - __testlib_print_one(b); - std::cout << std::endl; -} - -template -typename __testlib_enable_if::value, void>::type println(const A &a, const B &b) { - __println_range(a, b); -} - -template -void println(const A *a, const A *b) { - __println_range(a, b); -} - -template<> -void println(const char *a, const char *b) { - __testlib_print_one(a); - std::cout << " "; - __testlib_print_one(b); - std::cout << std::endl; -} - -template -void println(const T &x) { - __testlib_print_one(x); - std::cout << std::endl; -} - -template -void println(const A &a, const B &b, const C &c) { - __testlib_print_one(a); - std::cout << " "; - __testlib_print_one(b); - std::cout << " "; - __testlib_print_one(c); - std::cout << std::endl; -} - -template -void println(const A &a, const B &b, const C &c, const D &d) { - __testlib_print_one(a); - std::cout << " "; - __testlib_print_one(b); - std::cout << " "; - __testlib_print_one(c); - std::cout << " "; - __testlib_print_one(d); - std::cout << std::endl; -} - -template -void println(const A &a, const B &b, const C &c, const D &d, const E &e) { - __testlib_print_one(a); - std::cout << " "; - __testlib_print_one(b); - std::cout << " "; - __testlib_print_one(c); - std::cout << " "; - __testlib_print_one(d); - std::cout << " "; - __testlib_print_one(e); - std::cout << std::endl; -} - -template -void println(const A &a, const B &b, const C &c, const D &d, const E &e, const F &f) { - __testlib_print_one(a); - std::cout << " "; - __testlib_print_one(b); - std::cout << " "; - __testlib_print_one(c); - std::cout << " "; - __testlib_print_one(d); - std::cout << " "; - __testlib_print_one(e); - std::cout << " "; - __testlib_print_one(f); - std::cout << std::endl; -} - -template -void println(const A &a, const B &b, const C &c, const D &d, const E &e, const F &f, const G &g) { - __testlib_print_one(a); - std::cout << " "; - __testlib_print_one(b); - std::cout << " "; - __testlib_print_one(c); - std::cout << " "; - __testlib_print_one(d); - std::cout << " "; - __testlib_print_one(e); - std::cout << " "; - __testlib_print_one(f); - std::cout << " "; - __testlib_print_one(g); - std::cout << std::endl; -} - -/* opts */ -size_t getOptType(char* s) { - if (!s || strlen(s) <= 1) - return 0; - - if (s[0] == '-') { - if (isalpha(s[1])) - return 1; - else if (s[1] == '-') - return isalpha(s[2]) ? 2 : 0; - } - - return 0; -} - -size_t parseOpt(size_t argc, char* argv[], size_t index, std::map& opts) { - if (index >= argc) - return 0; - - size_t type = getOptType(argv[index]), inc = 1; - if (type > 0) { - std::string key(argv[index] + type), val; - size_t sep = key.find('='); - if (sep != std::string::npos) { - val = key.substr(sep + 1); - key = key.substr(0, sep); - } else { - if (index + 1 < argc && getOptType(argv[index + 1]) == 0) { - val = argv[index + 1]; - inc = 2; - } else { - if (key.length() > 1 && isdigit(key[1])) { - val = key.substr(1); - key = key.substr(0, 1); - } else { - val = "true"; - } - } - } - opts[key] = val; - } else { - return inc; - } - - return inc; -} - -std::vector __testlib_argv; -std::map __testlib_opts; - -void prepareOpts(int argc, char* argv[]) { - if (argc <= 0) - __testlib_fail("Opts: expected argc>=0 but found " + toString(argc)); - size_t n = static_cast(argc); // NOLINT(hicpp-use-auto,modernize-use-auto) - __testlib_opts = std::map(); - for (size_t index = 1; index < n; index += parseOpt(n, argv, index, __testlib_opts)); - __testlib_argv = std::vector(n); - for (size_t index = 0; index < n; index++) - __testlib_argv[index] = argv[index]; -} - -std::string __testlib_indexToArgv(int index) { - if (index < 0 || index >= int(__testlib_argv.size())) - __testlib_fail("Opts: index '" + toString(index) + "' is out of range [0," + toString(__testlib_argv.size()) + ")"); - return __testlib_argv[size_t(index)]; -} - -std::string __testlib_keyToOpts(const std::string& key) { - if (__testlib_opts.count(key) == 0) - __testlib_fail("Opts: unknown key '" + compress(key) + "'"); - return __testlib_opts[key]; -} - -template -T optValueToIntegral(const std::string& s, bool nonnegative); - -long double optValueToLongDouble(const std::string& s); - -std::string parseExponentialOptValue(const std::string& s) { - size_t pos = std::string::npos; - for (size_t i = 0; i < s.length(); i++) - if (s[i] == 'e' || s[i] == 'E') { - if (pos != std::string::npos) - __testlib_fail("Opts: expected typical exponential notation but '" + compress(s) + "' found"); - pos = i; - } - if (pos == std::string::npos) - return s; - std::string e = s.substr(pos + 1); - if (!e.empty() && e[0] == '+') - e = e.substr(1); - if (e.empty()) - __testlib_fail("Opts: expected typical exponential notation but '" + compress(s) + "' found"); - if (e.length() > 20) - __testlib_fail("Opts: expected typical exponential notation but '" + compress(s) + "' found"); - int ne = optValueToIntegral(e, false); - std::string num = s.substr(0, pos); - if (num.length() > 20) - __testlib_fail("Opts: expected typical exponential notation but '" + compress(s) + "' found"); - if (!num.empty() && num[0] == '+') - num = num.substr(1); - optValueToLongDouble(num); - bool minus = false; - if (num[0] == '-') { - minus = true; - num = num.substr(1); - } - for (int i = 0; i < +ne; i++) { - size_t sep = num.find('.'); - if (sep == std::string::npos) - num += '0'; - else { - if (sep + 1 == num.length()) - num[sep] = '0'; - else - std::swap(num[sep], num[sep + 1]); - } - } - for (int i = 0; i < -ne; i++) { - size_t sep = num.find('.'); - if (sep == std::string::npos) - num.insert(num.begin() + int(num.length()) - 1, '.'); - else { - if (sep == 0) - num.insert(num.begin() + 1, '0'); - else - std::swap(num[sep - 1], num[sep]); - } - } - while (!num.empty() && num[0] == '0') - num = num.substr(1); - while (num.find('.') != std::string::npos && num.back() == '0') - num = num.substr(0, num.length() - 1); - if (!num.empty() && num.back() == '.') - num = num.substr(0, num.length() - 1); - if ((!num.empty() && num[0] == '.') || num.empty()) - num.insert(num.begin(), '0'); - return (minus ? "-" : "") + num; -} - -template -T optValueToIntegral(const std::string& s_, bool nonnegative) { - std::string s(parseExponentialOptValue(s_)); - if (s.empty()) - __testlib_fail("Opts: expected integer but '" + compress(s_) + "' found"); - T value = 0; - long double about = 0.0; - signed char sign = +1; - size_t pos = 0; - if (s[pos] == '-') { - if (nonnegative) - __testlib_fail("Opts: expected non-negative integer but '" + compress(s_) + "' found"); - sign = -1; - pos++; - } - for (size_t i = pos; i < s.length(); i++) { - if (s[i] < '0' || s[i] > '9') - __testlib_fail("Opts: expected integer but '" + compress(s_) + "' found"); - value = value * 10 + s[i] - '0'; - about = about * 10 + s[i] - '0'; - } - value *= sign; - about *= sign; - if (fabsl(value - about) > 0.1) - __testlib_fail("Opts: integer overflow: expected integer but '" + compress(s_) + "' found"); - return value; -} - -long double optValueToLongDouble(const std::string& s_) { - std::string s(parseExponentialOptValue(s_)); - if (s.empty()) - __testlib_fail("Opts: expected float number but '" + compress(s_) + "' found"); - long double value = 0.0; - signed char sign = +1; - size_t pos = 0; - if (s[pos] == '-') { - sign = -1; - pos++; - } - bool period = false; - long double mul = 1.0; - for (size_t i = pos; i < s.length(); i++) { - if (s[i] == '.') { - if (period) - __testlib_fail("Opts: expected float number but '" + compress(s_) + "' found"); - else { - period = true; - continue; - } - } - if (period) - mul *= 10.0; - if (s[i] < '0' || s[i] > '9') - __testlib_fail("Opts: expected float number but '" + compress(s_) + "' found"); - if (period) - value += (s[i] - '0') / mul; - else - value = value * 10 + s[i] - '0'; - } - value *= sign; - return value; -} - -bool has_opt(const std::string key) { - return __testlib_opts.count(key) != 0; -} - -template -T opt(std::false_type, int index); - -template<> -std::string opt(std::false_type, int index) { - return __testlib_indexToArgv(index); -} - -template -T opt(std::true_type, int index) { - return T(optValueToLongDouble(__testlib_indexToArgv(index))); -} - -template -T opt(std::false_type, U, int index) { - return opt(std::is_floating_point(), index); -} - -template -T opt(std::true_type, std::false_type, int index) { - return optValueToIntegral(__testlib_indexToArgv(index), false); -} - -template -T opt(std::true_type, std::true_type, int index) { - return optValueToIntegral(__testlib_indexToArgv(index), true); -} - -template<> -bool opt(std::true_type, std::true_type, int index) { - std::string value = __testlib_indexToArgv(index); - if (value == "true" || value == "1") - return true; - if (value == "false" || value == "0") - return false; - __testlib_fail("Opts: opt by index '" + toString(index) + "': expected bool true/false or 0/1 but '" + compress(value) + "' found"); -} - -template -T opt(int index) { - return opt(std::is_integral(), std::is_unsigned(), index); -} - -std::string opt(int index) { - return opt(index); -} - -template -T opt(std::false_type, const std::string& key); - -template<> -std::string opt(std::false_type, const std::string& key) { - return __testlib_keyToOpts(key); -} - -template -T opt(std::true_type, const std::string& key) { - return T(optValueToLongDouble(__testlib_keyToOpts(key))); -} - -template -T opt(std::false_type, U, const std::string& key) { - return opt(std::is_floating_point(), key); -} - -template -T opt(std::true_type, std::false_type, const std::string& key) { - return optValueToIntegral(__testlib_keyToOpts(key), false); -} - -template -T opt(std::true_type, std::true_type, const std::string& key) { - return optValueToIntegral(__testlib_keyToOpts(key), true); -} - -template<> -bool opt(std::true_type, std::true_type, const std::string& key) { - if (!has_opt(key)) - return false; - std::string value = __testlib_keyToOpts(key); - if (value == "true" || value == "1") - return true; - if (value == "false" || value == "0") - return false; - __testlib_fail("Opts: key '" + compress(key) + "': expected bool true/false or 0/1 but '" + compress(value) + "' found"); -} - -template -T opt(const std::string key) { - return opt(std::is_integral(), std::is_unsigned(), key); -} - -std::string opt(const std::string key) { - return opt(key); -} -#endif #endif