//============================================================================// // validate.h // //============================================================================// // This header can be used to safely parse team output tokenwise. We support: // // - string tokens (get converted to lowercase) // // - integer tokens in [-2^63, 2^63) // // - float tokens (relative and absolute error of 10^6 is allowed by default) // // Tokens need to be separated by whitespace (any amount). The following // // command line flags allow stricter checking: // // - caseSensitive: string tokens don't get converted to lowercase // // - space_change_sensitive: tokens need to be separated by the corect // // amount of whitespaces // // - FLOAT_{RELATIVE|ABSOLUTE}_TOLERANCE: allowed relative/absolute error // // // // This header can also be used to safely verify input files. In this case // // tokens are case sensitive and all whitespaces have to be checked. Also // // whitespaces are not interchangeable. // // // // This header can be used to generate random numbers in a deterministic and // // reproducable fashion. (The randomness is consistent across compilers and // // machines) // //============================================================================// // version 2.3.2 // // https://github.com/mzuenni/icpc-header // //============================================================================// #ifndef VALIDATE_H #define VALIDATE_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //============================================================================// // Basic definitions and constants // //============================================================================// // default types using Integer = std::int64_t; using Real = long double; // derived types using UInteger = std::make_unsigned::type; constexpr Integer operator ""_int(unsigned long long int value) {return static_cast(value);} constexpr UInteger operator ""_uint(unsigned long long int value) {return static_cast(value);} constexpr Real operator ""_real(unsigned long long int value) {return static_cast(value);} constexpr Real operator ""_real(long double value) {return static_cast(value);} // settings which can be overwritten before the include! //#define DOUBLE_FALLBACK namespace Settings { namespace details { using RandomEngine = std::mt19937_64; constexpr Integer LARGE = 0x3FFF'FFFF'FFFF'FFFF; constexpr bool DEFAULT_CASE_LOWER = true; constexpr int DEFAULT_PRECISION = 6; constexpr Real DEFAULT_EPS = 1e-6_real; [[noreturn]] void exitVerdict(int exitCode) { //throw exitCode; //quick_exit(exitCode); std::exit(exitCode); } } using namespace details; } // make settings publically available using Settings::RandomEngine; using Settings::LARGE; using Settings::DEFAULT_CASE_LOWER; using Settings::DEFAULT_PRECISION; using Settings::DEFAULT_EPS; using Settings::exitVerdict; // useful constants constexpr std::string_view LETTER = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; constexpr std::string_view UPPER = LETTER.substr(0, 26); constexpr std::string_view LOWER = LETTER.substr(26); constexpr std::string_view VOWEL = "AEIOUaeiou"; constexpr std::string_view UPPER_VOWELS = VOWEL.substr(0, 5); constexpr std::string_view LOWER_VOWELS = VOWEL.substr(5); constexpr std::string_view CONSONANT = "BCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz"; constexpr std::string_view UPPER_CONSONANT = CONSONANT.substr(0, 26 - 5); constexpr std::string_view LOWER_CONSONANT = CONSONANT.substr(26 - 5); constexpr std::string_view ALPHA_NUMERIC = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; constexpr std::string_view UPPER_ALPHA_NUMERIC = ALPHA_NUMERIC.substr(0, 10 + 26); constexpr std::string_view LOWER_ALPHA_NUMERIC = "0123456789abcdefghijklmnopqrstuvwxyz"; constexpr std::string_view DIGITS = ALPHA_NUMERIC.substr(0, 10); constexpr std::string_view BRACKETS = "()[]{}<>"; constexpr char NEWLINE = '\n'; constexpr char SPACE = ' '; constexpr char NOSEP = '\0'; constexpr Real PI = 3.1415926535897932384626433832795028_real; //============================================================================// // internal definitions and constants // //============================================================================// constexpr UInteger DEFAULT_SEED = 3141592653589793238_uint; constexpr std::string_view CASE_SENSITIVE = "case_sensitive"; constexpr std::string_view SPACE_SENSITIVE = "space_change_sensitive"; constexpr std::string_view FLOAT_ABSOLUTE_TOLERANCE = "float_absolute_tolerance"; constexpr std::string_view FLOAT_RELATIVE_TOLERANCE = "float_relative_tolerance"; constexpr std::string_view FLOAT_TOLERANCE = "float_tolerance"; constexpr std::string_view JUDGE_MESSAGE = "judgemessage.txt"; constexpr char DEFAULT_SEPARATOR = SPACE; constexpr std::string_view EMPTY_COMMAND = ""; constexpr std::string_view COMMAND_PREFIX = "--"; constexpr std::string_view CONSTRAINT_COMMAND = "--constraints_file"; constexpr std::string_view SEED_COMMAND = "--seed"; constexpr auto REGEX_OPTIONS = std::regex::nosubs | std::regex::optimize; inline const std::regex INTEGER_REGEX("0|-?[1-9][0-9]*", REGEX_OPTIONS); inline const std::regex REAL_REGEX("-?(0|([1-9][0-9]*))(\\.[0-9]*)?([eE][+-]?(0|([1-9][0-9]*)))?", REGEX_OPTIONS); inline const std::regex STRICT_REAL_REGEX("-?(0|([1-9][0-9]*))\\.?[0-9]*", REGEX_OPTIONS); static_assert(2'000'000'000'000'000'000_int < LARGE / 2, "LARGE too small"); static_assert(LARGE <= std::numeric_limits::max() / 2, "LARGE too big"); static_assert(-1 == 0xFFFF'FFFF'FFFF'FFFF_int, "Two's complement for signed numbers is required" ); static_assert(std::is_convertible_v, "Incompatible Integer and UInteger types?!"); static_assert(std::is_convertible_v, "Incompatible Integer and UInteger types?!"); static_assert(sizeof(Integer) == sizeof(UInteger), "Incompatible Integer and UInteger types?!"); template constexpr void judgeAssert(bool asserted, std::string_view message) { if (!asserted) throw T(message.data()); } //============================================================================// // SFINAE // //============================================================================// namespace details { template struct IsContainer : std::false_type {}; template struct IsContainer>()))>> : std::true_type { using iterator_type = decltype(std::begin(std::declval>())); using value_type = std::remove_reference_t>()))>; }; template struct IsStdArray : std::false_type {}; template struct IsStdArray> : std::true_type {}; template struct IsTupleLike : std::false_type {}; template struct IsTupleLike))>> : std::true_type {}; template struct HasOstreamOperator : std::false_type {}; template struct HasOstreamOperator() << std::declval())>> : std::true_type {}; } //============================================================================// // Verdicts // //============================================================================// struct Verdict final { int exitCode; constexpr explicit Verdict(int exitCode_ = 1) : exitCode(exitCode_) {} constexpr operator int() const { return exitCode; } [[noreturn]] void exit() const { exitVerdict(exitCode); } friend void operator<<(std::ostream& os, const Verdict& v) { os << std::endl; v.exit(); } }; // default verdicts (we do not support scoring) constexpr Verdict AC(42); constexpr Verdict WA(43); constexpr Verdict PE = WA; constexpr Verdict FAIL(1); //============================================================================// // Output streams // //============================================================================// class NullStream final : public std::ostream { class NullBuffer final : public std::streambuf { protected: std::streamsize xsputn(const char* /**/, std::streamsize n) override { return n; } int overflow(int c = std::char_traits::eof()) override { return std::char_traits::not_eof(c); } } nullBuffer; public: NullStream() : std::ostream(&nullBuffer) {} }; namespace details { NullStream nullStream; } class OutputStream final { std::unique_ptr managed; std::ostream* os; void init() { *os << std::boolalpha; *os << std::fixed; *os << std::setprecision(DEFAULT_PRECISION); } public: OutputStream() : os(&details::nullStream) {} OutputStream(std::ostream& os_) : os(&os_) {init();} explicit OutputStream(const std::filesystem::path& path) : managed(std::make_unique(path)), os(managed.get()) { judgeAssert(managed->good(), "OutputStream: Could not open File: " + path.string()); init(); } OutputStream(OutputStream&& other) = default; OutputStream& operator=(OutputStream&& other) = default; OutputStream(const OutputStream&) = delete; OutputStream& operator=(const OutputStream&) = delete; template OutputStream& operator<<(const std::pair& t) { return *this << t.first << DEFAULT_SEPARATOR << t.second; } template OutputStream& operator<<(const std::tuple& t) { return join(t, std::index_sequence_for(), DEFAULT_SEPARATOR); } template OutputStream& operator<<(const T& x) { if constexpr ((std::is_array_v and !std::is_same_v, char*>) or (details::IsContainer{} and !details::HasOstreamOperator{})) { return join(std::begin(x), std::end(x), DEFAULT_SEPARATOR); } else { *os << x; return *this; } } OutputStream& operator<<(std::ostream& (*manip)(std::ostream&)) { *os << manip; return *this; } template OutputStream& join(const Tuple& t, std::index_sequence /**/, char separator) { static_assert(std::tuple_size_v == sizeof...(Is)); if (separator != NOSEP) ((*os << (Is == 0 ? std::string_view() : std::string_view(&separator, 1)), *this << std::get(t)), ...); else ((*this << std::get(t)), ...); return *this; } template OutputStream& join(T first, T last, char separator) { for (auto it = first; it != last; it++) { if (it != first and separator != NOSEP) *os << separator; *this << *it; } return *this; } }; namespace ValidateBase { // define this early so everyone can use it! OutputStream juryErr(std::cerr); OutputStream juryOut(std::cout); } // allow printing colletions as: // join(begin(), end(), [sep]) namespace details { template class TempWriter final { C callable; public: constexpr explicit TempWriter(const C& callable_) : callable(callable_) {} TempWriter(const TempWriter&) = delete; TempWriter(TempWriter&&) = delete; TempWriter& operator=(const TempWriter&) = delete; TempWriter& operator=(TempWriter&&) = delete; std::string asString() const { std::ostringstream os; OutputStream tmp(os); tmp << *this; return os.str(); } explicit operator std::string() const { return asString(); } friend OutputStream& operator<<(OutputStream& os, const TempWriter& writer) { writer.callable(os); return os; } friend OutputStream& operator<<(std::ostream& os, const TempWriter& writer) = delete; //news OutputStream }; struct JoinListCapture { std::function callable; template JoinListCapture(Args&&... args) : callable([t = std::forward_as_tuple(args...)](OutputStream& os, char separator) { os.join(t, std::index_sequence_for(), separator); }) {} }; } template constexpr auto join(T first, T last, char separator = DEFAULT_SEPARATOR) { return details::TempWriter([=](OutputStream& os) { os.join(first, last, separator); }); } template{}>, typename = std::enable_if_t>>{}>> constexpr auto join(CR&& c, char separator = DEFAULT_SEPARATOR) { if constexpr(std::is_rvalue_reference_v) { if constexpr (std::is_array_v) { return details::TempWriter([c, separator](OutputStream& os) { os.join(std::begin(c), std::end(c), separator); }); } else { return details::TempWriter([c = std::move(c), separator](OutputStream& os) { os.join(std::begin(c), std::end(c), separator); }); } } else { return join(std::begin(c), std::end(c), separator); } } template>::value> constexpr auto join(CR&& c, char separator = DEFAULT_SEPARATOR) { if constexpr(std::is_rvalue_reference_v) { return details::TempWriter([c = std::move(c), separator](OutputStream& os) { os.join(c, std::make_index_sequence{}, separator); }); } else { return details::TempWriter([&c, separator](OutputStream& os) { os.join(c, std::make_index_sequence{}, separator); }); } } template, char>>> constexpr auto join(T (&c)[N], char separator = DEFAULT_SEPARATOR) { static_assert(N > 0, "c-strings should be null terminated!"); return join(std::begin(c), std::prev(std::end(c)), separator); } template, char>>> constexpr auto join(T (&&c)[N], char separator = DEFAULT_SEPARATOR) { static_assert(N > 0, "c-strings should be null terminated!"); return details::TempWriter([c, separator](OutputStream& os) { os.join(std::begin(c), std::prev(std::end(c)), separator); }); } template{}>, typename = std::enable_if_t{}>, typename = std::enable_if_t{}>> constexpr auto join(const T& t, char separator = DEFAULT_SEPARATOR) = delete; auto join(details::JoinListCapture c, char separator = DEFAULT_SEPARATOR) { return details::TempWriter([c = std::move(c), separator](OutputStream& os) { c.callable(os, separator); }); } //============================================================================// // Basic datastructures // //============================================================================// // make usage of std::priority_queue easier... namespace details { template> struct invertCompare { constexpr bool operator()(const T &lhs, const T &rhs) const { return Compare{}(rhs, lhs); } }; } template> using MinPQ = std::priority_queue, details::invertCompare>; template> using MaxPQ = std::priority_queue, Compare>; template bool contains(const C& container, const K& key) { return container.find(key) != container.end(); } template void append(C1& c1, const C2& c2) { static_assert(std::is_same_v::value_type, typename details::IsContainer::value_type>, "cannot append container of different value type!"); if (static_cast(&c1) != static_cast(&c2)) { for (auto&& e : c2) c1.emplace(c1.end(), e); } else { C2 tmp = c2; for (auto&& e : tmp) c1.emplace(c1.end(), e); } } template void append(C1& c1, const typename C1::value_type(&c2)[N]) { for (auto&& e : c2) c1.emplace(c1.end(), e); } struct shorter { template bool operator()(const U& a, const V& b) const { return std::size(a) < std::size(b); } }; struct longer { template bool operator()(const U& a, const V& b) const { return std::size(b) < std::size(a); } }; namespace details { template struct Flatten {using value_type = T;}; template struct Flatten{}>> : Flatten::value_type> {}; template void flatAppend(CR&& c, std::vector& res) { using C = std::remove_reference_t; if constexpr(std::is_same_v) { res.emplace_back(std::forward(c)); } else if constexpr (!IsContainer{}) { static_assert(IsContainer{}, "invalid base type for flatten()!"); } else { if constexpr (std::is_rvalue_reference_v) { for (auto&& v : c) flatAppend(std::move(v), res); } else { for (auto&& v : c) flatAppend(v, res); } } } } template auto flatten(CR&& c) { std::vector res; details::flatAppend(std::forward(c), res); return res; } template auto flatten(CR&& c) { using C = std::remove_reference_t; return flatten::value_type, CR>(std::forward(c)); } template struct boolean { bool value; std::optional reason; constexpr boolean(bool value_) : value(value_) {} constexpr boolean(bool value_, const T& reason_) : value(value_), reason(reason_) {} constexpr operator bool() const { return value; } constexpr bool hasReason() const { return reason.has_value(); } }; //============================================================================// // Utility // //============================================================================// // for sequences template::value_type>>> auto isPerm(RandomIt first, RandomIt last, typename std::iterator_traits::value_type offset = 0) { using T = typename std::iterator_traits::value_type; auto count = std::distance(first, last); std::vector seen(count, false); for (; first != last; first++) { const T& x = *first; if (x < offset or x - offset >= count or seen[x - offset]) { return boolean(false, x); } seen[x - offset] = true; } return boolean(true); } template::value_type>, bool> = true> auto isPerm(const C& c, typename details::IsContainer::value_type offset = 0) { return isPerm(std::begin(c), std::end(c), offset); } template auto isPerm(itA firstA, itA lastA, itB firstB, itB lastB) { using T = typename std::iterator_traits::value_type; std::vector::value_type> a(firstA, lastA); std::vector::value_type> b(firstB, lastB); if (a.size() != b.size()) return boolean(false); std::sort(a.begin(), a.end()); std::sort(b.begin(), b.end()); for (std::size_t i = 0; i < a.size(); i++) { if (a[i] != b[i]) return boolean(false, a[i]); } return boolean(true); } template{}>, typename = std::enable_if_t{}>> auto isPerm(const C1& c1, const C2& c2) { return isPerm(std::begin(c1), std::end(c1), std::begin(c2), std::end(c2)); } template constexpr boolean anyAdjacent(RandomIt first, RandomIt last, BinaryPredicate p) { if (first != last) { for (Integer i = 1; std::next(first) != last; first++, i++) { if (p(*first, *std::next(first))) { return boolean(true, i); } } } return boolean(false); } template constexpr boolean anyAdjacent(const C& c, BinaryPredicate p) { return anyAdjacent(std::begin(c), std::end(c), p); } template constexpr boolean noneAdjacent(RandomIt first, RandomIt last, BinaryPredicate p) { auto res = anyAdjacent(first, last, p); res.value = !res.value; return res; } template constexpr boolean noneAdjacent(const C& c, BinaryPredicate p) { return noneAdjacent(std::begin(c), std::end(c), p); } template constexpr boolean allAdjacent(RandomIt first, RandomIt last, BinaryPredicate p) { return noneAdjacent(first, last, std::not_fn(p)); } template constexpr boolean allAdjacent(const C& c, BinaryPredicate p) { return noneAdjacent(std::begin(c), std::end(c), p); } template constexpr boolean areIncreasing(RandomIt first, RandomIt last) { using T = typename std::iterator_traits::value_type; return allAdjacent(first, last, std::less()); } template constexpr boolean areIncreasing(const C& c) { return areIncreasing(std::begin(c), std::end(c)); } template constexpr boolean areNonDecreasing(RandomIt first, RandomIt last) { using T = typename std::iterator_traits::value_type; return allAdjacent(first, last, std::less_equal()); } template constexpr boolean areNonDecreasing(const C& c) { return areNonDecreasing(std::begin(c), std::end(c)); } template constexpr boolean areDecreasing(RandomIt first, RandomIt last) { using T = typename std::iterator_traits::value_type; return allAdjacent(first, last, std::greater()); } template constexpr boolean areDecreasing(const C& c) { return areDecreasing(std::begin(c), std::end(c)); } template constexpr boolean areNonIncreasing(RandomIt first, RandomIt last) { using T = typename std::iterator_traits::value_type; return allAdjacent(first, last, std::greater_equal()); } template constexpr boolean areNonIncreasing(const C& c) { return areNonIncreasing(std::begin(c), std::end(c)); } template constexpr auto areDistinct(RandomIt first, RandomIt last) { using T = typename std::iterator_traits::value_type; std::vector tmp(first, last); std::sort(tmp.begin(), tmp.end()); auto [b, v] = anyAdjacent(tmp, std::equal_to()); if (v) return boolean(!b, tmp[*v]); return boolean(!b); } template constexpr auto areDistinct(const C& c) { return areDistinct(std::begin(c), std::end(c)); } // for strings (cctype functions are not safe to use with char...) constexpr bool isLower(char c) { return c >= 'a' and c <= 'z'; } constexpr bool isUpper(char c) { return c >= 'A' and c <= 'Z'; } constexpr bool isLetter(char c) { return isLower(c) or isUpper(c); } constexpr bool isDigit(char c) { return c >= '0' and c <= '9'; } constexpr char toLower(char c) { if (isUpper(c)) c += 'a' - 'A'; return c; } constexpr bool isVowel(char c) { c = toLower(c); for (char x : LOWER_VOWELS) { if (c == x) return true; } return false; } constexpr bool isConsonant(char c) { return isLetter(c) and !isVowel(c); } constexpr char toUpper(char c) { if (isLower(c)) c -= 'a' - 'A'; return c; } constexpr char toDefaultCase(char c) { if constexpr (DEFAULT_CASE_LOWER) return toLower(c); return toUpper(c); } void toLower(std::string& s) { for (char& c : s) c = toLower(c); } void toUpper(std::string& s) { for (char& c : s) c = toUpper(c); } void toDefaultCase(std::string& s) { if constexpr (DEFAULT_CASE_LOWER) return toLower(s); return toUpper(s); } constexpr bool isLower(std::string_view s) { for (char c : s) if (!isLower(c)) return false; return true; } constexpr boolean isUpper(std::string_view s) { for (char c : s) if (!isUpper(c)) return boolean(false, c); return boolean(true); } constexpr boolean isLetter(std::string_view s) { for (char c : s) if (!isLetter(c)) return boolean(false, c); return boolean(true); } constexpr boolean isDigit(std::string_view s) { for (char c : s) if (!isDigit(c)) return boolean(false, c); return boolean(true); } constexpr boolean isVowel(std::string_view s) { for (char c : s) if (!isVowel(c)) return boolean(false, c); return boolean(true); } constexpr boolean isConsonant(std::string_view s) { for (char c : s) if (!isConsonant(c)) return boolean(false, c); return boolean(true); } std::vector thueMorse(Integer lower, Integer upper) { judgeAssert(lower < upper, "thueMorse(): Lower must be less than upper!"); std::vector res(upper - lower); for (Integer i = lower; i < upper; i++) { res[i] = std::bitset<64>(i).count() % 2; } return res; } std::vector thueMorse(Integer upper) { return thueMorse(0, upper); } // allow using std::pair and std::complex similiar // (may be useful for geometric problem) template constexpr auto& getX(T& point) { return std::get<0>(point); } template constexpr auto& getY(T& point) { return std::get<1>(point); } template constexpr auto& getZ(T& point) { return std::get<2>(point); } template constexpr auto getX(const T& point) { return std::get<0>(point); } template constexpr auto getY(const T& point) { return std::get<1>(point); } template constexpr auto getZ(const T& point) { return std::get<2>(point); } template constexpr auto& getX(std::complex& point) { return reinterpret_cast(point)[0]; } template constexpr auto& getY(std::complex& point) { return reinterpret_cast(point)[1]; } template constexpr auto getX(const std::complex& point) { return reinterpret_cast(point)[0]; } template constexpr auto getY(const std::complex& point) { return reinterpret_cast(point)[1]; } template constexpr std::pair convert(const std::complex& t) { return {getX(t), getY(t)}; } template constexpr std::complex::type> convert(const std::pair& t) { return {getX(t), getY(t)}; } namespace details { // Test two numbers for equality, accounting for +/-INF, NaN and precision. // Real expected is considered the reference value for relative error. bool floatEqual(Real given, Real expected, Real floatAbsTol, Real floatRelTol) { judgeAssert(floatAbsTol >= 0.0_real, "floatEqual(): floatAbsTol must be positive!"); judgeAssert(floatRelTol >= 0.0_real, "floatEqual(): floatRelTol must be positive!"); // Finite values are compared with some tolerance if (std::isfinite(given) and std::isfinite(expected)) { Real absDiff = std::abs(given-expected); Real relDiff = std::abs((given-expected)/expected); return absDiff <= floatAbsTol or relDiff <= floatRelTol; } // NaN is equal to NaN (-NaN is also equal NaN) if (std::isnan(given) and std::isnan(expected)) { return true; } // Infinite values are equal if their sign matches if (std::isinf(given) and std::isinf(expected)) { return std::signbit(given) == std::signbit(expected); } // Values in different classes are always different. return false; } constexpr boolean stringEqual(std::string_view a, std::string_view b, bool caseSensitive) { std::size_t i = 0; for (; i < a.size() and i < b.size(); i++) { char aa = a[i]; char bb = b[i]; if (!caseSensitive) { aa = toDefaultCase(aa); bb = toDefaultCase(bb); } if (aa != bb) { return boolean(false, i); } } if (a.size() != b.size()) { return boolean(false, i); } else { return boolean(true); } } constexpr bool isToken(std::string_view a) { for (char c : a) { if (c == ' ') return false; if (c == '\n') return false; if (c == '\r') return false; if (c == '\t') return false; if (c == '\f') return false; if (c == '\v') return false; } return true; } template bool parse(std::string_view s, T& res) { const char* begin = s.data(); const char* end = s.data() + s.size(); auto [ptr, ec] = std::from_chars(begin, end, res); return ptr == end and ec == std::errc(); } #ifdef DOUBLE_FALLBACK template<> bool parse(std::string_view s, Real& res) { try { std::size_t pos = 0; res = std::stold(std::string(s), &pos); return pos == s.size(); } catch(...) { return false; } } #endif } //============================================================================// // Math // //============================================================================// namespace details { constexpr std::array TRIAL_PRIMES = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, }; constexpr std::array MILLER_RABIN_WITNESS = {2, 325, 9375, 28178, 450775, 9780504, 1795265022}; // these operations are safe as long as the value would fit in Integer constexpr UInteger mulMod(UInteger lhs, UInteger rhs, UInteger mod) { UInteger res = 0; while (rhs > 0) { if (rhs & 1) res = (lhs + res) % mod; lhs = (lhs + lhs) % mod; rhs /= 2; } return res; } constexpr UInteger powMod(UInteger base, UInteger exp, UInteger mod) { UInteger res = 1; if (mod <= 0x1'0000'0000) { while (exp > 0) { if (exp & 1) res = (base * res) % mod; base = (base * base) % mod; exp /= 2; } } else { while (exp > 0) { if (exp & 1) res = mulMod(base, res, mod); base = mulMod(base, base, mod); exp /= 2; } } return res; } constexpr Integer extendedEuclid(Integer a, Integer b, Integer& x, Integer& y) { if (a == 0) { x = 0; y = 1; return b; } else { Integer x1 = 0; Integer y1 = 0; Integer d = extendedEuclid(b % a, a, x1, y1); x = y1 - (b / a) * x1; y = x1; return d; } } } constexpr Integer applyMod(Integer x, Integer mod) { x %= mod; if (x < 0) x += mod; return x; } constexpr Integer mulMod(Integer lhs, Integer rhs, Integer mod) { judgeAssert(mod > 0, "mulMod(): mod must be positive!"); UInteger ul = static_cast(applyMod(lhs, mod)); UInteger ur = static_cast(applyMod(rhs, mod)); UInteger um = static_cast(mod); return static_cast(details::mulMod(ul, ur, um)); } constexpr Integer powMod(Integer base, Integer exp, Integer mod) { judgeAssert(mod > 0, "powMod(): mod must be positive!"); judgeAssert(exp >= 0, "powMod(): exp must be non negative!"); UInteger ub = static_cast(applyMod(base, mod)); UInteger ue = static_cast(exp); UInteger um = static_cast(mod); return static_cast(details::powMod(ub, ue, um)); } constexpr Integer multInv(Integer n, Integer mod) { judgeAssert(mod > 0, "multInv(): mod must be positive!"); Integer x = 0; Integer y = 0; Integer g = details::extendedEuclid(n, mod, x, y); if (g != 1) return -1; else return applyMod(x, mod); } constexpr bool isPrime(Integer n) { for (Integer p : details::TRIAL_PRIMES) { if (n <= p or n % p == 0) { return n == p; } } if (details::powMod(details::TRIAL_PRIMES.back() + 1, n - 1, n) != 1) { return false; } UInteger un = static_cast(n); UInteger d = un - 1; UInteger j = 0; while (d % 2 == 0) { d /= 2; j++; } for (UInteger a : details::MILLER_RABIN_WITNESS) { if (a % un == 0) continue; UInteger v = details::powMod(a, d, un); if (v == 1 or v == un - 1) continue; for (UInteger i = 1; i < j; i++) { v = details::mulMod(v, v, un); if (v == un - 1 or v <= 1) break; } if (v != un - 1) return false; } return true; } std::vector primes(Integer lower, Integer upper) { judgeAssert(lower < upper, "primes(): Lower must be less than upper!"); lower = std::max(2, lower); upper = std::max(2, upper); Integer count = upper - lower; Integer cache = (count + 1) / 2; std::vector notPrime(cache), notPrimeSegment(cache); for (Integer i = 3; i < count; i += 2) { if (!notPrime[i / 2]) { for (Integer j = i * i; j < count; j += 2 * i) { notPrime[j / 2] = true; } Integer lowest = lower - (lower % (2*i)) + i; if (lowest < lower) lowest += 2*i; for (Integer j = std::max(i * i, lowest); j < upper; j += 2 * i) { notPrimeSegment[(j - lower) / 2] = true; } } } std::vector res; if (lower <= 2 and 2 < upper) res.emplace_back(2); for (Integer i = lower | 1; i < upper; i += 2) { if (!notPrimeSegment[(i - lower) / 2] and (i < count*count or isPrime(i))) { res.emplace_back(i); } } return res; } std::vector primes(Integer upper) { return primes(0, upper); } template constexpr Integer sign(T x) { return (T(0) < x) - (x < T(0)); } //============================================================================// // Geometry (this is just for utility stuff...) // //============================================================================// namespace details { template constexpr Integer cross(Point a, Point b) { return getX(a) * getY(b) - getY(a) * getX(b); } template constexpr Integer cross(Point p, Point a, Point b) { getX(a) -= getX(p); getY(a) -= getY(p); getX(b) -= getX(p); getY(b) -= getY(p); return cross(a, b); } template constexpr bool left(Point p) { return getX(p) == 0 ? getY(p) < 0 : getX(p) < 0; } template void cyclicSort(std::vector& in) { std::sort(in.begin(), in.end(), [](const Point& a, const Point& b){ return left(a) != left(b) ? left(a) > left(b) : cross(a, b) > 0; }); } } template constexpr bool areConvex(RandomIt first, RandomIt last) { std::size_t n = 0; for (auto it = first; it != last; it++) { n++; judgeAssert(std::abs(getX(*it)) <= 0x3FFF'FFFF, "areConvex(): coordinates too large!"); judgeAssert(std::abs(getY(*it)) <= 0x3FFF'FFFF, "areConvex(): coordinates too large!"); } if (n < 3) return false; bool hasArea = false; for (std::size_t i = 0; i < n; i++) { if (first[i] == first[(i+1) % n]) return false; if (details::cross(first[0], first[i], first[(i+1) % n]) < 0) return false; if (details::cross(first[i], first[(i+1) % n], first[(i+2) % n]) < 0) return false; hasArea |= details::cross(first[i], first[(i+1) % n], first[(i+2) % n]) != 0; } return hasArea; } template constexpr bool areConvex(const C& c) { return areConvex(std::begin(c), std::end(c)); } template constexpr bool areStrictlyConvex(RandomIt first, RandomIt last) { if (!areConvex(first, last)) return false; std::size_t n = std::distance(first, last); for (std::size_t i = 0; i < n; i++) { if (details::cross(first[i], first[(i+1) % n], first[(i+2) % n]) == 0) return false; } return true; } template constexpr bool areStrictlyConvex(const C& c) { return areStrictlyConvex(std::begin(c), std::end(c)); } //============================================================================// // Random // //============================================================================// namespace Random { // You should not rely on the implementation in details! // Especially you should never use randomNumberGenerator on your own. There is no function in // c++ which uses a random engine and is not implementation defined. namespace details { constexpr Real PI = 3.141592653589793238462643383279502884_real; constexpr Integer PRIME_TRIALS = 4*1600; RandomEngine randomNumberGenerator(DEFAULT_SEED); static_assert(RandomEngine::max() == 0xFFFF'FFFF'FFFF'FFFF_uint, "Random Engine should produce 64bit of randomness"); static_assert(RandomEngine::min() == 0_uint, "Random Engine should produce 64bit of randomness"); constexpr UInteger bitMask(UInteger x) { static_assert(sizeof(UInteger) == 8, "bitMask requires 8byte UInteger!"); x |= x >> 1; x |= x >> 2; x |= x >> 4; x |= x >> 8; x |= x >> 16; x |= x >> 32; return x; } } void seed(UInteger seed) { details::randomNumberGenerator.seed(seed); } //========================================================================// // Distributions and generators // //========================================================================// bool bit() {// in {0, 1} return std::bitset<64>(details::randomNumberGenerator()).count() & 1; } Integer integer() {// in [-2^63, 2^63) return static_cast(details::randomNumberGenerator()); } Integer integer(Integer lower, Integer upper) {// in [lower, upper) judgeAssert(lower < upper, "Random::integer(): Lower must be less than upper!"); UInteger ul = static_cast(lower); UInteger uu = static_cast(upper); UInteger mask = details::bitMask(uu - ul - 1_uint); UInteger res; do { res = details::randomNumberGenerator() & mask; } while (res >= uu - ul); return static_cast(res + ul); } Integer integer(Integer upper) {// in [0, upper) return integer(0, upper); } Real real() {// in [0, 1) while (true) { Real res = details::randomNumberGenerator() / 0x1.0p64_real; res += details::randomNumberGenerator() / 0x1.0p128_real; if (0.0_real <= res and res < 1.0_real) return res; } } Real real(Real upper) {// in [0, upper) judgeAssert(std::isfinite(upper), "Random::real(): Upper must be finite!"); judgeAssert(upper > 0.0_real, "Random::real(): Upper must be greater than zero!"); while (true) { Real res = real() * upper; if (0.0_real <= res and res < upper) return res; } } Real real(Real lower, Real upper) {// in [lower, upper) judgeAssert(std::isfinite(lower), "Random::real(): Lower must be finite!"); judgeAssert(std::isfinite(upper), "Random::real(): Upper must be finite!"); judgeAssert(lower < upper, "Random::real(): Lower must be less than upper!"); while (true) { Real x = real(); Real res = lower * (1.0_real - x) + upper * x; if (lower <= res and res < upper) return res; } } Real normal(Real mean, Real stddev) {// theoretically in (-inf, inf) judgeAssert(stddev >= 0.0_real, "Random::normal(): Standard deviation must be non negative!"); Real u1 = real(); Real u2 = real(); Real res = std::sqrt(-2.0_real * std::log(u1)) * std::cos(2.0_real * details::PI * u2); return std::sqrt(stddev) * res + mean; } Real normal(Real lower, Real upper, Real mean, Real stddev) {// in [lower, upper) judgeAssert(!std::isnan(lower), "Random::normal(): Lower must not be NaN!"); judgeAssert(!std::isnan(upper), "Random::normal(): Upper must not be NaN!"); judgeAssert(lower < upper, "Random::normal(): Lower must be less than upper!"); judgeAssert(stddev >= 0.0_real, "Random::normal(): Standard deviation must be non negative!"); Real res; while (true) { Real u1 = real(); Real u2 = real(); // Box-Muller-Methode // https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform res = std::sqrt(-2.0_real * std::log(u1)) * std::cos(2.0_real * details::PI * u2); res = std::sqrt(stddev) * res + mean; if (lower <= res and res < upper) return res; res = std::sqrt(-2.0_real * std::log(u1)) * std::sin(2.0_real * details::PI * u2); res = std::sqrt(stddev) * res + mean; if (lower <= res and res < upper) return res; } } Real exponential(Real lambda) {// theoretically in [0, inf) judgeAssert(lambda > 0.0_real, "Random::lambda(): lambda must be positive!"); return -std::log(real()) / lambda; } Real exponential(Real lower, Real upper, Real lambda) {// in [lower, upper) judgeAssert(std::isfinite(lower), "Random::exponential(): Lower must be finite!"); judgeAssert(!std::isnan(upper), "Random::exponential(): Upper must not be NaN!"); judgeAssert(lower < upper, "Random::exponential(): Lower must be less than upper!"); judgeAssert(lambda > 0.0_real, "Random::exponential(): Lambda must be positive!"); while (true) { Real res = lower - std::log(real()) / lambda; if (res < upper) return res; } } Integer geometric(Real p) {// theoretically in [0, inf) judgeAssert(0.0_real <= p and p < 1.0_real, "Random::geometric(): p must be in [0,1)!"); return std::llround(std::floor(std::log(real()) / std::log1p(-p))); } Integer geometric(Integer lower, Integer upper, Real p) {// in [lower, upper) judgeAssert(lower < upper, "Random::geometric(): Lower must be less than upper!"); judgeAssert(0.0_real <= p and p < 1.0_real, "Random::geometric(): p must be in [0,1)!"); while (true) { // https://en.wikipedia.org/wiki/Geometric_distribution // "The exponential distribution is the continuous analogue of the geometric distribution[...]" Integer res = lower + std::llround(std::floor(std::log(real()) / std::log1p(-p))); if (res < upper) return res; } } Integer binomial(Integer n, Real p) {// in [0, n] judgeAssert(n >= 0, "Random::binomial(): n must be non negative!"); judgeAssert(0.0_real <= p and p <= 1.0_real, "Random::binomial(): p must be in [0,1)!"); bool swap = p > 0.5_real; p = std::min(p, 1.0_real - p); if (p*n < 10.0_real) { // BG: Geometric method // https://dl.acm.org/doi/pdf/10.1145/42372.42381 Integer res = 0; Integer y = 0; Real lg = std::log1p(-p); if (lg >= 0) return swap ? n : 0; do { y += std::llround(std::floor(std::log(real()) / lg)) + 1; if (y > n) return swap ? n - res : res; res++; } while (true); } else { // BTRS algorithm // https://epub.wu.ac.at/1242/1/document.pdf // note that the original paper has an error // the break condition at the end has to be log(v) < h[...] Real q = 1.0_real - p; Real spq = std::sqrt(n * p * q); Real b = 1.15_real + 2.53_real * spq; Real a = -0.0873_real + 0.0248_real * b + 0.01_real * p; Real c = n * p + 0.5_real; Real vr = 0.92_real - 4.2_real / b; bool initialized = false; Real alpha, lpq, m, h; do { Real u = real() - 0.5_real; Real us = 0.5_real - std::abs(u); Integer res = std::llround(std::floor((2.0_real * a / us + b) * u + c)); if (res < 0 or res > n) continue; Real v = real(); if (us >= 0.07_real and v <= vr) { return swap ? n - res : res; } if (!initialized) { alpha = (2.83_real + 5.1_real / b) * spq; lpq = std::log(p / q); m = std::llround(std::floor((n + 1) * p)); h = std::lgamma(m + 1) + std::lgamma(n - m + 1); initialized = true; } v *= alpha / (a / (us * us) + b); if (std::log(v) <= h - std::lgamma(res + 1) - std::lgamma(n - res + 1) + (res - m) * lpq) { return swap ? n - res : res; } } while (true); } } Integer binomial(Integer lower, Integer upper, Integer n, Real p) {// in [lower, upper) judgeAssert(lower < upper, "Random::binomial(): n Lower must be less than upper!"); while (true) { Integer res = binomial(n, p); if (lower <= res and res < upper) return res; } } Integer maximum(Integer lower, Integer upper, Integer n) {// in [lower, upper) judgeAssert(n > 0, "Random::maximum(): n musst be positive!"); judgeAssert(lower < upper, "Random::maximum(): Lower must be less than upper!"); if (n < 5) { Integer res = lower; for (Integer i = 0; i < n; i++) res = std::max(res, integer(lower, upper)); return res; } else {// such large n seem unlikely UInteger ul = static_cast(lower); UInteger uu = static_cast(upper); UInteger res = (uu - ul) * std::exp2(std::log2(real()) / n); return std::min(upper - 1, static_cast(res + ul)); } } Integer maximum(Integer upper, Integer n) { return maximum(0, upper, n); } Integer minimum(Integer lower, Integer upper, Integer n) {// in [lower, upper) return upper - 1 - maximum(0, upper - lower, n); } Integer minimum(Integer upper, Integer n) { return minimum(0, upper, n); } Integer prime(Integer lower, Integer upper) {// in [lower, upper) judgeAssert(lower < upper, "Random::prime(): Lower must be less than upper!"); Integer sampleL = lower <= 2 ? 0 : (lower / 2); Integer sampleU = upper / 2; if (sampleL < sampleU) { for (Integer i = 0; i < details::PRIME_TRIALS and i < 4 * (upper - lower); i++) { Integer res = std::max(2, 2*integer(sampleL, sampleU) | 1); if (isPrime(res)) return res; } } judgeAssert(false, "Random::prime(): range contains no primes?"); return -1; } Integer prime(Integer upper) {// in [0, upper) return prime(0, upper); } //========================================================================// // utility // //========================================================================// template typename std::iterator_traits::reference select(RandomIt first, RandomIt last) { judgeAssert(first < last, "Random::select(): Lower must be less than upper!"); return first[integer(0, last - first)]; } template typename ::details::IsContainer::value_type select(const C& c) { return select(std::begin(c), std::end(c)); } template typename C::reference select(C& c) { return select(std::begin(c), std::end(c)); } template T select(const T(&c)[N]) { return select(std::begin(c), std::end(c)); } template T& select(T(&c)[N]) { return select(std::begin(c), std::end(c)); } template T select(const std::pair& t) { return bit() ? getX(t) : getY(t); } template T select(const std::complex& t) { return bit() ? getX(t) : getY(t); } template void shuffle(RandomIt first, RandomIt last) { using std::swap; auto n = last - first; for (auto i = n-1; i > 0; i--) { swap(first[i], first[integer(0, i+1)]); } } template void shuffle(C& c) { return shuffle(std::begin(c), std::end(c)); } template void shuffle(std::pair& t) { using std::swap; if (bit()) swap(getX(t), getY(t)); } template void shuffle(std::complex& t) { using std::swap; if (bit()) swap(getX(t), getY(t)); } template Integer rotate(RandomIt first, RandomIt last) { Integer rotation = integer(0, last - first); std::rotate(first, first + rotation, last); return rotation; } template Integer rotate(C& c) { return rotate(std::begin(c), std::end(c)); } //========================================================================// // sequences // //========================================================================// std::vector distinct(Integer count, Integer lower, Integer upper) { judgeAssert(count >= 0, "Random::distinct(): Count must be non negative!"); judgeAssert(lower + count <= upper, "Random::distinct(): Lower must be less than upper + count!"); std::map used; std::vector res; for (Integer i = 0; i < count; i++) { Integer x = integer(lower, upper - i); auto it = used.find(x); if (it != used.end()) res.emplace_back(it->second); else res.emplace_back(x); it = used.find(upper - i - 1); if (it != used.end()) used[x] = it->second; else used[x] = upper - i - 1; } return res; } std::vector distinct(Integer count, Integer upper) { return distinct(count, 0, upper); } std::vector perm(Integer count, Integer offset = 0) { return distinct(count, offset, offset+count); } std::vector perm(const std::vector& cycles, Integer offset = 0) { auto p = perm(std::accumulate(cycles.begin(), cycles.end(), 0_int)); std::vector res(p.size()); Integer tmp = 0; for (std::size_t i = 0; i < cycles.size(); tmp += cycles[i], i++) { judgeAssert(cycles[i] > 0, "Random::perm(): Cycle lengths must be positive!"); for (Integer j = tmp; j + 1 < tmp + cycles[i]; j++) { res[p[j]] = p[j + 1] + offset; } res[p[tmp + cycles[i] - 1]] = p[tmp] + offset; } return res; } std::vector perm(std::initializer_list cycles, Integer offset = 0) { return perm(std::vector(cycles), offset); } std::vector multiple(Integer count, Integer lower, Integer upper) { std::vector res(count); for (Integer& x : res) x = integer(lower, upper); return res; } std::vector multiple(Integer count, Integer upper) { return multiple(count, 0, upper); } std::vector increasing(Integer count, Integer lower, Integer upper) { std::vector res = distinct(count, lower, upper); std::sort(res.begin(), res.end(), std::less()); return res; } std::vector increasing(Integer count, Integer upper) { return increasing(count, 0, upper); } std::vector decreasing(Integer count, Integer lower, Integer upper) { std::vector res = distinct(count, lower, upper); std::sort(res.begin(), res.end(), std::greater()); return res; } std::vector decreasing(Integer count, Integer upper) { return decreasing(count, 0, upper); } std::vector nonDecreasing(Integer count, Integer lower, Integer upper) { std::vector res = multiple(count, lower, upper); std::sort(res.begin(), res.end(), std::less()); return res; } std::vector nonDecreasing(Integer count, Integer upper) { return nonDecreasing(count, 0, upper); } std::vector nonIncreasing(Integer count, Integer lower, Integer upper) { std::vector res = multiple(count, lower, upper); std::sort(res.begin(), res.end(), std::greater()); return res; } std::vector nonIncreasing(Integer count, Integer upper) { return nonIncreasing(count, 0, upper); } std::vector partition(Integer n, Integer k, Integer min = 1) { judgeAssert(n > 0, "Random::partition(): n must be positive!"); judgeAssert(k > 0, "Random::partition(): k must be positive!"); judgeAssert(min <= 0 or k <= n / min, "Random::partition(): k too large!"); n -= (min - 1) * k; std::vector res = increasing(k-1, 1, n); res.emplace_back(n); for (Integer i = 0, last = 0; i < k; i++) { res[i] -= last; last += res[i]; res[i] += min - 1; } return res; } std::string bracketSequence(Integer n, char open = '(', char close = ')') {//proper bracket sequence of length 2*n judgeAssert(0 <= n and n <= 0x7FFF'FFFF, "Random::bracketSequence(): n out of range!"); std::string res(2 * n, open); for (Integer i = 0, diff = 0; i < 2 * n; i++) { Integer opened = (i + diff) / 2; if (integer((2 * n - i) * (diff + 1)) < (n - opened) * (diff + 2)) { diff++; } else { res[i] = close; diff--; } } return res; } //========================================================================// // geometry // //========================================================================// template> std::vector convex(Integer n, Integer dim) { judgeAssert(dim <= 0x3FFF'FFFF, "Random::convex(): dim too large!"); judgeAssert(dim > 0, "Random::convex(): dim must be positive!"); judgeAssert(n <= 8*dim - 8, "Random::convex(): dim too small!"); judgeAssert(n >= 3, "Random::convex(): n too small!"); while (true) { Integer left = 1 + binomial(n - 2, 0.5); Integer down = 1 + binomial(n - 2, 0.5); auto x = partition(2 * dim - 2, left, 0); auto y = partition(2 * dim - 2, down, 0); for (auto& z : x) z = -z; for (auto& z : y) z = -z; append(x, partition(2 * dim - 2, n - left, 0)); append(y, partition(2 * dim - 2, n - down, 0)); auto itX = std::partition(x.begin(), x.end(), [](Integer z){return z == 0;}); auto itY = std::partition(y.begin(), y.end(), [](Integer z){return z != 0;}); if (std::distance(x.begin(), itX) + std::distance(itY, y.end()) > n) continue; shuffle(itX, x.end()); if (itX != x.begin()) shuffle(y.begin(), itY); std::vector dirs(n); for (Integer i = 0; i < n; i++) { dirs[i] = {x[i], y[i]}; } ::details::cyclicSort(dirs); std::vector res = {{0, 0}}; Integer maxX = 0; Integer maxY = 0; for (auto dir : dirs) { Point tmp = res.back(); getX(tmp) += getX(dir); getY(tmp) += getY(dir); maxX = std::max(maxX, getX(tmp)); maxY = std::max(maxY, getY(tmp)); res.emplace_back(tmp); } res.pop_back(); for (auto& point : res) { getX(point) += dim - 1 - maxX; getY(point) += dim - 1 - maxY; } return res; } } template> std::vector nonCollinearPoints(Integer n, Integer dim) { judgeAssert(dim <= 0x1FFF'FFFF, "Random::nonCollinearPoints(): dim too large!"); judgeAssert(n >= 0, "Random::nonCollinearPoints(): dim must be non negative!"); judgeAssert(dim > n, "Random::nonCollinearPoints(): dim too small!"); Integer p = prime(dim - 1, 2*dim + 2); Integer rotA = 0; Integer rotB = 0; while (rotA == 0 && rotB == 0) { rotA = integer(0, p); rotB = integer(0, p); } std::array abc = { integer(1, p), integer(0, p), integer(0, p), }; Integer dx = integer(-dim + 1, dim - p); Integer dy = integer(-dim + 1, dim - p); auto xs = distinct(n, p); std::vector res; for (auto tmpX : xs) { Integer tmpY = 0; for (Integer add : abc[0]) { tmpY *= tmpX; tmpY += add; tmpY %= p; } Integer x = applyMod(tmpX * rotA - tmpY * rotB, p); Integer y = applyMod(tmpX * rotB + tmpY * rotA, p); res.emplace_back(x + dx, y + dy); } return res; } } // namespace Random //============================================================================// // args parser // //============================================================================// class ParamaterBase { friend class Command; friend struct Paramater; std::optional token; template T parse(std::string_view s) const { T res = {}; judgeAssert(details::parse(s, res), "Command: Could not parse args"); return res; } ParamaterBase() = default; explicit ParamaterBase(std::string_view token_) : token(token_) {} public: std::string asString() const { return std::string(token.value()); } std::string asString(std::string_view defaultValue) const { return std::string(token.value_or(defaultValue)); } Integer asInteger() const { return parse(token.value()); } Integer asInteger(Integer defaultValue) const { return token ? asInteger() : defaultValue; } Real asReal() const { return parse(token.value()); } Real asReal(Real defaultValue) const { return token ? asReal() : defaultValue; } }; struct Paramater final : private ParamaterBase { using ParamaterBase::ParamaterBase; using ParamaterBase::asString; using ParamaterBase::asInteger; using ParamaterBase::asReal; bool exists() const { return token.has_value(); } explicit operator bool() const { return exists(); } }; class Command final : private ParamaterBase { const std::vector& raw; const Integer first, count; const bool found; public: explicit Command(const std::vector& raw_) : raw(raw_), first(0), count(0), found(false) {} explicit Command(const std::vector& raw_, Integer first_, Integer count_) : ParamaterBase(count_ == 0 ? ParamaterBase() : ParamaterBase(raw_[first_])), raw(raw_), first(first_), count(count_), found(true) { judgeAssert(count >= 0, "Command: Invalid command in args!"); } bool exists() const { return found; } explicit operator bool() const { return exists(); } Integer parameterCount() const { return count; } Paramater operator[](Integer i) const { if (i >= 0 and i < count) return Paramater(raw[first + i]); return Paramater(); } using ParamaterBase::asString; using ParamaterBase::asInteger; using ParamaterBase::asReal; std::vector asStrings() const { std::vector res; std::transform(raw.begin() + first, raw.begin() + first + count, std::back_inserter(res), [](const std::string& value) { return std::string(value); }); return res; } std::vector asIntegers() const { std::vector res; std::transform(raw.begin() + first, raw.begin() + first + count, std::back_inserter(res), [this](const std::string& value) { return parse(value); }); return res; } std::vector asReals() const { std::vector res; std::transform(raw.begin() + first, raw.begin() + first + count, std::back_inserter(res), [this](const std::string& value) { return parse(value); }); return res; } }; class CommandParser final { std::vector raw; std::map> commands; std::map tokens; static bool isCommand(std::string_view s) { return s.size() > 2 and s.substr(0, 2) == COMMAND_PREFIX; } void addCommand(std::string_view command, Integer first, Integer count = 0) { judgeAssert(commands.count(command) == 0, "Command: Duplcated command in args!"); commands.emplace(command, std::pair{first, count}); } public: CommandParser() = default; explicit CommandParser(int argc, char** argv) { raw.assign(argc, {}); std::string_view command = EMPTY_COMMAND; Integer first = 0; Integer count = 0; for (int i = 0; i < argc; i++) { raw[i] = std::string(argv[i]); tokens.emplace(raw[i], i+1); if (isCommand(raw[i])) { addCommand(command, first, count); command = raw[i]; first = i+1; count = 0; } else { count++; } } addCommand(command, first, count); } CommandParser(CommandParser&&) = default; CommandParser& operator=(CommandParser&&) = default; CommandParser(const CommandParser&) = delete; CommandParser& operator=(const CommandParser&) = delete; std::string_view operator[](Integer t) const { judgeAssert(t >= 0 and t < static_cast(raw.size()), "Command: Index out of args!"); return raw[t]; } Command operator[](std::string_view command) const & { judgeAssert(details::isToken(command), "Command: must not contain a space!"); auto it = commands.find(command); if (it == commands.end()) return Command(raw); return Command(raw, it->second.first, it->second.second); } Command getRaw(std::string_view command) const & { judgeAssert(details::isToken(command), "Command: must not contain a space!"); auto it = tokens.find(command); if (it == tokens.end()) return Command(raw); return Command(raw, it->second, raw.size() - it->second); } Command getRaw() const & { return Command(raw, 0, raw.size()); } }; //============================================================================// // Constraints // //============================================================================// template class Bounds final { bool hadMin, hadMax; // was value==lower/upper at some point T min, max; // range of seen values T lower, upper; // bounds for value public: constexpr explicit Bounds(T lower_, T upper_, T value_) : hadMin(false), hadMax(false), min(value_), max(value_), lower(lower_), upper(upper_) { update(lower_, upper_, value_); } void update(T lower_, T upper_, T value_) { if constexpr (std::is_same_v) { hadMin |= details::floatEqual(value_, lower_, DEFAULT_EPS, DEFAULT_EPS); hadMax |= details::floatEqual(value_, upper_, DEFAULT_EPS, DEFAULT_EPS); } else { hadMin |= value_ == lower_; hadMax |= value_ == upper_; } min = std::min(min, value_); max = std::max(max, value_); lower = std::min(lower, lower_); upper = std::max(upper, upper_); } friend std::ostream& operator<<(std::ostream& os, const Bounds& bounds) { os << bounds.hadMin << " " << bounds.hadMax << " "; os << bounds.min << " " << bounds.max << " "; return os << bounds.lower << " " << bounds.upper; } }; namespace details { //using typeIndex = std::type_index; using typeIndex = void*; template typeIndex getTypeIndex() { //return std::type_index(type id(T)); static T* uniqueTypeIndex = nullptr; return &uniqueTypeIndex; } } class Constraint final { friend class ConstraintsLogger; std::variant< std::monostate, // uninitialized Bounds, // Integer or container bound Bounds // Real bound > bound; std::optional type; template void update(T lower, T upper, T value) { if constexpr(std::is_integral_v) { upper--; // for BAPCtools the range is closed but we use half open ranges! } if (!type) { type = details::getTypeIndex(); bound = Bounds(lower, upper, value); } judgeAssert(type == details::getTypeIndex(), "Constraint: type must not change!"); std::get>(bound).update(lower, upper, value); } public: Constraint() = default; Constraint(Constraint&&) = default; Constraint& operator=(Constraint&&) = default; Constraint(const Constraint&) = delete; Constraint& operator=(const Constraint&) = delete; template, bool> = true> void log(Integer lower, Integer upper, V value) { update(lower, upper, value); } template, bool> = true> void log(Real lower, Real upper, V value) { update(lower, upper, value); } template, bool> = true> void log(Integer lower, Integer upper, const C& container) { update(lower, upper, static_cast(std::size(container))); } }; class ConstraintsLogger final { std::optional fileName; std::map byName; std::vector> constraints; public: ConstraintsLogger() = default; explicit ConstraintsLogger(std::string_view fileName_) : fileName(fileName_) {} ConstraintsLogger(ConstraintsLogger&&) = default; ConstraintsLogger& operator=(ConstraintsLogger&&) = default; ConstraintsLogger(const ConstraintsLogger&) = delete; ConstraintsLogger& operator=(const ConstraintsLogger&) = delete; Constraint& operator[](const std::string& name) & { judgeAssert(details::isToken(name), "Constraint: name must not contain a space!"); auto res = byName.try_emplace(name, constraints.size()); if (res.second) constraints.emplace_back(std::make_unique()); return *(constraints[res.first->second]); } void write() const { if (!fileName) return; std::ofstream os(*fileName); os << std::noboolalpha; os << std::fixed; os << std::setprecision(DEFAULT_PRECISION); for (const auto& [name, id] : byName) { const Constraint& c = *(constraints[id]); if (c.type) { os << "LocationNotSupported:" << name << " " << name << " "; if (c.bound.index() == 1) os << std::get<1>(c.bound); if (c.bound.index() == 2) os << std::get<2>(c.bound); os << std::endl; } } } ~ConstraintsLogger() noexcept { write(); } }; //============================================================================// // custom input stream // //============================================================================// class InputStream final { std::unique_ptr managed; std::istream* in; bool spaceSensitive, caseSensitive; Verdict onFail; Real floatAbsTol; Real floatRelTol; void init() { if (spaceSensitive) *in >> std::noskipws; else *in >> std::skipws; } void checkIn() { judgeAssert(in != nullptr, "InputStream: not initialized!"); } public: InputStream() = default; explicit InputStream(const std::filesystem::path& path, bool spaceSensitive_, bool caseSensitive_, Verdict onFail_, Real floatAbsTol_ = DEFAULT_EPS, Real floatRelTol_ = DEFAULT_EPS) : managed(std::make_unique(path)), in(managed.get()), spaceSensitive(spaceSensitive_), caseSensitive(caseSensitive_), onFail(onFail_), floatAbsTol(floatAbsTol_), floatRelTol(floatRelTol_) { judgeAssert(managed->good(), "InputStream: Could not open File: " + path.string()); init(); } explicit InputStream(std::istream& in_, bool spaceSensitive_, bool caseSensitive_, Verdict onFail_, Real floatAbsTol_ = DEFAULT_EPS, Real floatRelTol_ = DEFAULT_EPS) : managed(), in(&in_), spaceSensitive(spaceSensitive_), caseSensitive(caseSensitive_), onFail(onFail_), floatAbsTol(floatAbsTol_), floatRelTol(floatRelTol_) { init(); } InputStream(InputStream&& other) = default; InputStream& operator=(InputStream&& other) = default; InputStream(const InputStream&) = delete; InputStream& operator=(const InputStream&) = delete; void eof() { checkIn(); if (!spaceSensitive) *in >> std::ws; if (in->peek() != std::char_traits::eof()) { in->get(); ValidateBase::juryOut << "Missing EOF!"; fail(); } } void noteof() { checkIn(); if (!spaceSensitive) *in >> std::ws; if (in->peek() == std::char_traits::eof()) { ValidateBase::juryOut << "Unexpected EOF!" << onFail; } } void space() { if (spaceSensitive) { noteof(); if (in->get() != std::char_traits::to_int_type(SPACE)) { ValidateBase::juryOut << "Missing space!"; fail(); } } } void newline() { if (spaceSensitive) { noteof(); if (in->get() != std::char_traits::to_int_type(NEWLINE)) { ValidateBase::juryOut << "Missing newline!"; fail(); } } } private: void check(const std::string& token, const std::regex& pattern) { if (!std::regex_match(token, pattern)) { ValidateBase::juryOut << "Token \"" << token << "\" does not match pattern!"; fail(); } } std::function checkSeparator(char separator) { if (separator == SPACE) return [this](){space();}; if (separator == NEWLINE) return [this](){newline();}; judgeAssert(false, "InputStream: Separator must be ' ' or '\\n'!"); return {}; } template T parse(const std::string& s) { T res = {}; if (!details::parse(s, res)) { ValidateBase::juryOut << "Could not parse token \"" << s << "\"!"; fail(); } return res; } public: std::string string() { noteof(); if (spaceSensitive and !std::isgraph(in->peek())) { in->get(); ValidateBase::juryOut << "Invalid whitespace!"; fail(); } std::string res; *in >> res; if (res.empty()) { ValidateBase::juryOut << "Unexpected EOF!" << onFail; } if (!caseSensitive) toDefaultCase(res); return res; } std::string string(Integer lower, Integer upper) { std::string t = string(); Integer length = static_cast(t.size()); if (length < lower or length >= upper) { ValidateBase::juryOut << "String length " << length << " out of range [" << lower << ", " << upper << ")!"; fail(); } return t; } std::string string(Integer lower, Integer upper, Constraint& constraint) { std::string res = string(lower, upper); constraint.log(lower, upper, res); return res; } std::string string(const std::regex& pattern) { std::string t = string(); check(t, pattern); return t; } std::string string(const std::regex& pattern, Integer lower, Integer upper) { std::string t = string(lower, upper); check(t, pattern); return t; } std::string string(const std::regex& pattern, Integer lower, Integer upper, Constraint& constraint) { std::string res = string(pattern, lower, upper); constraint.log(lower, upper, res); return res; } template std::vector strings(Args... args, Integer count, char separator) { auto sepCall = checkSeparator(separator); std::vector res(count); for (Integer i = 0; i < count; i++) { res[i] = string(args...); if (i + 1 < count) sepCall(); } return res; } std::vector strings(Integer count, char separator = DEFAULT_SEPARATOR) { return strings<>(count, separator); } std::vector strings(Integer lower, Integer upper, Integer count, char separator = DEFAULT_SEPARATOR) { return strings(lower, upper, count, separator); } std::vector strings(Integer lower, Integer upper, Constraint& constraint, Integer count, char separator = DEFAULT_SEPARATOR) { return strings(lower, upper, constraint, count, separator); } std::vector strings(const std::regex& pattern, Integer count, char separator = DEFAULT_SEPARATOR) { return strings(pattern, count, separator); } std::vector strings(const std::regex& pattern, Integer lower, Integer upper, Integer count, char separator = DEFAULT_SEPARATOR) { return strings(pattern, lower, upper, count, separator); } std::vector strings(const std::regex& pattern, Integer lower, Integer upper, Constraint& constraint, Integer count, char separator = DEFAULT_SEPARATOR) { return strings(pattern, lower, upper, constraint, count, separator); } Integer integer() { return parse(string(INTEGER_REGEX)); } Integer integer(Integer lower, Integer upper) { Integer res = integer(); if (res < lower or res >= upper) { ValidateBase::juryOut << "Integer " << res << " out of range [" << lower << ", " << upper << ")!"; fail(); } return res; } Integer integer(Integer lower, Integer upper, Constraint& constraint) { Integer res = integer(lower, upper); constraint.log(lower, upper, res); return res; } template std::vector integers(Args... args, Integer count, char separator) { auto sepCall = checkSeparator(separator); std::vector res(count); for (Integer i = 0; i < count; i++) { res[i] = integer(args...); if (i + 1 < count) sepCall(); } return res; } std::vector integers(Integer count, char separator = DEFAULT_SEPARATOR) { return integers<>(count, separator); } std::vector integers(Integer lower, Integer upper, Integer count, char separator = DEFAULT_SEPARATOR) { return integers(lower, upper, count, separator); } std::vector integers(Integer lower, Integer upper, Constraint& constraint, Integer count, char separator = DEFAULT_SEPARATOR) { return integers(lower, upper, constraint, count, separator); } // this does not allow NaN or Inf! // However, those should never be desired. Real real() { return parse(string(REAL_REGEX)); } Real real(Real lower, Real upper) {// uses eps Real res = real(); if (details::floatEqual(res, lower, floatAbsTol, floatRelTol)) return res; if (details::floatEqual(res, upper, floatAbsTol, floatRelTol)) return res; if (std::isnan(res) or !(res >= lower) or !(res < upper)) { ValidateBase::juryOut << "Real " << res << " out of range [" << lower << ", " << upper << ")!"; fail(); } return res; } Real real(Real lower, Real upper, Constraint& constraint) { Real res = real(lower, upper); constraint.log(lower, upper, res); return res; } template std::vector reals(Args... args, Integer count, char separator) { auto sepCall = checkSeparator(separator); std::vector res(count); for (Integer i = 0; i < count; i++) { res[i] = real(args...); if (i + 1 < count) sepCall(); } return res; } std::vector reals(Integer count, char separator = DEFAULT_SEPARATOR) { return reals<>(count, separator); } std::vector reals(Real lower, Real upper, Integer count, char separator = DEFAULT_SEPARATOR) { return reals(lower, upper, count, separator); } std::vector reals(Real lower, Real upper, Constraint& constraint, Integer count, char separator = DEFAULT_SEPARATOR) { return reals(lower, upper, constraint, count, separator); } Real realStrict(Real lower, Real upper, Integer minDecimals, Integer maxDecimals) {// does not use eps std::string t = string(STRICT_REAL_REGEX); auto dot = t.find('.'); Integer decimals = dot == std::string::npos ? 0 : t.size() - dot - 1; if (decimals < minDecimals or decimals >= maxDecimals) { ValidateBase::juryOut << "Real " << t << " has wrong amount of decimals!"; fail(); return 0; } try { Real res = parse(t); if (std::isnan(res) or !(res >= lower) or !(res < upper)) { ValidateBase::juryOut << "Real " << res << " out of range [" << lower << ", " << upper << ")!"; fail(); } return res; } catch(...) { ValidateBase::juryOut << "Could not parse token \"" << t << "\" as real!"; fail(); return 0; } } Real realStrict(Real lower, Real upper, Integer minDecimals, Integer maxDecimals, Constraint& constraint) { Real res = realStrict(lower, upper, minDecimals, maxDecimals); constraint.log(lower, upper, res); return res; } template std::vector realsStrict(Args... args, Integer count, char separator) { auto sepCall = checkSeparator(separator); std::vector res(count); for (Integer i = 0; i < count; i++) { res[i] = realStrict(args...); if (i + 1 < count) sepCall(); } return res; } std::vector realsStrict(Real lower, Real upper, Integer minDecimals, Integer maxDecimals, Integer count, char separator = DEFAULT_SEPARATOR) { return realsStrict(lower, upper, minDecimals, maxDecimals, count, separator); } std::vector realsStrict(Real lower, Real upper, Integer minDecimals, Integer maxDecimals, Constraint& constraint, Integer count, char separator = DEFAULT_SEPARATOR) { return realsStrict(lower, upper, minDecimals, maxDecimals, constraint, count, separator); } void expectString(std::string_view expected) { judgeAssert(details::isToken(expected), "InputStream: expected must not contain a space!"); std::string seen = string(); auto [eq, pos] = details::stringEqual(seen, expected, caseSensitive); if (!eq) { if (seen.size() > 80) { seen = seen.substr(0, 75) + "[...]"; } ValidateBase::juryOut << "Expected \"" << expected << "\" but got \"" << seen << "\"!"; if (pos and *pos > 5) { ValidateBase::juryOut << " (different at position: " << *pos+1 << ")"; } fail(); } } void expectInt(Integer expected) { Integer seen = integer(); if (seen != expected) { ValidateBase::juryOut << "Expected " << expected << " but got " << seen << "!"; fail(); } } void expectReal(Real expected) { Real seen = real(); if (details::floatEqual(seen, expected, floatAbsTol, floatRelTol)) { ValidateBase::juryOut << "Expected " << expected << " but got " << seen << "!"; if (std::isfinite(seen) and std::isfinite(expected)) { Real absDiff = std::abs(seen-expected); Real relDiff = std::abs((seen-expected)/expected); ValidateBase::juryOut << " (abs: " << absDiff << ", rel: " << relDiff << ")"; } fail(); } } private: void fail() { //try to find input position... in->clear(); auto originalPos = in->tellg(); in->seekg(0); if (originalPos != std::streamoff(-1) and *in) { Integer line = 1; std::size_t l = 0, r = 0; std::string buffer; bool extend = true; while (*in and in->tellg() < originalPos) { l = r = buffer.size(); if (std::isgraph(in->peek())) { std::string tmp; *in >> tmp; buffer += tmp; } else if (in->peek() == std::char_traits::to_int_type(NEWLINE)) { line++; in->get(); if (in->tellg() < originalPos) { buffer.clear(); } else { buffer += ' '; extend = false; } } else { buffer += std::char_traits::to_char_type(in->get()); } if (*in and in->tellg() >= originalPos) { r = buffer.size(); } } if (l != r) { ValidateBase::juryOut << " Line: " << line << ", Char: " << l << '\n'; if (extend) { char tmp; while ((buffer.size() < 80 or buffer.size() < r + 80) and in->get(tmp) and tmp != NEWLINE) { buffer += tmp; } } if (r > 60 and l > 20) { Integer offset = std::min(l - 20, r - 60); l -= offset; r -= offset; buffer = "[...]" + buffer.substr(offset + 5); } if (buffer.size() > 80) { buffer = buffer.substr(0, 75); buffer += "[...]"; r = std::min(r, buffer.size()); } ValidateBase::juryOut << buffer << '\n'; ValidateBase::juryOut << std::string(l, ' ') << '^' << std::string(r - l - 1, '~'); } } ValidateBase::juryOut << onFail; } }; //============================================================================// // state guard // //============================================================================// namespace details { bool initialized(bool set = false) { static bool value = false; return std::exchange(value, value |= set); } struct InitGuard final { ~InitGuard() { if (std::uncaught_exceptions() == 0) { judgeAssert(initialized(), "validate.h: init(argc, argv) was never called!"); } } } initGuard; } //============================================================================// // Settings // //============================================================================// template class SettingBase { template friend class Setting; friend class SettingCaseSensitive; T value; SettingBase(T value_) : value(value_) {} public: SettingBase(SettingBase&& other) = delete; SettingBase(const SettingBase&) = delete; SettingBase& operator=(SettingBase&& other) = delete; SettingBase& operator=(const SettingBase&) = delete; operator T() const { return value; } SettingBase& operator=(T value_) { judgeAssert(!details::initialized(), "validate.h: Cannot change setting after init(argc, argv) was called!"); value = value_; return *this; } }; template class Setting final : public SettingBase { public: Setting(T value) : SettingBase(value) {} using SettingBase::operator T; using SettingBase::operator=; }; class SettingCaseSensitive final : public SettingBase { public: SettingCaseSensitive(bool value) : SettingBase(value) {} using SettingBase::operator bool; using SettingBase::operator=; std::regex regex(std::string_view s, std::regex_constants::syntax_option_type f = std::regex_constants::ECMAScript) const { if (!value) f |= std::regex_constants::icase; return std::regex(s.data(), s.size(), f); } }; //============================================================================// // Validators and stuff // //============================================================================// namespace ValidateBase { //OutputStream juryOut(std::cout); //already defined earlier //OutputStream juryErr(std::cerr); CommandParser arguments; //you may change these values before calling::init() but not afterwards! Setting floatAbsTol(DEFAULT_EPS); Setting floatRelTol(DEFAULT_EPS); Setting spaceSensitive(false); SettingCaseSensitive caseSensitive(false); // Real r2 is considered the reference value for relative error. bool floatEqual(Real given, Real expected, Real floatAbsTol_ = floatAbsTol, Real floatRelTol_ = floatRelTol) { return details::floatEqual(given, expected, floatAbsTol_, floatRelTol_); } bool floatLess(Real given, Real expected, Real floatAbsTol_ = floatAbsTol, Real floatRelTol_ = floatRelTol) { return given <= expected or floatEqual(given, expected, floatAbsTol_, floatRelTol_); } bool floatGreater(Real given, Real expected, Real floatAbsTol_ = floatAbsTol, Real floatRelTol_ = floatRelTol) { return given >= expected or floatEqual(given, expected, floatAbsTol_, floatRelTol_); } constexpr boolean stringEqual(std::string_view a, std::string_view b, bool caseSensitive_ = caseSensitive) { return details::stringEqual(a, b, caseSensitive_); } namespace details { void init(int argc, char** argv) { judgeAssert(!::details::initialized(), "validate.h: init(argc, argv) was called twice!"); //std::ios_base::sync_with_stdio(false); //cin.tie(nullptr); arguments = CommandParser(argc, argv); if (auto seed = arguments[SEED_COMMAND]) Random::seed(seed.asInteger()); // parse default flags manually, since they dont use '--' prefix auto eps = arguments.getRaw(FLOAT_TOLERANCE); floatAbsTol = eps.asReal(floatAbsTol); floatRelTol = eps.asReal(floatRelTol); floatAbsTol = arguments.getRaw(FLOAT_ABSOLUTE_TOLERANCE).asReal(floatAbsTol); floatRelTol = arguments.getRaw(FLOAT_RELATIVE_TOLERANCE).asReal(floatRelTol); if (arguments.getRaw(SPACE_SENSITIVE)) spaceSensitive = true; if (arguments.getRaw(CASE_SENSITIVE)) caseSensitive = true; ::details::initialized(true); } } } // namespace ValidateBase namespace ConstraintsBase { ConstraintsLogger constraint; void initConstraints() { if (auto file = ValidateBase::arguments[CONSTRAINT_COMMAND]) { constraint = ConstraintsLogger(file.asString()); } } } // namespace ConstraintsBase //called as ./validator [arguments] < inputfile namespace InputValidator { using namespace ValidateBase; using namespace ConstraintsBase; InputStream testIn; void init(int argc, char** argv) { spaceSensitive = true; caseSensitive = true; ValidateBase::details::init(argc, argv); juryOut = OutputStream(std::cout); testIn = InputStream(std::cin, spaceSensitive, caseSensitive, WA, floatAbsTol, floatRelTol); initConstraints(); } } // namespace InputValidator //called as ./validator input judgeanswer feedbackdir < teamoutput namespace OutputValidator { using namespace ValidateBase; using namespace ConstraintsBase; InputStream testIn; InputStream juryAns; InputStream teamAns; void init(int argc, char** argv) { ValidateBase::details::init(argc, argv); juryOut = OutputStream(std::filesystem::path(arguments[3]) / JUDGE_MESSAGE); testIn = InputStream(std::filesystem::path(arguments[1]), false, caseSensitive, FAIL); juryAns = InputStream(std::filesystem::path(arguments[2]), false, caseSensitive, FAIL); teamAns = InputStream(std::cin, spaceSensitive, caseSensitive, WA); initConstraints(); } } // namespace OutputValidator //called as ./interactor input judgeanswer feedbackdir <> teamoutput namespace Interactor { using namespace ValidateBase; OutputStream toTeam; InputStream testIn; InputStream fromTeam; void init(int argc, char** argv) { ValidateBase::details::init(argc, argv); juryOut = OutputStream(std::filesystem::path(arguments[3]) / JUDGE_MESSAGE); toTeam = OutputStream(std::cout); testIn = InputStream(std::filesystem::path(arguments[1]), false, caseSensitive, FAIL); fromTeam = InputStream(std::cin, spaceSensitive, caseSensitive, WA); } } // namespace Interactor //called as ./generator [arguments] namespace Generator { using namespace ValidateBase; OutputStream testOut; void init(int argc, char** argv) { ValidateBase::details::init(argc, argv); juryOut = OutputStream(std::cerr); testOut = OutputStream(std::cout); } } // namespace Generator #endif