// https://github.com/MikeMirzayanov/testlib/blob/master/testlib.h #ifndef _TESTLIB_H_ #define _TESTLIB_H_ #define VERSION "0.9.24-SNAPSHOT" #ifdef _MSC_VER #define _CRT_SECURE_NO_DEPRECATE #define _CRT_SECURE_NO_WARNINGS #define _CRT_NO_VA_START_VALIDATION #endif #define random __random_deprecated #include #include #include #include #undef random #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 #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 #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) #ifndef OK_EXIT_CODE #ifdef CONTESTER #define OK_EXIT_CODE 0xAC #else #define OK_EXIT_CODE 0 #endif #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 #endif #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 #endif #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 #endif #ifndef DIRT_EXIT_CODE #ifdef EJUDGE #define DIRT_EXIT_CODE 6 #else #define DIRT_EXIT_CODE 4 #endif #endif #ifndef POINTS_EXIT_CODE #define POINTS_EXIT_CODE 7 #endif #ifndef UNEXPECTED_EOF_EXIT_CODE #define UNEXPECTED_EOF_EXIT_CODE 8 #endif #ifndef PC_BASE_EXIT_CODE #ifdef TESTSYS #define PC_BASE_EXIT_CODE 50 #else #define PC_BASE_EXIT_CODE 0 #endif #endif #ifdef __GNUC__ #define __TESTLIB_STATIC_ASSERT(condition) typedef void* __testlib_static_assert_type[(condition) ? 1 : -1] __attribute__((unused)) #else #define __TESTLIB_STATIC_ASSERT(condition) typedef void* __testlib_static_assert_type[(condition) ? 1 : -1] #endif #ifdef ON_WINDOWS #define I64 "%I64d" #define U64 "%I64u" #else #define I64 "%lld" #define U64 "%llu" #endif #ifdef _MSC_VER #define NORETURN __declspec(noreturn) #elif defined __GNUC__ #define NORETURN __attribute__((noreturn)) #else #define NORETURN #endif static char __testlib_format_buffer[16777216]; static int __testlib_format_buffer_usage_count = 0; #define FMT_TO_RESULT(fmt, cstr, result) \ std::string result; \ if (__testlib_format_buffer_usage_count != 0) \ __testlib_fail("FMT_TO_RESULT::__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); \ 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); template 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) { return a < b ? a : b; } template static inline T __testlib_max(const T& a, const T& b) { return a > b ? a : b; } 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); #else return std::_isnan(ra); #endif } 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'; } #ifdef __GNUC__ __attribute__((format(printf, 1, 2))) #endif std::string format(const char* fmt, ...) { FMT_TO_RESULT(fmt, fmt, result); return result; } 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 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)); ra = -ra; std::memcpy((void*)&llr2, (void*)&ra, sizeof(double)); long long llnan = 0xFFF8000000000000LL; return __testlib_prelimIsNaN(r) || llnan == llr1 || llnan == llr2; } static double __testlib_nan() { __TESTLIB_STATIC_ASSERT(sizeof(double) == sizeof(long long)); #ifndef NAN long long llnan = 0xFFF8000000000000LL; double nan; std::memcpy(&nan, &llnan, sizeof(double)); return nan; #else return NAN; #endif } static bool __testlib_isInfinite(double r) { volatile double ra = r; return (ra > 1E300 || ra < -1E300); } #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; } #if !defined(_MSC_VER) || _MSC_VER < 1900 #ifndef _fileno #define _fileno(_stream) ((_stream)->_file) #endif #endif #ifndef O_BINARY static void __testlib_set_binary( #ifdef __GNUC__ __attribute__((unused)) #endif std::FILE* file) #else static void __testlib_set_binary(std::FILE* file) #endif { #ifdef O_BINARY if (NULL != file) { #ifndef __BORLANDC__ _setmode(_fileno(file), O_BINARY); #else setmode(fileno(file), O_BINARY); #endif } #endif } class random_t; class pattern { public: pattern(std::string s); std::string next(random_t& rnd) const; bool matches(const std::string& s) const; std::string src() const; private: bool matches(const std::string& s, size_t pos) const; std::string s; std::vector children; std::vector chars; int from; int to; }; class random_t { private: unsigned long long seed; static const unsigned long long multiplier; static const unsigned long long addend; static const unsigned long long mask; static const int lim; long long nextBits(int bits) { if (bits <= 48) { seed = (seed * multiplier + addend) & mask; 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; } } public: static int version; random_t() : seed(3905348978240129619LL) {} void setSeed(int argc, char* argv[]) { random_t p; seed = 3905348978240129619LL; 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 += multiplier / addend; } seed = seed & mask; } void setSeed(long long _seed) { _seed = (_seed ^ multiplier) & mask; seed = _seed; } #ifndef __BORLANDC__ std::string next(const std::string& ptrn) { pattern p(ptrn); return p.next(*this); } #else std::string next(std::string ptrn) { pattern p(ptrn); return p.next(*this); } #endif int next(int n) { if (n <= 0)__testlib_fail("random_t::next(int n): n must be positive"); if ((n & -n) == n)return (int)((n * (long long)nextBits(31)) >> 31); const long long limit = INT_MAX / n * n; long long bits; do { bits = nextBits(31); } while (bits >= limit); return int(bits % 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)); } 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); } while (bits >= limit); return bits % n; } 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)); } long next(long n) { return (long)next((long long)(n)); } 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)); } int next(int from, int to) { return 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); } long long next(long long from, long long to) { return next(to - from + 1) + from; } 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; } long next(long from, long to) { return next(to - from + 1) + from; } 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; } double next() { long long left = ((long long)(nextBits(26)) << 27); long long right = nextBits(27); return (double)(left + right) / (double)(1LL << 53); } double next(double n) { return n * next(); } double next(double from, double to) { if (from > to)__testlib_fail("random_t::next(double from, double to): from can't not exceed to"); return next(to - from) + from; } 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"); return *(c.begin() + next(size)); } 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"); return *(begin + next(size)); } #ifdef __GNUC__ __attribute__((format(printf, 2, 3))) #endif std::string next(const char* format, ...) { FMT_TO_RESULT(format, format, ptrn); return next(ptrn); } 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) { 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 { double p; if (type > 0)p = std::pow(next() + 0.0, 1.0 / (type + 1)); else p = 1 - std::pow(next() + 0.0, 1.0 / (-type + 1)); return int(n * p); } } 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) { 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 { double p; if (type > 0)p = std::pow(next() + 0.0, 1.0 / (type + 1)); else p = std::pow(next() + 0.0, -type + 1); return __testlib_min(__testlib_max((long long)(double(n) * p), 0LL), n - 1LL); } } 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 { double p; if (type > 0)p = std::pow(next() + 0.0, 1.0 / (type + 1)); else p = std::pow(next() + 0.0, -type + 1); return p; } } 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) { 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 { double p; if (type > 0)p = std::pow(next() + 0.0, 1.0 / (type + 1)); else p = std::pow(next() + 0.0, -type + 1); return n * p; } } 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); } 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); } long wnext(long n, int type) { return (long)wnext((long long)(n), type); } 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); } 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"); return wnext(to - from + 1, type) + from; } 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"); return int(wnext(to - from + 1, type) + from); } 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"); return wnext(to - from + 1, type) + from; } 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"); return wnext(to - from + 1, type) + from; } 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"); return wnext(to - from + 1, type) + from; } 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; } 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"); return wnext(to - from, type) + from; } 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"); return *(c.begin() + wnext(size, 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"); return *(begin + wnext(size, type)); } 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); for (T i = 0; i < size; i++)p[i] = first + i; if (size > 1)for (T i = 1; i < size; i++)std::swap(p[i], p[next(i + 1)]); return p; } template std::vector perm(T size) { return perm(size, T(0)); } }; const int random_t::lim = 25; const unsigned long long random_t::multiplier = 0x5DEECE66DLL; const unsigned long long random_t::addend = 0xBLL; const unsigned long long random_t::mask = (1LL << 48) - 1; int random_t::version = -1; bool pattern::matches(const std::string& s) const { return matches(s, 0); } 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) { if (pos >= s.length())return false; int slashes = 0; int before = int(pos) - 1; while (before >= 0 && s[before] == '\\')before--, slashes++; return slashes % 2 == 0 && s[pos] == value; } static char __pattern_getChar(const std::string& s, size_t& pos) { if (__pattern_isSlash(s, pos))pos += 2; else pos++; return s[pos - 1]; } #ifdef __GNUC__ __attribute__((pure)) #endif static int __pattern_greedyMatch(const std::string& s, size_t pos, const std::vector chars) { int result = 0; while (pos < s.length()) { char c = s[pos++]; if (!std::binary_search(chars.begin(), chars.end(), c))break; else result++; } return result; } std::string pattern::src() const { return s; } bool pattern::matches(const std::string& s, size_t pos) const { std::string result; if (to > 0) { int size = __pattern_greedyMatch(s, pos, chars); if (size < from)return false; if (size > to)size = to; pos += size; } if (children.size() > 0) { for (size_t child = 0; child < children.size(); child++)if (children[child].matches(s, pos))return true; return false; } else return pos == s.length(); } 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) { 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) { int child = rnd.next(int(children.size())); result += children[child].next(rnd); } return result; } 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, '{')) { std::vector parts; std::string part; pos++; while (pos < s.length() && !__pattern_isCommandChar(s, pos, '}')) { if (__pattern_isCommandChar(s, pos, ','))parts.push_back(part), part = "", pos++; else part += __pattern_getChar(s, pos); } if (part != "")parts.push_back(part); if (!__pattern_isCommandChar(s, pos, '}'))__testlib_fail("pattern: Illegal pattern (or part) \"" + s + "\""); pos++; if (parts.size() < 1 || parts.size() > 2)__testlib_fail("pattern: Illegal pattern (or part) \"" + s + "\""); std::vector numbers; for (size_t i = 0; i < parts.size(); i++) { if (parts[i].length() == 0)__testlib_fail("pattern: Illegal pattern (or part) \"" + s + "\""); int number; if (std::sscanf(parts[i].c_str(), "%d", &number) != 1)__testlib_fail("pattern: Illegal pattern (or part) \"" + s + "\""); numbers.push_back(number); } if (numbers.size() == 1)from = to = numbers[0]; else from = numbers[0], to = numbers[1]; if (from > to)__testlib_fail("pattern: Illegal pattern (or part) \"" + s + "\""); } else { if (__pattern_isCommandChar(s, pos, '?')) { from = 0, to = 1, pos++; return; } if (__pattern_isCommandChar(s, pos, '*')) { from = 0, to = INT_MAX, pos++; return; } 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) { if (pos >= s.length())__testlib_fail("pattern: Illegal pattern (or part) \"" + s + "\""); std::vector result; if (__pattern_isCommandChar(s, pos, '[')) { pos++; bool negative = __pattern_isCommandChar(s, pos, '^'); char 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, ']')) { result.push_back(prev); prev = '-'; continue; } char next = __pattern_getChar(s, pos); if (prev > next)__testlib_fail("pattern: Illegal pattern (or part) \"" + s + "\""); for (char c = prev; c != next; c++)result.push_back(c); result.push_back(next); prev = 0; } else { if (prev != 0)result.push_back(prev); prev = __pattern_getChar(s, pos); } } if (prev != 0)result.push_back(prev); if (!__pattern_isCommandChar(s, pos, ']'))__testlib_fail("pattern: Illegal pattern (or part) \"" + s + "\""); pos++; if (negative) { std::sort(result.begin(), result.end()); std::vector actuals; for (int code = 0; code < 255; code++) { char c = char(code); if (!std::binary_search(result.begin(), result.end(), c))actuals.push_back(c); } result = actuals; } std::sort(result.begin(), result.end()); } else result.push_back(__pattern_getChar(s, pos)); return result; } 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, ' '))t += s[i]; s = t; int opened = 0; int firstClose = -1; std::vector seps; for (size_t i = 0; i < s.length(); i++) { if (__pattern_isCommandChar(s, i, '(')) { opened++; continue; } 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 + "\""); if (__pattern_isCommandChar(s, i, '|') && opened == 0)seps.push_back(int(i)); } 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, ')')) { children.push_back(pattern(s.substr(1, s.length() - 2))); } else { if (seps.size() > 0) { seps.push_back(int(s.length())); int last = 0; for (size_t i = 0; i < seps.size(); i++) { children.push_back(pattern(s.substr(last, seps[i] - last))); last = seps[i] + 1; } } else { size_t pos = 0; chars = __pattern_scanCharSet(s, pos); __pattern_scanCounts(s, pos, from, to); if (pos < s.length())children.push_back(pattern(s.substr(pos))); } } } template inline bool isEof(C c) { return c == EOFC; } template inline bool isEoln(C c) { return (c == LF || c == CR); } template 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 { _input, _output, _answer }; enum TResult { _ok = 0, _wa = 1, _pe = 2, _fail = 3, _dirt = 4, _points = 5, _unexpected_eof = 8, _partially = 16 }; enum TTestlibMode { _unknown, _checker, _validator, _generator, _interactor }; #define _pc(exitCode) (TResult(_partially + (exitCode))) const std::string outcomes[] = { "accepted", "wrong-answer", "presentation-error", "fail", "fail", #ifndef PCMS2 "points", #else "relative-scoring", #endif "reserved", "reserved", "unexpected-eof", "reserved", "reserved", "reserved", "reserved", "reserved", "reserved", "reserved", "partially-correct" }; class InputStreamReader { public: 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() {} class StringInputStreamReader : public InputStreamReader { private: std::string s; size_t pos; public: StringInputStreamReader(const std::string& content) : s(content), pos(0) {} int curChar() { if (pos >= s.length())return EOFC; else return s[pos]; } int nextChar() { if (pos >= s.length()) { pos++; return EOFC; } else return s[pos++]; } void skipChar() { pos++; } void unreadChar(int c) { if (pos == 0)__testlib_fail("FileFileInputStreamReader::unreadChar(int): pos == 0."); pos--; if (pos < s.length())s[pos] = char(c); } std::string getName() { return __testlib_part(s); } int getLine() { return -1; } bool eof() { return pos >= s.length(); } void close() {} }; class FileInputStreamReader : public InputStreamReader { private: 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) { 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) {} int curChar() { if (feof(file))return EOFC; else { int c = getc(file); ungetc(c); return postprocessGetc(c); } } int nextChar() { if (feof(file))return EOFC; else return postprocessGetc(getc(file)); } void skipChar() { getc(file); } void unreadChar(int c) { ungetc(c); } std::string getName() { return name; } int getLine() { return line; } bool eof() { if (NULL == file || 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; } } }; class BufferedFileInputStreamReader : public InputStreamReader { private: static const size_t BUFFER_SIZE; static const size_t MAX_UNREAD_COUNT; std::FILE* file; char* buffer; bool* isEof; int bufferPos; size_t bufferSize; std::string name; int line; bool refill() { if (NULL == file)__testlib_fail("BufferedFileInputStreamReader: file == NULL (" + getName() + ")"); if (bufferPos >= int(bufferSize)) { size_t readSize = fread( buffer + MAX_UNREAD_COUNT, 1, BUFFER_SIZE - MAX_UNREAD_COUNT, file); if (readSize < BUFFER_SIZE - MAX_UNREAD_COUNT && ferror(file))__testlib_fail("BufferedFileInputStreamReader: unable to read (" + getName() + ")"); bufferSize = MAX_UNREAD_COUNT + readSize; bufferPos = int(MAX_UNREAD_COUNT); std::memset(isEof + MAX_UNREAD_COUNT, 0, sizeof(isEof[0]) * readSize); return readSize > 0; } 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) { buffer = new char[BUFFER_SIZE]; isEof = new bool[BUFFER_SIZE]; bufferSize = MAX_UNREAD_COUNT; bufferPos = int(MAX_UNREAD_COUNT); } ~BufferedFileInputStreamReader() { if (NULL != buffer) { delete[] buffer; buffer = NULL; } if (NULL != isEof) { delete[] isEof; isEof = NULL; } } int curChar() { if (!refill())return EOFC; return isEof[bufferPos] ? EOFC : buffer[bufferPos]; } int nextChar() { if (!refill())return EOFC; return isEof[bufferPos] ? EOFC : increment(); } void skipChar() { increment(); } 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--; } std::string getName() { return name; } int getLine() { return line; } bool eof() { return !refill() || EOFC == curChar(); } void close() { if (NULL != file) { fclose(file); file = NULL; } } }; const size_t BufferedFileInputStreamReader::BUFFER_SIZE = 2000000; const size_t BufferedFileInputStreamReader::MAX_UNREAD_COUNT = BufferedFileInputStreamReader::BUFFER_SIZE / 2; struct InStream { InStream(); ~InStream(); InStream(const InStream& baseStream, std::string content); InputStreamReader* reader; int lastLine; std::string name; TMode mode; bool opened; bool stdfile; bool strict; 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 skipBlanks(); char curChar(); void skipChar(); char nextChar(); char readChar(); char readChar(char c); char readSpace(); void unreadChar(char c); void reset(std::FILE* file = NULL); bool eof(); bool seekEof(); bool eoln(); bool seekEoln(); void nextLine(); std::string readWord(); std::string readToken(); 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 readToken(const std::string& ptrn, const std::string& variableName = ""); std::string readToken(const pattern& p, 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 = ""); long long readLong(); unsigned long long readUnsignedLong(); int readInteger(); int readInt(); long long readLong(long long minv, long long maxv, const std::string& variableName = ""); std::vector readLongs(int size, long long minv, long long maxv, const std::string& variablesName = "", int indexBase = 1); 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); int readInteger(int minv, int maxv, const std::string& variableName = ""); int readInt(int minv, int maxv, const std::string& variableName = ""); std::vector readIntegers(int size, int minv, int maxv, const std::string& variablesName = "", int indexBase = 1); std::vector readIntegers(int size, int indexBase = 1); std::vector readInts(int size, int minv, int maxv, const std::string& variablesName = "", int indexBase = 1); std::vector readInts(int size, int indexBase = 1); double readReal(); double readDouble(); 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 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 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); 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); std::string readString(); std::vector readStrings(int size, int indexBase = 1); void readStringTo(std::string& result); std::string readString(const pattern& p, const std::string& variableName = ""); std::string readString(const std::string& ptrn, const std::string& variableName = ""); std::vector readStrings(int size, const pattern& p, const std::string& variableName = "", int indexBase = 1); std::vector readStrings(int size, const std::string& ptrn, const std::string& variableName = "", int indexBase = 1); void readStringTo(std::string& result, const pattern& p, const std::string& variableName = ""); void readStringTo(std::string& result, const std::string& ptrn, const std::string& variableName = ""); std::string readLine(); std::vector readLines(int size, int indexBase = 1); void readLineTo(std::string& result); std::string readLine(const pattern& p, const std::string& variableName = ""); std::string readLine(const std::string& ptrn, const std::string& variableName = ""); std::vector readLines(int size, const pattern& p, const std::string& variableName = "", int indexBase = 1); std::vector readLines(int size, const std::string& ptrn, const std::string& variableName = "", int indexBase = 1); void readLineTo(std::string& result, const pattern& p, const std::string& variableName = ""); void readLineTo(std::string& result, const std::string& ptrn, const std::string& variableName = ""); void readEoln(); void readEof(); NORETURN void quit(TResult result, const char* msg); NORETURN void quitf(TResult result, const char* msg, ...); void quitif(bool condition, TResult result, const char* msg, ...); NORETURN void quits(TResult result, std::string msg); #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; static void textColor(WORD color); static void quitscr(WORD color, const char* msg); static void quitscrS(WORD color, std::string msg); void xmlSafeWrite(std::FILE* file, const char* msg); private: InStream(const InStream&); InStream& operator=(const InStream&); }; InStream inf; InStream ouf; InStream ans; bool appesMode; std::string resultName; std::string checkerName = "untitled checker"; 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: 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() : _testset("tests"), _group() {} std::string testset() const { return _testset; } std::string group() const { 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 { static bool alive; int quitCount, readEofCount; TestlibFinalizeGuard() : quitCount(0), readEofCount(0) {} ~TestlibFinalizeGuard() { bool _alive = alive; alive = false; 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(); } }; bool TestlibFinalizeGuard::alive = true; TestlibFinalizeGuard testlibFinalizeGuard; void disableFinalizeGuard() { TestlibFinalizeGuard::alive = false; } std::fstream tout; #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()); } #else template static std::string vtos(const T& t) { std::string s; static std::stringstream ss; ss.str(std::string()); ss.clear(); ss << t; ss >> s; return s; } #endif template static std::string toString(const T& t) { return vtos(t); } InStream::InStream() { reader = NULL; lastLine = -1; name = ""; mode = _input; strict = false; stdfile = false; wordReserveSize = 4; readManyIteration = NO_INDEX; maxFileSize = 128 * 1024 * 1024; maxTokenLength = 32 * 1024 * 1024; maxMessageLength = 32000; } InStream::InStream(const InStream& baseStream, std::string content) { reader = new StringInputStreamReader(content); lastLine = -1; opened = true; strict = baseStream.strict; mode = baseStream.mode; name = "based on " + baseStream.name; readManyIteration = NO_INDEX; maxFileSize = 128 * 1024 * 1024; maxTokenLength = 32 * 1024 * 1024; maxMessageLength = 32000; } InStream::~InStream() { if (NULL != reader) { reader->close(); delete reader; reader = NULL; } } #ifdef __GNUC__ __attribute__((const)) #endif int resultExitCode(TResult r) { if (r == _ok)return OK_EXIT_CODE; if (r == _wa)return WA_EXIT_CODE; if (r == _pe)return PE_EXIT_CODE; if (r == _fail)return FAIL_EXIT_CODE; if (r == _dirt)return DIRT_EXIT_CODE; if (r == _points)return POINTS_EXIT_CODE; if (r == _unexpected_eof) #ifdef ENABLE_UNEXPECTED_EOF return UNEXPECTED_EOF_EXIT_CODE; #else return PE_EXIT_CODE; #endif if (r >= _partially)return PC_BASE_EXIT_CODE + (r - _partially); 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) 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"); } } #endif } 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 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 = i; else openPos = INT_MAX; } if (message[i] == InStream::CLOSE_BRACKET) { if (closePos == -1)closePos = 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 = i; else openPos = INT_MAX; } if (message[i] == InStream::CLOSE_BRACKET) { if (closePos == -1)closePos = 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) { if (TestlibFinalizeGuard::alive)testlibFinalizeGuard.quitCount++; std::string message(msg); message = trim(message); 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"); } } 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::string errorName; if (__testlib_shouldCheckDirt(result)) { if (testlibMode != _interactor && !ouf.seekEof())quit(_dirt, "Extra information in the output file"); } 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 "; 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 ??? "); } if (resultName != "") { resultFile = std::fopen(resultName.c_str(), "w"); if (resultFile == NULL)quit(_fail, "Can not write to the result file"); if (appesMode) { std::fprintf(resultFile, ""); if (isPartial)std::fprintf(resultFile, "", outcomes[(int)_partially].c_str(), pctype); else { if (result != _points)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()); } } xmlSafeWrite(resultFile, __testlib_toPrintableMessage(message).c_str()); std::fprintf(resultFile, "\n"); } else std::fprintf(resultFile, "%s", __testlib_toPrintableMessage(message).c_str()); if (NULL == resultFile || fclose(resultFile) != 0)quit(_fail, "Can not write to the result file"); } quitscr(LightGray, __testlib_toPrintableMessage(message).c_str()); std::fprintf(stderr, "\n"); inf.close(); ouf.close(); ans.close(); if (tout.is_open())tout.close(); textColor(LightGray); if (resultName != "")std::fprintf(stderr, "See file to check exit message\n"); halt(resultExitCode(result)); } #ifdef __GNUC__ __attribute__((format(printf, 3, 4))) #endif 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) { InStream::quit(result, msg.c_str()); } 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] == '&') { std::fprintf(file, "%s", "&"); continue; } if (msg[i] == '<') { std::fprintf(file, "%s", "<"); continue; } if (msg[i] == '>') { std::fprintf(file, "%s", ">"); continue; } if (msg[i] == '"') { std::fprintf(file, "%s", """); continue; } if (0 <= msg[i] && msg[i] <= 31) { std::fprintf(file, "%c", '.'); continue; } std::fprintf(file, "%c", msg[i]); } } void InStream::quitscrS(WORD color, std::string msg) { quitscr(color, msg.c_str()); } 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) { if (opened && stdfile)quit(_fail, "Can't reset standard handle"); if (opened)close(); if (!stdfile)if (NULL == (file = std::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 + "\""); } if (NULL != file) { opened = true; __testlib_set_binary(file); if (stdfile)reader = new FileInputStreamReader(file, name); else reader = new BufferedFileInputStreamReader(file, name); } else { opened = false; reader = NULL; } } void InStream::init(std::string fileName, TMode mode) { opened = false; 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(); 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) { 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); } char InStream::curChar() { return char(reader->curChar()); } char InStream::nextChar() { return char(reader->nextChar()); } char InStream::readChar() { return nextChar(); } char InStream::readChar(char c) { lastLine = reader->getLine(); char found = readChar(); if (c != found) { if (!isEoln(found))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()); } return found; } char InStream::readSpace() { return readChar(' '); } void InStream::unreadChar(char c) { reader->unreadChar(c); } void InStream::skipChar() { reader->skipChar(); } void InStream::skipBlanks() { while (isBlanks(reader->curChar()))reader->skipChar(); } std::string InStream::readWord() { readWordTo(_tmpReadToken); return _tmpReadToken; } void InStream::readWordTo(std::string& result) { if (!strict)skipBlanks(); lastLine = reader->getLine(); int cur = reader->nextChar(); if (cur == EOFC)quit(_unexpected_eof, "Unexpected end of file - token expected"); if (isBlanks(cur))quit(_pe, "Unexpected white-space - token expected"); result.clear(); while (!(isBlanks(cur) || cur == EOFC)) { result += char(cur); if (result.length() > maxTokenLength)quitf(_pe, "Length of token exceeds %d, token is '%s...'", int(maxTokenLength), __testlib_part(result).c_str()); cur = reader->nextChar(); } reader->unreadChar(cur); if (result.length() == 0)quit(_unexpected_eof, "Unexpected end of file or white-space - token expected"); } std::string InStream::readToken() { return readWord(); } void InStream::readTokenTo(std::string& result) { readWordTo(result); } static std::string __testlib_part(const std::string& s) { if (s.length() <= 64)return s; else 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) { 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()); } } 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) { 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) { 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) { 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) { readWordTo(result); if (!p.matches(result)) { if (variableName.empty())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()); } } 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) { return readWordTo(result, p, 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) { if (integer == LLONG_MIN)return strcmp(s, "-9223372036854775808") == 0; if (integer == 0LL)return strcmp(s, "0") == 0; size_t length = strlen(s); if (length == 0)return false; if (integer < 0 && s[0] != '-')return false; if (integer < 0)s++, length--, integer = -integer; 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; } #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) { double retval; size_t length = strlen(buffer); int minusCount = 0; int plusCount = 0; int decimalPointCount = 0; int digitCount = 0; int eCount = 0; 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] == '+') { if ('0' <= buffer[i] && buffer[i] <= '9')digitCount++; if (buffer[i] == 'e' || buffer[i] == 'E')eCount++; if (buffer[i] == '-')minusCount++; if (buffer[i] == '+')plusCount++; if (buffer[i] == '.')decimalPointCount++; } else in.quit(_pe, ("Expected double, but \"" + __testlib_part(buffer) + "\" found").c_str()); } 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]; 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))in.quit(_pe, ("Expected double, but \"" + __testlib_part(buffer) + "\" found").c_str()); return retval; } else in.quit(_pe, ("Expected double, but \"" + __testlib_part(buffer) + "\" found").c_str()); } 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."); double retval; size_t length = strlen(buffer); if (length == 0 || length > 1000)in.quit(_pe, ("Expected strict double, but \"" + __testlib_part(buffer) + "\" found").c_str()); 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] == '.') { if (pointPos > -1)in.quit(_pe, ("Expected strict double, but \"" + __testlib_part(buffer) + "\" found").c_str()); pointPos = int(i); } if (buffer[i] != '.' && (buffer[i] < '0' || buffer[i] > '9'))in.quit(_pe, ("Expected strict double, but \"" + __testlib_part(buffer) + "\" found").c_str()); } if (buffer[length - 1] < '0' || buffer[length - 1] > '9')in.quit(_pe, ("Expected strict double, but \"" + __testlib_part(buffer) + "\" found").c_str()); 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()); int firstDigitPos = -1; for (size_t i = 0; i < length; i++)if (buffer[i] >= '0' && buffer[i] <= '9') { firstDigitPos = int(i); break; } 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')in.quit(_pe, ("Expected strict double, but \"" + __testlib_part(buffer) + "\" found").c_str()); 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) || __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 in.quit(_pe, ("Expected double, but \"" + __testlib_part(buffer) + "\" found").c_str()); } 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; if (length > 20)in.quit(_pe, ("Expected integer, but \"" + __testlib_part(buffer) + "\" found").c_str()); long long retval = 0LL; int zeroes = 0; int processingZeroes = true; for (int i = (minus ? 1 : 0); i < int(length); i++) { if (buffer[i] == '0' && processingZeroes)zeroes++; else processingZeroes = false; if (buffer[i] < '0' || buffer[i] > '9')in.quit(_pe, ("Expected integer, but \"" + __testlib_part(buffer) + "\" found").c_str()); retval = retval * 10 + (buffer[i] - '0'); } 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()); retval = (minus ? -retval : +retval); if (length < 19)return retval; if (equals(retval, buffer))return retval; else in.quit(_pe, ("Expected int64, but \"" + __testlib_part(buffer) + "\" found").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") == 1)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()); } int InStream::readInteger() { if (!strict && seekEof())quit(_unexpected_eof, "Unexpected end of file - int32 expected"); readWordTo(_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() { if (!strict && seekEof())quit(_unexpected_eof, "Unexpected end of file - int64 expected"); readWordTo(_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.c_str()); } 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 [" + 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()); } else { if (variableName.empty())quit(_wa, ("Integer element [index=" + vtos(readManyIteration) + "] equals to " + vtos(result) + ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str()); else quit(_wa, ("Integer element " + std::string(variableName) + "[" + vtos(readManyIteration) + "] 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 [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str()); else quit(_wa, ("Unsigned integer parameter [name=" + std::string(variableName) + "] equals to " + vtos(result) + ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str()); } else { if (variableName.empty())quit(_wa, ("Unsigned integer element [index=" + vtos(readManyIteration) + "] equals to " + vtos(result) + ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str()); else quit(_wa, ("Unsigned integer element " + std::string(variableName) + "[" + vtos(readManyIteration) + "] 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::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() { return readInteger(); } 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 [" + 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()); } else { if (variableName.empty())quit(_wa, ("Integer element [index=" + vtos(readManyIteration) + "] equals to " + vtos(result) + ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str()); else quit(_wa, ("Integer element " + std::string(variableName) + "[" + vtos(readManyIteration) + "] 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; } int InStream::readInteger(int minv, int maxv, const std::string& variableName) { return readInt(minv, maxv, variableName); } 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() { if (!strict && seekEof())quit(_unexpected_eof, "Unexpected end of file - double expected"); return stringToDouble(*this, readWord().c_str()); } double InStream::readDouble() { return readReal(); } 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 (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) { 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) { if (!strict && seekEof())quit(_unexpected_eof, "Unexpected end of file - strict double expected"); 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 (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) { return readStrictReal(minv, maxv, 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)return true; return reader->eof(); } bool InStream::seekEof() { if (!strict && NULL == reader)return true; skipBlanks(); return eof(); } bool InStream::eoln() { if (!strict && NULL == reader)return true; int c = reader->nextChar(); if (!strict) { if (c == EOFC)return true; if (c == CR) { c = reader->nextChar(); if (c != LF) { reader->unreadChar(c); reader->unreadChar(CR); return false; } else return true; } if (c == LF)return true; reader->unreadChar(c); return false; } else { bool returnCr = false; #if (defined(ON_WINDOWS) && !defined(FOR_LINUX)) || defined(FOR_WINDOWS) if (c != CR) { reader->unreadChar(c); return false; } else { if (!returnCr)returnCr = true; c = reader->nextChar(); } #endif if (c != LF) { reader->unreadChar(c); if (returnCr)reader->unreadChar(CR); return false; } return true; } } void InStream::readEoln() { lastLine = reader->getLine(); if (!eoln())quit(_pe, "Expected EOLN"); } void InStream::readEof() { lastLine = reader->getLine(); if (!eof())quit(_pe, "Expected EOF"); if (TestlibFinalizeGuard::alive && this == &inf)testlibFinalizeGuard.readEofCount++; } bool InStream::seekEoln() { if (!strict && NULL == reader)return true; int cur; do { cur = reader->nextChar(); } while (cur == SPACE || cur == TAB); reader->unreadChar(cur); return eoln(); } void InStream::nextLine() { readLine(); } void InStream::readStringTo(std::string& result) { if (NULL == reader)quit(_pe, "Expected line"); result.clear(); for (;;) { int cur = reader->curChar(); if (cur == LF || cur == EOFC)break; if (cur == CR) { cur = reader->nextChar(); if (reader->curChar() == LF) { reader->unreadChar(cur); break; } } lastLine = reader->getLine(); result += char(reader->nextChar()); } if (strict)readEoln(); else eoln(); } 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) { 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()); } } } 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) { 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) { 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) { readStringTo(result); } 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) { readStringTo(result, p, 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) { 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) { 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(); delete reader; reader = NULL; } opened = false; } NORETURN void quit(TResult result, const std::string& msg) { ouf.quit(result, msg.c_str()); } NORETURN void quit(TResult result, const char* msg) { ouf.quit(result, msg); } NORETURN void __testlib_quitp(double points, const char* message) { __testlib_points = points; std::string stringPoints = removeDoubleTrailingZeroes(format("%.10f", points)); std::string quitMessage; if (NULL == message || 0 == strlen(message))quitMessage = stringPoints; else quitMessage = stringPoints + " " + 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 = "") { __testlib_quitp(double(points), message.c_str()); } 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 = "") { __testlib_quitp(double(points), message.c_str()); } NORETURN void quitp(int points, const std::string& message = "") { __testlib_quitp(points, message.c_str()); } template #ifdef __GNUC__ __attribute__((format(printf, 2, 3))) #endif NORETURN void quitp(F points, const char* format, ...) { FMT_TO_RESULT(format, format, message); quitp(points, message); } #ifdef __GNUC__ __attribute__((format(printf, 2, 3))) #endif NORETURN void quitf(TResult result, const char* format, ...) { FMT_TO_RESULT(format, format, message); quit(result, message); } #ifdef __GNUC__ __attribute__((format(printf, 3, 4))) #endif void quitif(bool condition, TResult result, const char* format, ...) { if (condition) { FMT_TO_RESULT(format, format, message); quit(result, message); } } static void __testlib_ensuresPreconditions() { __TESTLIB_STATIC_ASSERT(sizeof(int) == 4); __TESTLIB_STATIC_ASSERT(INT_MAX == 2147483647); __TESTLIB_STATIC_ASSERT(sizeof(long long) == 8); __TESTLIB_STATIC_ASSERT(sizeof(double) == 8); if (!__testlib_isNaN(+__testlib_nan()))quit(_fail, "Function __testlib_isNaN is not working correctly: possible reason is '-ffast-math'"); if (!__testlib_isNaN(-__testlib_nan()))quit(_fail, "Function __testlib_isNaN is not working correctly: possible reason is '-ffast-math'"); } 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; __testlib_ensuresPreconditions(); testlibMode = _generator; __testlib_set_binary(stdin); rnd.setSeed(argc, argv); } #ifdef USE_RND_AS_BEFORE_087 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[]) { 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"); registerGen(argc, argv, 0); } #endif void registerInteraction(int argc, char* argv[]) { __testlib_ensuresPreconditions(); testlibMode = _interactor; __testlib_set_binary(stdin); 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"); } if (argc <= 4) { resultName = ""; appesMode = false; } #ifndef EJUDGE if (argc == 5) { resultName = argv[4]; appesMode = false; } if (argc == 6) { 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("'")); ouf.init(stdin, _output); if (argc >= 4)ans.init(argv[3], _answer); else ans.name = "unopened answer stream"; } void registerValidation() { __testlib_ensuresPreconditions(); testlibMode = _validator; __testlib_set_binary(stdin); inf.init(stdin, _input); inf.strict = true; } void registerValidation(int argc, char* argv[]) { registerValidation(); 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); } void registerTestlibCmd(int argc, char* argv[]) { __testlib_ensuresPreconditions(); testlibMode = _checker; __testlib_set_binary(stdin); if (argc < 4 || argc > 6) { quit(_fail, std::string("Program must be run with the following arguments: ") + std::string(" [ [<-appes>]]") + "\nUse \"--help\" to get help information"); } if (argc == 4) { resultName = ""; appesMode = false; } if (argc == 5) { resultName = argv[4]; appesMode = false; } 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 = argv[4]; appesMode = true; } } inf.init(argv[1], _input); ouf.init(argv[2], _output); ans.init(argv[3], _answer); } 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]; va_list ap; va_start(ap, argc); argv[0] = NULL; for (int i = 0; i < argc; i++) { argv[i + 1] = va_arg(ap, char*); } va_end(ap); registerTestlibCmd(argc + 1, argv); delete[] argv; } 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) { if (!cond)quit(_fail, msg); } #define ensure(cond) __testlib_ensure(cond, "Condition failed: \"" #cond "\"") #ifdef __GNUC__ __attribute__((format(printf, 2, 3))) #endif 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) { quitf(_fail, "%s", message.c_str()); } #ifdef __GNUC__ __attribute__((format(printf, 1, 2))) #endif void setName(const char* format, ...) { FMT_TO_RESULT(format, format, name); checkerName = name; } template 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)); } template #if defined(__GNUC__) && !defined(__clang__) __attribute__((error("Don't use random_shuffle(), use shuffle() instead"))) #endif void random_shuffle(_RandomAccessIter, _RandomAccessIter) { quitf(_fail, "Don't use random_shuffle(), use shuffle() instead"); } #ifdef __GLIBC__ #define RAND_THROW_STATEMENT throw() #else #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"); } #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=%d].", seed); } 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) { 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) { 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) { return __testlib_part(s); } inline std::string englishEnding(int x) { x %= 100; if (x / 10 == 1)return "th"; if (x % 10 == 1)return "st"; if (x % 10 == 2)return "nd"; if (x % 10 == 3)return "rd"; return "th"; } template std::string join(_ForwardIterator first, _ForwardIterator last, _Separator separator) { std::stringstream ss; bool repeated = false; for (_ForwardIterator i = first; i != last; i++) { if (repeated)ss << separator; else repeated = true; ss << *i; } return ss.str(); } template std::string join(_ForwardIterator first, _ForwardIterator last) { return join(first, last, ' '); } template std::string join(const _Collection& collection, _Separator separator) { return join(collection.begin(), collection.end(), separator); } template std::string join(const _Collection& collection) { return join(collection, ' '); } 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; } 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; } 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; } 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) { 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()); else message = format("expected '%s', but found '%s'", compress(expected).c_str(), compress(found).c_str()); quit(result, message); } 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 #ifdef __GNUC__ __attribute__((format(printf, 4, 5))) #endif 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 <> #ifdef __GNUC__ __attribute__((format(printf, 4, 5))) #endif 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 <> #ifdef __GNUC__ __attribute__((format(printf, 4, 5))) #endif 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 <> #ifdef __GNUC__ __attribute__((format(printf, 4, 5))) #endif 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 <> #ifdef __GNUC__ __attribute__((format(printf, 4, 5))) #endif 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 <> #ifdef __GNUC__ __attribute__((format(printf, 4, 5))) #endif 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; } #endif #endif