Validators with testlib.h

Revision en23, by arsijo, 2018-11-02 01:01:28

If you have written some programming problems, and have prepared test cases, you will probably experience the terrible feeling that some test cases may be invalid (meaning it does not agree with the constraints in problem statement): upper bound can be violated, your graph not satisfied connectivity requirements or is not at tree... It is reasonable to feel that way. Even experienced problem setters make mistakes sometimes (for example, in the prestigious ACM ICPC World final 2007).

It is strictly recommended to write a special program (called validator) to formally check each test to satisfy all requirements from problem statements. Validators are strictly required for problems on Codeforces. Polygon has built-in support of validators.

It is really easy to write a validator using testlib.h.

Example

Following is the validator was written for the problem 100541A - Stock Market:

#include "testlib.h"

int main(int argc, char* argv[]) {
    registerValidation(argc, argv);
    int testCount = inf.readInt(1, 10, "testCount");
    inf.readEoln();
    
    for (int i = 0; i < testCount; i++) {
        int n = inf.readInt(1, 100, "n");
        inf.readSpace();
        inf.readInt(1, 1000000, "w");
        inf.readEoln();

        for(int i = 0; i < n; ++i) {
            inf.readInt(1, 1000, "p_i");
            if (i < n-1)
                inf.readSpace();
        }
        inf.readEoln();
    }

    inf.readEof();
}

The wonderful thing about this validator is that it is very simple and it is very difficult to write something incorrect.

More examples can be found at the Github repo

Available methods

The first line of your code should be registerValidation() which does some magic in the background, so that you can use the necessary methods. Most methods for validators start with prefix "read" and it does the same thing: moves input stream pointer to next suitable place after reading something. It also detect violations (input does not match what you are trying to read), and then throw error.

Notes:

  • Validator is strict. It cares about correct placing of spaces. For example, when you're trying to read an integer and the next character is a space (and then an integer), the validator will throw error.
  • Some method has "regex" feature. It is not a full-featured regex as you may have used in many programming languages. It is a very simple version, which supports the following:
    • Set of character, e.g: [a-z] is a lowercase latin letter, [^a-z] matches anything but a lowercase latin letter.
    • Range, e.g. [a-z]{1,5} is a string of length 1 to 5 consists of only lowercase latin letter.
    • Or operator, e.g. mike|john is either mike or john.
    • Optional character, e.g. -?[1-9][0-9]{0,3} will match non-zero integers from -9999 to 9999 (note the optional minus sign).
    • Repetition, e.g. [0-9]* will match sequences (empty or non-empty) of digits, and [0-9]+ will match non-empty sequences of digits.
  • Also regarding regex, very simple greedy algorithm is used. For example, pattern [0-9]?1 will not match 1, because of greedy nature of matching.

Following is full list of methods available:

Method What it does
void registerValidation() This method must be called at the beginning of your code in order to use validator.
After calling this method, you can access input stream by variable named inf.
char readChar() Returns current character and moves pointer one character forward.
char readChar(char c) Same as readChar() but ensures that the readCharacter is 'c'.
char readSpace() Same as readChar(' ').
void unreadChar(char c) Puts back character c to input stream.
string readToken() Reads a new token, i.e. a word that doesn't contain any whitespaces (like space, tab, EOLN and etc).
string readToken(string regex) Same as readToken() but ensures that it matches given regex.
string readWord() Same as readToken()
string readWord(string regex) Same as readToken(string regex)
long long readLong() Reads a long (long long in C/C++ and long in Java)
long long readLong(long long L, long long R) Same as readLong() but ensures that the value is in range [L, R] (inclusively)
vector<long long> readLongs(int n, long long L, long long R) Reads n space separated longs (long long in C/C++ and long in Java) and ensures that the values are in range [L, R] (inclusively)
int readInt(),
int readInteger()
Reads an integer (int type in both Java and C/C++)
int readInt(int L, int R),
int readInteger(L, R)
Same as readInt() but ensures that the value is in range [L, R] (inclusively)
vector<int> readInts(int n, int L, int R),
vector<int> readIntegers(int n, int L, int R)
Reads n space separated integers and ensures that the values are in range [L, R] (inclusively)
double readReal(),
double readDouble()
Reads a double.
double readReal(double L, double R),
double readDouble(double L, double R)
Same as readReal(), readDouble() but ensures that the value is in range [L, R].
double readStrictReal(double L, double R, int minPrecision, int maxPrecision),
double readStrictDouble(double L, double R, int minPrecision, int maxPrecision)
Same as readReal(L, R), readDouble(L, R), but additionally ensures that the number of digits after decimal point is between [minPrecision, maxPrecision]. Doesn't allow exponential or any other non-standard form.
string readString(),
string readLine()
Reads a line from current position to EOLN. Moves input stream pointer to first character of new line (if exists).
string readString(string regex),
string readLine(string regex)
Same as readString() and readLine(), but ensures that the string matches given regex.
void readEoln() Reads EOLN or fails. Note that this method magically works on both Windows and Linux. On Windows it reads #13#10 and on Linux it reads #10.
void readEof() Reads EOF or fails.

Parameter variableName

It is recommended to insert last string parameter to readInt/readInteger/readLong/readDouble/readWord/readToken/readString/readLine called variableName to make error message be human-readable. So it is preffered to use inf.readInt(1, 100, n) instead of inf.readInt(1, 100). The first statement will fail with human-readable message like FAIL Integer parameter [name=n] equals to 0, violates the range [1, 100].

Using ensure/ensuref

To check a requirement (like a graph doesn't contain loops, i.e. xi ≠ yi) use ensuref(x_i != y_i, "Graph can't contain loops"). It is allowed to use C-language format specifiers like ensuref(s.length() % 2 == 0, "String 's' should have even length, but s.length()=%d", int(s.length())). Also you can use simple form like ensure(x > y), it will print failed condition if it doesn't hold in form FAIL Condition failed: "x > y".

Reference: Github page of testlib.h

Tags testlib, validator, validators, polygon

History

 
 
 
 
Revisions
 
 
  Rev. Lang. By When Δ Comment
ru11 Russian KAN 2022-07-07 20:21:26 2128
en24 English KAN 2022-07-07 20:16:48 4201 Tiny change: 'alidation()` which d' -> 'alidation(argc, argv)` which d'
ru10 Russian KAN 2022-07-07 19:00:36 2066 added global methods
ru9 Russian KAN 2022-07-07 18:43:02 2171 updated functions
ru8 Russian MikeMirzayanov 2018-12-29 14:28:59 20 Мелкая правка: ' readLong(int L, int R)|Аналог' -> ' readLong(long long L, long long R)|Аналог'
ru7 Russian arsijo 2018-11-02 01:08:48 527
en23 English arsijo 2018-11-02 01:01:28 400
ru6 Russian Zlobober 2016-10-01 20:36:24 32
en22 English Zlobober 2016-02-24 02:54:05 32
en21 English I_love_Hoang_Yen 2015-06-18 14:09:58 20 Tiny change: ' readLong(int L, int R)|Same a' -> ' readLong(long long L, long long R)|Same a'
ru5 Russian riadwaw 2015-06-10 22:35:00 20
en20 English I_love_Hoang_Yen 2015-06-10 21:00:28 20
ru4 Russian MikeMirzayanov 2015-06-10 12:29:16 118
en19 English MikeMirzayanov 2015-06-10 12:27:46 410
en18 English PrinceOfPersia 2015-06-10 08:57:32 218
ru3 Russian MikeMirzayanov 2015-06-10 02:25:07 67
en17 English MikeMirzayanov 2015-06-10 02:24:34 67
ru2 Russian MikeMirzayanov 2015-06-10 02:20:24 3
ru1 Russian elena 2015-06-10 02:18:13 7940 Первая редакция перевода на Русский
en16 English elena 2015-06-10 01:13:44 30 Tiny change: 'inus sign), note that it will match '-0'.\n - *' -> 'inus sign).\n - *'
en15 English MikeMirzayanov 2015-06-09 17:33:29 1546
en14 English PrinceOfPersia 2015-06-09 17:16:10 56
en13 English I_love_Hoang_Yen 2015-06-09 14:08:48 125
en12 English I_love_Hoang_Yen 2015-06-09 14:06:24 246 Tiny change: '| Same as readChar(' ').|\n|void ' -> '| Same as `readChar(' ')`.|\n|void '
en11 English I_love_Hoang_Yen 2015-06-09 13:56:28 86 Update styling
en10 English I_love_Hoang_Yen 2015-06-09 13:53:23 1840 (published)
en9 English I_love_Hoang_Yen 2015-06-09 13:38:13 291
en8 English I_love_Hoang_Yen 2015-06-09 13:34:33 28 Tiny change: ' C/C++)|\n\n|readInt' -
en7 English I_love_Hoang_Yen 2015-06-09 13:01:14 3 Tiny change: 'readInt(), readIntege' -> 'readInt(),\nreadIntege'
en6 English I_love_Hoang_Yen 2015-06-09 12:54:26 2 Tiny change: ' in range [L, R].|\n|readS' -> ' in range $[L, R]$.|\n|readS'
en5 English I_love_Hoang_Yen 2015-06-09 12:54:01 60
en4 English I_love_Hoang_Yen 2015-06-09 12:52:57 1888
en3 English I_love_Hoang_Yen 2015-06-09 12:34:31 206
en2 English I_love_Hoang_Yen 2015-06-09 12:04:31 331
en1 English I_love_Hoang_Yen 2015-06-09 12:01:01 1478 Initial revision (saved to drafts)