// CERC 2012 // Problem K: Graphic Madness // Output checker // Author: Jakub Pachocki /* oi.h - pakiet funkcji do pisania weryfikatorow wejsc (inwer) i wyjsc (chk) Wersja: 1.3 Autor: Piotr Niedzwiedz W razie problemow, bledow lub pomyslow na ulepszenie pisac na dzwiedziux@gmail.com Historia: 1.1: Poprawka readInt (liczby ujemne). 1.2: Zmiana nazwy, dodano namespace oi, dodano Random i MultipleRecursiveGenerator 1.3: Poprawienie stylu kodu(Google code style). Zablokowano konstruktory kopiujÄ…ce. Dopisano randomShuffle. Dopisano randInt i zmieniono nazwy metod Random na bardziej intiucyjne. */ #ifndef OI_LIB_OI_H_ #define OI_LIB_OI_H_ #include #include #include #include #include #include #include #include using std::vector; using std::max; using std::swap; // We want prevent usage of standard random function. int rand(){ fprintf(stderr, "DONT USE rand or random_shuffle!\nUse oi::Random class.\n"); exit(1); return 0; } namespace oi { enum Lang { EN = 0, PL = 1 }; class Reader; class Scanner { protected: static const int realNumbersLimit = 20; Lang lang; Reader* reader; void(*end)(const char* msg, int line, int position); void readULL(unsigned long long int limit, unsigned long long int &val, bool &sign); void readLDB(long double limit, long double &val, bool &sign); public: Scanner(const char* file, Lang _lang = Lang(EN)); Scanner(const char* file, void(*endf)(const char* msg, int line, int position), Lang _lang = Lang(EN)); Scanner(FILE* input, Lang _lang = Lang(EN)); Scanner(FILE* input, void(*endf)(const char* msg, int line, int position), Lang _lang = Lang(EN)); ~Scanner(); void error(const char* msg); // Skips all whitespaces until any occurrence of EOF or other non-white character. int skipWhitespaces(); // Skips all whitespaces until any occurrence of EOF, EOLN or other non-white character. int skipWhitespacesUntilEOLN(); int readInt(int min_value = INT_MIN, int max_value = INT_MAX); unsigned int readUInt(unsigned int min_value = 0, unsigned int max_value = UINT_MAX); long long readLL(long long int min_value = LLONG_MIN, long long int max_value = LLONG_MAX); unsigned long long readULL(unsigned long long int min_value = 0, unsigned long long int max_value = ULLONG_MAX); float readFloat(float min_value, float max_value); double readDouble(double min_value, double max_value); long double readLDouble(long double min_value, long double max_value); char readChar(); // Newline character is read, but isn't added to s int readLine(char* s, int size); // Reads a string until occurrence of EOF, EOLN or whitespace. // Returns the number of characters read (possibly 0). int readString(char* s, int size); void readEof(); void readEofOrEoln(); void readEoln(); void readSpace(); void readTab(); bool isEOF(); private: Scanner(Scanner &s) {} }; class MultipleRecursiveGenerator; class Random { public: Random(); explicit Random(unsigned int seed); ~Random(); void setSeed(unsigned int seed); // Random number from range <0..2^31 - 1> int rand(); // randS* - signed * // randU* - unsigned * int randSInt(); unsigned int randUInt(); long long randSLL(); unsigned long long randULL(); template void randomShuffle(RandomAccessIterator first, RandomAccessIterator last); private: Random(Random &r) {} MultipleRecursiveGenerator* mrg_; void init(); }; class MultipleRecursiveGenerator { public: MultipleRecursiveGenerator(unsigned int modulo, const vector &A); void setSeed(unsigned int seed); unsigned int next16Bits(); private: int n_; unsigned int modulo_; vector A_; vector X_; }; class Reader { private: static const int bufferSize = 1000; char Buffer[bufferSize]; int head, tail; int line, position; void fillBuffer(); FILE* input; public: explicit Reader(const char* file); explicit Reader(FILE* _input); ~Reader(); bool isEOF(); int getLine() {return line;} int getPosition() {return position;} char read(bool move = false); private: Reader(Reader &r) {}; }; const char* msgLeadingZeros[]= { "Leading zeros", "Zera wiodace"}; const char* msgMinusZero[]= { "Minus zero -0", "Minus zero -0"}; const char* msgNoNumber[]= { "No number", "Brak liczby"}; const char* msgNoChar[]= { "No char - EOF", "Brak znaku - EOF"}; const char* msgNotEof[]= { "Not EOF", "Brak konca pliku"}; const char* msgNotEoln[]= { "Not EOLN", "Brak konca linii"}; const char* msgNotEofOrEoln[]= { "Not EOF or EOLN", "Brak konca linii i brak konca pliku"}; const char* msgNotSpace[]= { "Not space", "Brak spacji"}; const char* msgNotTab[]= { "Not tab", "Brak znaku tabulacji"}; const char* msgOutOfRangeInt[]= { "Integer out of range", "Liczba calkowita spoza zakresu"}; const char* msgOutOfRangeReal[]= { "Real number out of range", "Liczba rzeczywista spoza zakresu"}; const char* msgRealNumberLimit[]= { "Too many digits after dot", "Za duzo cyfr po kropce dziesietnej"}; const char* msgBadRealNumberFormat[]= { "Bad real number format", "Niepoprawny format liczby rzeczywistej"}; // ------------------------------- Implementation ----------------------------- typedef unsigned long long ull; typedef unsigned int uint; typedef long long ll; typedef long double ldb; inline bool isDot(char x) { return x == '.'; } inline bool isEOLN(char x) { return x == '\n'; } inline bool isMinus(char x) { return x == '-'; } inline bool isSpace(char x) { return x == ' '; } inline bool isTab(char x) { return x == '\t'; } inline bool isWhitespace(char x) { return x == ' ' || x == '\t' || x == '\n'; } void endDefault(const char* msg, int line, int position) { printf("ERROR(line: %d, position: %d): %s\n", line, position, msg); exit(1); } // ------------------------------- Random ------------------------------------- void Random::init() { // Here is a reference about it: // http://random.mat.sbg.ac.at/results/karl/server/node7.html vector A(5, 0); A[0] = 107374182, A[4] = 104480; unsigned int modulo = 2147483647; // 2^31 -1 mrg_ = new MultipleRecursiveGenerator(modulo, A); } Random::Random() { init(); } Random::Random(unsigned int seed) { init(); setSeed(seed); } Random::~Random() { delete mrg_; } void Random::setSeed(unsigned int seed) { mrg_->setSeed(seed); } #define RAND_TYPE(type, iter)\ type res = 0;\ for (int i = 0; i < iter; ++i) {\ res += ((type)mrg_->next16Bits()) << (i * 16);\ }\ return res; int Random::rand() { int x = randSInt(); if (x<0) return ~x; return x; } int Random::randSInt() { RAND_TYPE(int, 2); } unsigned int Random::randUInt() { RAND_TYPE(unsigned int, 2); } long long Random::randSLL() { RAND_TYPE(long long, 4); } unsigned long long Random::randULL() { RAND_TYPE(unsigned long long, 4); } template void Random::randomShuffle(RandomAccessIterator first, RandomAccessIterator last) { int n = last - first; for (int i = 1; i < n; ++i) { int to = rand() % (i+1); swap(first[to], first[i]); } } // ----------------------- MultipleRecursiveGenerator ------------------------- MultipleRecursiveGenerator::MultipleRecursiveGenerator( unsigned int modulo, const vector &A) : modulo_(modulo), A_(A) { n_ = A_.size(); X_ = vector(n_, 0); setSeed(0); } void MultipleRecursiveGenerator::setSeed(unsigned int seed) { for (int i = 0; i < n_; ++i) { seed = (seed + 1) % modulo_; X_[i] = seed; } for (int i = 0; i < n_; ++i) next16Bits(); } unsigned int MultipleRecursiveGenerator::next16Bits() { unsigned int res = 0; for (int i = 0; i < n_; ++i) { res = (res + (long long)A_[i] * X_[i]) % modulo_; if (i < n_ - 1) X_[i] = X_[i+1]; } X_[n_ - 1] = res; return res; } // --------------------------- Reader's methods ------------------------------- Reader::Reader(const char* file) { assert((input = fopen(file, "r")) != NULL); head = tail= 0; line = position = 1; } Reader::Reader(FILE* _input) { input = _input; head = tail = 0; line = position = 1; } Reader::~Reader() { assert(fclose(input) == 0); } void Reader::fillBuffer() { while ((tail + 1) % bufferSize != head) { int v = getc(input); if (v == EOF) break; Buffer[tail] = v; tail = (tail + 1) % bufferSize; } } bool Reader::isEOF() { fillBuffer(); return head == tail; } char Reader::read(bool move) { fillBuffer(); assert((head != tail) || (!move)); if (head == tail) return 0; char v = Buffer[head]; if (move) { if (isEOLN(v)) { line++; position = 1; } else { position++; } head = (head + 1) % bufferSize; } return v; } // ---------------------------- Scanner's methods ----------------------------- Scanner::Scanner(const char* file, Lang _lang): lang(_lang) { reader = new Reader(file); end = endDefault; } Scanner::Scanner(const char* file, void(*endf)(const char* msg, int line, int position), Lang _lang): lang(_lang) { reader = new Reader(file); end = endf; } Scanner::Scanner(FILE* input, Lang _lang): lang(_lang) { reader = new Reader(input); end = endDefault; } Scanner::Scanner(FILE* input, void(*endf)(const char* msg, int line, int position), Lang _lang): lang(_lang) { reader = new Reader(input); end = endf; } Scanner::~Scanner() { delete reader; } void Scanner::error(const char* msg) { int l = reader->getLine(); int p = reader->getPosition(); delete reader; (*end)(msg, l, p); } int Scanner::skipWhitespaces() { int result = 0; while (isWhitespace(reader->read())) { reader->read(1); result++; } return result; } int Scanner::skipWhitespacesUntilEOLN() { int result = 0; while (isWhitespace(reader->read()) && !isEOLN(reader->read())) { reader->read(1); result++; } return result; } // INTEGERS int Scanner::readInt(int min_value, int max_value) { return (int)readLL(min_value, max_value); } uint Scanner::readUInt(uint min_value, uint max_value) { return (uint)readULL(min_value, max_value); } inline bool lower_equal(ull a, bool sign_a, ull b, bool sign_b) { if (sign_a != sign_b) return sign_a; if (sign_a) return a >= b; return a <= b; } inline ull spec_abs(ll x) { if (x < 0) return (-(x + 1)) + 1; return x; } ll Scanner::readLL(ll min_value, ll max_value) { assert(min_value <= max_value); bool sign; ull val; readULL(max(spec_abs(min_value), spec_abs(max_value)), val, sign); ll v = val; if (!(lower_equal(spec_abs(min_value), min_value < 0, v, sign) && lower_equal(v, sign, spec_abs(max_value), max_value < 0))) error(msgOutOfRangeInt[lang]); if (sign) v *= -1; return v; } ull Scanner::readULL(ull min_value, ull max_value) { assert(min_value <= max_value); bool sign; ull val; readULL(max_value, val, sign); if (sign) error(msgOutOfRangeInt[lang]); if (!(min_value <= val)) error(msgOutOfRangeInt[lang]); return val; } // REAL NUMBERS float Scanner::readFloat(float min_value, float max_value) { return (float)readLDouble(min_value, max_value); } double Scanner::readDouble(double min_value, double max_value) { return (double)readLDouble(min_value, max_value); } long double Scanner::readLDouble(long double min_value, long double max_value) { assert(min_value <= max_value); bool sign; ldb val; readLDB(max(fabsl(min_value), fabsl(max_value)), val, sign); if (sign) val *= -1; if (!(min_value <= val && val <= max_value)) error(msgOutOfRangeReal[lang]); return val; } // STRINGS int Scanner::readString(char* s, int size) { int x = 0; while ( x < size - 1 && !isEOF() && !isWhitespace(reader->read())) s[x++] = reader->read(1); s[x]=0; return x; } int Scanner::readLine(char* s, int size) { int x = 0; while ( x < size - 1 && !isEOLN(reader->read()) && !isEOF()) s[x++] = reader->read(1); s[x] = 0; if (isEOLN(reader->read())) reader->read(1); return x; } char Scanner::readChar() { if (reader->isEOF()) error(msgNoChar[lang]); return reader->read(1); } // WHITESPACES void Scanner::readEof() { if (!reader->isEOF()) error(msgNotEof[lang]); } void Scanner::readEoln() { if (!isEOLN(reader->read())) error(msgNotEoln[lang]); reader->read(1); } void Scanner::readEofOrEoln() { if (isEOLN(reader->read())) { reader->read(1); } else if (!reader->isEOF()) { error(msgNotEofOrEoln[lang]); } } void Scanner::readSpace() { if (!isSpace(reader->read())) error(msgNotSpace[lang]); reader->read(1); } void Scanner::readTab() { if (!isTab(reader->read())) error(msgNotTab[lang]); reader->read(1); } bool Scanner::isEOF() { return reader->isEOF(); } // PROTECTED void Scanner::readULL(ull limit, ull &val, bool &sign) { sign = 0; val = 0; sign = isMinus(reader->read()); if (sign) reader->read(1); int zeros = 0; int valDigits = 0; while ('0' == reader->read()) { zeros++; valDigits++; reader->read(1); if (zeros > 1) error(msgLeadingZeros[lang]); } int limDigits = 0; ull tmp = limit; while (tmp) { limDigits++; tmp /= 10; } if (!limDigits) limDigits = 1; while (isdigit(reader->read())) { valDigits++; if (valDigits > limDigits) error(msgOutOfRangeInt[lang]); char x = reader->read(1); if (valDigits == limDigits) { if (limit / 10 < val) error(msgOutOfRangeInt[lang]); if (limit / 10 == val && limit % 10 < (ull)(x - '0')) error(msgOutOfRangeInt[lang]); } val = val * 10 + x - '0'; } if (val > 0 && zeros) error(msgLeadingZeros[lang]); if (sign && zeros) error(msgMinusZero[lang]); if (!valDigits) error(msgNoNumber[lang]); } void Scanner::readLDB(ldb limit, ldb &val, bool &sign) { sign = 0; val = 0; sign = isMinus(reader->read()); if (sign) reader->read(1); int zeros = 0; int valDigits = 0; while ('0' == reader->read()) { zeros++; valDigits++; reader->read(1); if (zeros > 1) error(msgLeadingZeros[lang]); } if (zeros && isdigit(reader->read())) error(msgLeadingZeros[lang]); while (isdigit(reader->read())) { valDigits++; char x = reader->read(1); val = val * 10.0 + x - '0'; } if (!valDigits) error(msgNoNumber[lang]); if (isDot(reader->read())) { reader->read(1); ldb dec = 1; int dotDigits = 0; while (isdigit(reader->read())) { dotDigits++; if (dotDigits > realNumbersLimit) error(msgRealNumberLimit[lang]); char x = reader->read(1); dec /= 10.0; val += dec * (x - '0'); } if (!dotDigits) error(msgBadRealNumberFormat[lang]); } } } // namespace oi #endif // OI_LIB_OI_H_ #include #include #include #include using namespace std; const int MAX_LINE_LEN = 4000000; char correct[2 * MAX_LINE_LEN + 5]; char returned[2 * MAX_LINE_LEN + 5]; vector get_tokens(char *s) { vector tokens; tokens.push_back(""); for (int i = 0; s[i]; ++i) { if (isblank(s[i])) { if (!tokens.back().empty()) { tokens.push_back(""); } } else { tokens.back() += s[i]; } } if (tokens.back().empty()) { tokens.pop_back(); } return tokens; } int main(int argc, char **argv) { oi::Scanner output(argv[2]); oi::Scanner answer(argv[3]); while (!output.isEOF()) { output.readLine(correct, 2 * MAX_LINE_LEN); answer.readLine(returned, 2 * MAX_LINE_LEN); if ((int) strlen(returned) > MAX_LINE_LEN) { cerr << "line too long" << endl; return 1; } vector tokens1 = get_tokens(correct); vector tokens2 = get_tokens(returned); if (tokens1.size() != tokens2.size()) { cerr << "wrong number of tokens" << endl; return 1; } if (tokens1[0] != tokens2[0]) { cerr << "different boolean expected" << endl; return 1; } tokens1.erase(tokens1.begin()); tokens2.erase(tokens2.begin()); if (tokens1.empty()) { continue; } vector::iterator it = find(tokens2.begin(), tokens2.end(), tokens1[0]); if (it == tokens2.end()) { cerr << "cycle head not found" << endl; return 1; } int pos = it - tokens2.begin(); if (tokens2[(pos + 1) % tokens2.size()] != tokens1[1]) { reverse(tokens2.begin(), it + 1); reverse(it + 1, tokens2.end()); } else { reverse(tokens2.begin(), it); reverse(it, tokens2.end()); reverse(tokens2.begin(), tokens2.end()); } if (tokens1 != tokens2) { cerr << "bad cycle" << endl; return 1; } } answer.skipWhitespaces(); if (!answer.isEOF()) { return 1; } return 0; }