You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
637 lines
15 KiB
637 lines
15 KiB
/*
|
|
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 <algorithm>
|
|
#include <assert.h>
|
|
#include <cctype>
|
|
#include <climits>
|
|
#include <cmath>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <vector>
|
|
|
|
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<typename RandomAccessIterator>
|
|
void randomShuffle(RandomAccessIterator first, RandomAccessIterator last);
|
|
|
|
private:
|
|
Random(Random &r) {}
|
|
MultipleRecursiveGenerator* mrg_;
|
|
void init();
|
|
};
|
|
|
|
class MultipleRecursiveGenerator {
|
|
public:
|
|
MultipleRecursiveGenerator(unsigned int modulo,
|
|
const vector<unsigned int> &A);
|
|
void setSeed(unsigned int seed);
|
|
unsigned int next16Bits();
|
|
|
|
private:
|
|
int n_;
|
|
unsigned int modulo_;
|
|
vector<unsigned int> A_;
|
|
vector<unsigned int> 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<unsigned int> 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<typename RandomAccessIterator>
|
|
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<unsigned int> &A) : modulo_(modulo), A_(A) {
|
|
n_ = A_.size();
|
|
X_ = vector<unsigned int>(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_
|
|
|