Browse Source

WIP: tests

MikeMirzayanov 1 year ago
parent
commit
6a52d7ded3
100 changed files with 6002 additions and 8 deletions
  1. 1 0
      .gitattributes
  2. 201 0
      .github/workflows/ci.yml
  3. 1 1
      checkers/uncmp.cpp
  4. 31 7
      testlib.h
  5. 15 0
      tests/README.md
  6. BIN
      tests/lib/msvc-2022-include.7z
  7. 4830 0
      tests/lib/testlib.h
  8. BIN
      tests/lib/windows-kit-10.0.19041.0-include.7z
  9. 238 0
      tests/run.sh
  10. 62 0
      tests/scripts/compile
  11. 110 0
      tests/scripts/test-ref
  12. 54 0
      tests/src/tester-lcmp.cpp
  13. 2 0
      tests/t.bat
  14. 6 0
      tests/t.sh
  15. 10 0
      tests/test-000_compile-all-cpp/run.sh
  16. 0 0
      tests/test-001_run-sval/files/unix/sval/input.01
  17. 1 0
      tests/test-001_run-sval/files/unix/sval/input.02
  18. 1 0
      tests/test-001_run-sval/files/unix/sval/input.03
  19. 1 0
      tests/test-001_run-sval/files/unix/sval/input.04
  20. 1 0
      tests/test-001_run-sval/files/unix/sval/input.05
  21. 1 0
      tests/test-001_run-sval/files/unix/sval/input.06
  22. 1 0
      tests/test-001_run-sval/files/unix/sval/input.07
  23. 1 0
      tests/test-001_run-sval/files/unix/sval/input.08
  24. 1 0
      tests/test-001_run-sval/files/unix/sval/input.09
  25. 1 0
      tests/test-001_run-sval/files/unix/sval/input.10
  26. 0 0
      tests/test-001_run-sval/files/win/sval/input.01
  27. 1 0
      tests/test-001_run-sval/files/win/sval/input.02
  28. 1 0
      tests/test-001_run-sval/files/win/sval/input.03
  29. 1 0
      tests/test-001_run-sval/files/win/sval/input.04
  30. 1 0
      tests/test-001_run-sval/files/win/sval/input.05
  31. 1 0
      tests/test-001_run-sval/files/win/sval/input.06
  32. 1 0
      tests/test-001_run-sval/files/win/sval/input.07
  33. 1 0
      tests/test-001_run-sval/files/win/sval/input.08
  34. 1 0
      tests/test-001_run-sval/files/win/sval/input.09
  35. 1 0
      tests/test-001_run-sval/files/win/sval/input.10
  36. 1 0
      tests/test-001_run-sval/refs/r-sval-input.01/exit_code
  37. 1 0
      tests/test-001_run-sval/refs/r-sval-input.01/stderr
  38. 0 0
      tests/test-001_run-sval/refs/r-sval-input.01/stdout
  39. 1 0
      tests/test-001_run-sval/refs/r-sval-input.02/exit_code
  40. 1 0
      tests/test-001_run-sval/refs/r-sval-input.02/stderr
  41. 0 0
      tests/test-001_run-sval/refs/r-sval-input.02/stdout
  42. 1 0
      tests/test-001_run-sval/refs/r-sval-input.03/exit_code
  43. 0 0
      tests/test-001_run-sval/refs/r-sval-input.03/stderr
  44. 0 0
      tests/test-001_run-sval/refs/r-sval-input.03/stdout
  45. 1 0
      tests/test-001_run-sval/refs/r-sval-input.04/exit_code
  46. 0 0
      tests/test-001_run-sval/refs/r-sval-input.04/stderr
  47. 0 0
      tests/test-001_run-sval/refs/r-sval-input.04/stdout
  48. 1 0
      tests/test-001_run-sval/refs/r-sval-input.05/exit_code
  49. 0 0
      tests/test-001_run-sval/refs/r-sval-input.05/stderr
  50. 0 0
      tests/test-001_run-sval/refs/r-sval-input.05/stdout
  51. 1 0
      tests/test-001_run-sval/refs/r-sval-input.06/exit_code
  52. 1 0
      tests/test-001_run-sval/refs/r-sval-input.06/stderr
  53. 0 0
      tests/test-001_run-sval/refs/r-sval-input.06/stdout
  54. 1 0
      tests/test-001_run-sval/refs/r-sval-input.07/exit_code
  55. 1 0
      tests/test-001_run-sval/refs/r-sval-input.07/stderr
  56. 0 0
      tests/test-001_run-sval/refs/r-sval-input.07/stdout
  57. 1 0
      tests/test-001_run-sval/refs/r-sval-input.08/exit_code
  58. 1 0
      tests/test-001_run-sval/refs/r-sval-input.08/stderr
  59. 0 0
      tests/test-001_run-sval/refs/r-sval-input.08/stdout
  60. 1 0
      tests/test-001_run-sval/refs/r-sval-input.09/exit_code
  61. 1 0
      tests/test-001_run-sval/refs/r-sval-input.09/stderr
  62. 0 0
      tests/test-001_run-sval/refs/r-sval-input.09/stdout
  63. 1 0
      tests/test-001_run-sval/refs/r-sval-input.10/exit_code
  64. 1 0
      tests/test-001_run-sval/refs/r-sval-input.10/stderr
  65. 0 0
      tests/test-001_run-sval/refs/r-sval-input.10/stdout
  66. 15 0
      tests/test-001_run-sval/run.sh
  67. 20 0
      tests/test-001_run-sval/src/sval.cpp
  68. 1 0
      tests/test-002_run-fcmp-wcmp/files/answer.01
  69. 1 0
      tests/test-002_run-fcmp-wcmp/files/input.01
  70. 3 0
      tests/test-002_run-fcmp-wcmp/files/output.01
  71. 1 0
      tests/test-002_run-fcmp-wcmp/refs/r1/exit_code
  72. 1 0
      tests/test-002_run-fcmp-wcmp/refs/r1/stderr
  73. 0 0
      tests/test-002_run-fcmp-wcmp/refs/r1/stdout
  74. 1 0
      tests/test-002_run-fcmp-wcmp/refs/r2/exit_code
  75. 1 0
      tests/test-002_run-fcmp-wcmp/refs/r2/stderr
  76. 0 0
      tests/test-002_run-fcmp-wcmp/refs/r2/stdout
  77. 1 0
      tests/test-002_run-fcmp-wcmp/refs/r3/exit_code
  78. 1 0
      tests/test-002_run-fcmp-wcmp/refs/r3/stderr
  79. 0 0
      tests/test-002_run-fcmp-wcmp/refs/r3/stdout
  80. 1 0
      tests/test-002_run-fcmp-wcmp/refs/r4/exit_code
  81. 1 0
      tests/test-002_run-fcmp-wcmp/refs/r4/stderr
  82. 0 0
      tests/test-002_run-fcmp-wcmp/refs/r4/stdout
  83. 1 0
      tests/test-002_run-fcmp-wcmp/refs/r5/exit_code
  84. 1 0
      tests/test-002_run-fcmp-wcmp/refs/r5/stderr
  85. 0 0
      tests/test-002_run-fcmp-wcmp/refs/r5/stdout
  86. 1 0
      tests/test-002_run-fcmp-wcmp/refs/r6/exit_code
  87. 1 0
      tests/test-002_run-fcmp-wcmp/refs/r6/stderr
  88. 0 0
      tests/test-002_run-fcmp-wcmp/refs/r6/stdout
  89. 14 0
      tests/test-002_run-fcmp-wcmp/run.sh
  90. 36 0
      tests/test-002_run-fcmp-wcmp/src/fcmp.cpp
  91. 38 0
      tests/test-002_run-fcmp-wcmp/src/wcmp.cpp
  92. 1 0
      tests/test-003_run-rnd/refs/r1/exit_code
  93. 0 0
      tests/test-003_run-rnd/refs/r1/stderr
  94. 31 0
      tests/test-003_run-rnd/refs/r1/stdout
  95. 1 0
      tests/test-003_run-rnd/refs/r2/exit_code
  96. 0 0
      tests/test-003_run-rnd/refs/r2/stderr
  97. 121 0
      tests/test-003_run-rnd/refs/r2/stdout
  98. 7 0
      tests/test-003_run-rnd/run.sh
  99. 104 0
      tests/test-003_run-rnd/src/gen.cpp
  100. 1 0
      tests/test-004_use-test.h/refs/r1/exit_code

+ 1 - 0
.gitattributes

@@ -0,0 +1 @@
+*     binary

+ 201 - 0
.github/workflows/ci.yml

@@ -0,0 +1,201 @@
+name: CI
+on: [push, pull_request]
+env:
+  TEST_REF_FORBID_GEN_REFS: true
+
+jobs:
+  tests-ubuntu1804-gpp:
+    strategy:
+      matrix:
+        os: [ubuntu-18.04]
+        compiler: [g++]
+        version: [7, 9, 10]
+    name: Use ${{ matrix.compiler }}-${{ matrix.version }} on ${{ matrix.os }}
+    runs-on: ${{ matrix.os }}
+    steps:
+      - uses: actions/checkout@v3
+      - name: Run tests
+        run: |
+          cd tests
+          bash ./run.sh ${{ matrix.compiler }} v${{ matrix.version }}
+
+  tests-ubuntu1804-clang:
+    strategy:
+      matrix:
+        os: [ubuntu-18.04]
+        compiler: [clang++]
+        version: [9]
+    name: Use ${{ matrix.compiler }}-${{ matrix.version }} on ${{ matrix.os }}
+    runs-on: ${{ matrix.os }}
+    steps:
+      - uses: actions/checkout@v3
+      - name: Run tests
+        run: |
+          cd tests
+          bash ./run.sh ${{ matrix.compiler }} v${{ matrix.version }}
+
+  tests-ubuntu2204-gpp:
+    strategy:
+      matrix:
+        os: [ubuntu-22.04]
+        compiler: [g++]
+        version: [9, 10, 11]
+    name: Use ${{ matrix.compiler }}-${{ matrix.version }} on ${{ matrix.os }}
+    runs-on: ${{ matrix.os }}
+    steps:
+      - uses: actions/checkout@v3
+      - name: Run tests
+        run: |
+          cd tests
+          bash ./run.sh ${{ matrix.compiler }} v${{ matrix.version }}
+
+  tests-ubuntu2204-clang:
+    strategy:
+      matrix:
+        os: [ubuntu-22.04]
+        compiler: [clang++]
+        version: [12, 13, 14]
+    name: Use ${{ matrix.compiler }}-${{ matrix.version }} on ${{ matrix.os }}
+    runs-on: ${{ matrix.os }}
+    steps:
+      - uses: actions/checkout@v3
+      - name: Run tests
+        run: |
+          cd tests
+          bash ./run.sh ${{ matrix.compiler }} v${{ matrix.version }}
+
+  tests-ubuntu1804-gpp-32:
+    strategy:
+      matrix:
+        os: [ubuntu-18.04]
+        compiler: [g++]
+        version: [7, 9, 10]
+    name: Use ${{ matrix.compiler }}-${{ matrix.version }} -m32 on ${{ matrix.os }}
+    runs-on: ${{ matrix.os }}
+    steps:
+      - uses: actions/checkout@v3
+      - name: Run tests
+        run: |
+          sudo apt-get install gcc-${{ matrix.version }}-multilib g++-${{ matrix.version }}-multilib
+          cd tests
+          bash ./run.sh ${{ matrix.compiler }} v${{ matrix.version }} 32
+
+  tests-ubuntu1804-clang-32:
+    strategy:
+      matrix:
+        os: [ubuntu-18.04]
+        compiler: [clang++]
+        version: [9]
+    name: Use ${{ matrix.compiler }}-${{ matrix.version }} -m32 on ${{ matrix.os }}
+    runs-on: ${{ matrix.os }}
+    steps:
+      - uses: actions/checkout@v3
+      - name: Run tests
+        run: |
+          sudo apt-get install gcc-multilib g++-multilib
+          cd tests
+          bash ./run.sh ${{ matrix.compiler }} v${{ matrix.version }} 32
+
+  tests-ubuntu2204-gpp-32:
+    strategy:
+      matrix:
+        os: [ubuntu-22.04]
+        compiler: [g++]
+        version: [9, 10, 11]
+    name: Use ${{ matrix.compiler }}-${{ matrix.version }} -m32 on ${{ matrix.os }}
+    runs-on: ${{ matrix.os }}
+    steps:
+      - uses: actions/checkout@v3
+      - name: Run tests
+        run: |
+          sudo apt-get install gcc-${{ matrix.version }}-multilib g++-${{ matrix.version }}-multilib
+          cd tests
+          bash ./run.sh ${{ matrix.compiler }} v${{ matrix.version }} 32
+
+  tests-ubuntu2204-clang-32:
+    strategy:
+      matrix:
+        os: [ubuntu-22.04]
+        compiler: [clang++]
+        version: [12, 13, 14]
+    name: Use ${{ matrix.compiler }}-${{ matrix.version }} -m32 on ${{ matrix.os }}
+    runs-on: ${{ matrix.os }}
+    steps:
+      - uses: actions/checkout@v3
+      - name: Run tests
+        run: |
+          sudo apt-get install gcc-multilib g++-multilib
+          cd tests
+          bash ./run.sh ${{ matrix.compiler }} v${{ matrix.version }} 32
+
+  tests-macos11-gpp:
+    strategy:
+      matrix:
+        os: [macos-11]
+        compiler: [g++]
+        version: [9, 10]
+    name: Use ${{ matrix.compiler }}-${{ matrix.version }} on ${{ matrix.os }}
+    runs-on: ${{ matrix.os }}
+    steps:
+      - uses: actions/checkout@v3
+      - name: Run tests
+        run: |
+          cd tests
+          bash ./run.sh ${{ matrix.compiler }} v${{ matrix.version }}
+
+  tests-macos11-clang:
+    strategy:
+      matrix:
+        os: [macos-11]
+        compiler: [clang++]
+    name: Use ${{ matrix.compiler }} on ${{ matrix.os }}
+    runs-on: ${{ matrix.os }}
+    steps:
+      - uses: actions/checkout@v3
+      - name: Run tests
+        run: |
+          cd tests
+          bash ./run.sh ${{ matrix.compiler }}
+
+  tests-macos12-gpp:
+    strategy:
+      matrix:
+        os: [macos-12]
+        compiler: [g++]
+    name: Use ${{ matrix.compiler }} on ${{ matrix.os }}
+    runs-on: ${{ matrix.os }}
+    steps:
+      - uses: actions/checkout@v3
+      - name: Run tests
+        run: |
+          cd tests
+          bash ./run.sh ${{ matrix.compiler }}
+
+  tests-macos12-clang:
+    strategy:
+      matrix:
+        os: [macos-12]
+        compiler: [clang++]
+        version: [12, 13, 14]
+    name: Use ${{ matrix.compiler }} on ${{ matrix.os }}
+    runs-on: ${{ matrix.os }}
+    steps:
+      - uses: actions/checkout@v3
+      - name: Run tests
+        run: |
+          cd tests
+          bash ./run.sh ${{ matrix.compiler }}
+
+  tests-windows:
+    strategy:
+      matrix:
+        os: [windows-2019, windows-2022]
+        compiler: [msvc, g++, clang++]
+    name: Use ${{ matrix.compiler }} on ${{ matrix.os }}
+    runs-on: ${{ matrix.os }}
+    steps:
+      - uses: actions/checkout@v3
+      - name: Run tests
+        run: |
+          cd tests
+          bash ./run.sh ${{ matrix.compiler }}

+ 1 - 1
checkers/uncmp.cpp

@@ -5,7 +5,7 @@ using namespace std;
 
 int main(int argc, char * argv[])
 {
-    setName("compare unordered sequences of signed int%lu numbers", 8 * sizeof(long long));
+    setName("compare unordered sequences of signed int%u numbers", int(8 * sizeof(long long)));
 
     registerTestlibCmd(argc, argv);
 

+ 31 - 7
testlib.h

@@ -179,6 +179,10 @@ const char *latestFeatures[] = {
 #include <stdarg.h>
 #include <fcntl.h>
 
+#ifdef TESTLIB_THROW_EXIT_EXCEPTION_INSTEAD_OF_EXIT
+#   include <exception>
+#endif
+
 #if (_WIN32 || __WIN32__ || _WIN64 || __WIN64__ || __CYGWIN__)
 #   if !defined(_MSC_VER) || _MSC_VER > 1400
 #       define NOMINMAX 1
@@ -372,7 +376,10 @@ static bool __testlib_prelimIsNaN(double r) {
 static std::string removeDoubleTrailingZeroes(std::string value) {
     while (!value.empty() && value[value.length() - 1] == '0' && value.find('.') != std::string::npos)
         value = value.substr(0, value.length() - 1);
-    return value + '0';
+    if (!value.empty() && value[value.length() - 1] == '.')
+        return value + '0';
+    else
+        return value;
 }
 
 #ifdef __GNUC__
@@ -462,14 +469,18 @@ static void __testlib_set_binary(std::FILE *file) {
 #ifdef O_BINARY
 #   ifdef _MSC_VER
         _setmode(_fileno(file), O_BINARY);
-#   elseif
+#   else
         setmode(fileno(file), O_BINARY);
 #   endif
 #else
-    if (file == stdin)
-        freopen(NULL, "rb", file);
-    if (file == stdout || file == stderr)
-        freopen(NULL, "wb", file);
+    if (file == stdin) {
+        if (!freopen(NULL, "rb", file))
+            __testlib_fail("Unable to freopen stdin");
+    }
+    if (file == stdout || file == stderr) {
+        if (!freopen(NULL, "wb", file))
+            __testlib_fail("Unable to freopen stdout/stderr");
+    }
 #endif
     }
 }
@@ -740,7 +751,7 @@ public:
     /* Returns random element from container. */
     template<typename Container>
     typename Container::value_type any(const Container &c) {
-        size_t size = c.size();
+        int size = int(c.size());
         if (size <= 0)
             __testlib_fail("random_t::any(const Container& c): c.size() must be positive");
         return *(c.begin() + next(size));
@@ -2519,12 +2530,25 @@ void InStream::textColor(
 #endif
 }
 
+#ifdef TESTLIB_THROW_EXIT_EXCEPTION_INSTEAD_OF_EXIT
+class exit_exception: public std::exception {
+private:
+    int exitCode;
+public:
+    exit_exception(int exitCode): exitCode(exitCode) {}
+    int getExitCode() { return exitCode; }
+};
+#endif
+
 NORETURN void halt(int exitCode) {
 #ifdef FOOTER
     InStream::textColor(InStream::LightGray);
     std::fprintf(stderr, "Checker: \"%s\"\n", checkerName.c_str());
     std::fprintf(stderr, "Exit code: %d\n", exitCode);
     InStream::textColor(InStream::LightGray);
+#endif
+#ifdef TESTLIB_THROW_EXIT_EXCEPTION_INSTEAD_OF_EXIT
+    throw exit_exception(exitCode);    
 #endif
     std::exit(exitCode);
 }

+ 15 - 0
tests/README.md

@@ -0,0 +1,15 @@
+### Parameters (use can use all combinations):
+
+* `g++`, `clang++`, `msvc` - to specify compiler(s)
+* `11`, `14`, `17`, `20`, `23` - to specify standard(s)
+* `vxx` (example `v10`) - to try `-10` suffix to run compilers, like `g++-10`, use `v0` to run without any suffixes
+* `test-` - to specify test(s)
+
+### Examples:
+    
+* `bash run.sh` - to run all tests on all compilers on multiple standards
+* `bash run.sh g++` - to run all tests on `g++` on multiple standards
+* `bash run.sh g++ 11` - to run all tests on `g++` with `--std=c++11`
+* `bash run.sh g++ 11 test-001_run-sval` - to run `test-001_run-sval` on `g++` with `--std=c++11`
+* `bash run.sh test-001_run-sval` - to run `test-001_run-sval` on all compilers on multiple standards
+* `bash run.sh g++ 11 17` - to run all tests on `g++` with `--std=c++11` and with `--std=c++17`

BIN
tests/lib/msvc-2022-include.7z


+ 4830 - 0
tests/lib/testlib.h

@@ -0,0 +1,4830 @@
+/* 
+ * It is strictly recommended to include "testlib.h" before any other include 
+ * in your code. In this case testlib overrides compiler specific "random()".
+ *
+ * If you can't compile your code and compiler outputs something about 
+ * ambiguous call of "random_shuffle", "rand" or "srand" it means that 
+ * you shouldn't use them. Use "shuffle", and "rnd.next()" instead of them
+ * because these calls produce stable result for any C++ compiler. Read 
+ * sample generator sources for clarification.
+ *
+ * Please read the documentation for class "random_t" and use "rnd" instance in
+ * generators. Probably, these sample calls will be usefull for you:
+ *              rnd.next(); rnd.next(100); rnd.next(1, 2); 
+ *              rnd.next(3.14); rnd.next("[a-z]{1,100}").
+ *
+ * Also read about wnext() to generate off-center random distribution.
+ *
+ * See https://github.com/MikeMirzayanov/testlib/ to get latest version or bug tracker.
+ */
+
+#ifndef _TESTLIB_H_
+#define _TESTLIB_H_
+
+/*
+ * Copyright (c) 2005-2020
+ */
+
+#define VERSION "0.9.27-SNAPSHOT"
+
+/* 
+ * Mike Mirzayanov
+ *
+ * This material is provided "as is", with absolutely no warranty expressed
+ * or implied. Any use is at your own risk.
+ *
+ * Permission to use or copy this software for any purpose is hereby granted 
+ * without fee, provided the above notices are retained on all copies.
+ * Permission to modify the code and to distribute modified code is granted,
+ * provided the above notices are retained, and a notice that the code was
+ * modified is included with the above copyright notice.
+ *
+ */
+
+/* NOTE: This file contains testlib library for C++.
+ *
+ *   Check, using testlib running format:
+ *     check.exe <Input_File> <Output_File> <Answer_File> [<Result_File> [-appes]],
+ *   If result file is specified it will contain results.
+ *
+ *   Validator, using testlib running format:                                          
+ *     validator.exe < input.txt,
+ *   It will return non-zero exit code and writes message to standard output.
+ *
+ *   Generator, using testlib running format:                                          
+ *     gen.exe [parameter-1] [parameter-2] [... paramerter-n]
+ *   You can write generated test(s) into standard output or into the file(s).
+ *
+ *   Interactor, using testlib running format:                                          
+ *     interactor.exe <Input_File> <Output_File> [<Answer_File> [<Result_File> [-appes]]],
+ *   Reads test from inf (mapped to args[1]), writes result to tout (mapped to argv[2],
+ *   can be judged by checker later), reads program output from ouf (mapped to stdin),
+ *   writes output to program via stdout (use cout, printf, etc).
+ */
+
+const char *latestFeatures[] = {
+        "Opts supported: use them like n = opt<int>(\"n\"), in a command line you can use an exponential notation",
+        "Reformatted",
+        "Use setTestCase(i) or unsetTestCase() to support test cases (you can use it in any type of program: generator, interactor, validator or checker)",
+        "Fixed issue #87: readStrictDouble accepts \"-0.00\"",
+        "Fixed issue #83: added InStream::quitif(condition, ...)",
+        "Fixed issue #79: fixed missed guard against repeated header include",
+        "Fixed issue #80: fixed UB in case of huge quitf message",
+        "Fixed issue #84: added readXs(size, indexBase = 1)",
+        "Fixed stringstream repeated usage issue",
+        "Fixed compilation in g++ (for std=c++03)",
+        "Batch of println functions (support collections, iterator ranges)",
+        "Introduced rnd.perm(size, first = 0) to generate a `first`-indexed permutation",
+        "Allow any whitespace in readInts-like functions for non-validators",
+        "Ignore 4+ command line arguments ifdef EJUDGE",
+        "Speed up of vtos",
+        "Show line number in validators in case of incorrect format",
+        "Truncate huge checker/validator/interactor message",
+        "Fixed issue with readTokenTo of very long tokens, now aborts with _pe/_fail depending of a stream type",
+        "Introduced InStream::ensure/ensuref checking a condition, returns wa/fail depending of a stream type",
+        "Fixed compilation in VS 2015+",
+        "Introduced space-separated read functions: readWords/readTokens, multilines read functions: readStrings/readLines",
+        "Introduced space-separated read functions: readInts/readIntegers/readLongs/readUnsignedLongs/readDoubles/readReals/readStrictDoubles/readStrictReals",
+        "Introduced split/tokenize functions to separate string by given char",
+        "Introduced InStream::readUnsignedLong and InStream::readLong with unsigned long long paramerters",
+        "Supported --testOverviewLogFileName for validator: bounds hits + features",
+        "Fixed UB (sequence points) in random_t",
+        "POINTS_EXIT_CODE returned back to 7 (instead of 0)",
+        "Removed disable buffers for interactive problems, because it works unexpectedly in wine",
+        "InStream over string: constructor of InStream from base InStream to inherit policies and std::string",
+        "Added expectedButFound quit function, examples: expectedButFound(_wa, 10, 20), expectedButFound(_fail, ja, pa, \"[n=%d,m=%d]\", n, m)",
+        "Fixed incorrect interval parsing in patterns",
+        "Use registerGen(argc, argv, 1) to develop new generator, use registerGen(argc, argv, 0) to compile old generators (originally created for testlib under 0.8.7)",
+        "Introduced disableFinalizeGuard() to switch off finalization checkings",
+        "Use join() functions to format a range of items as a single string (separated by spaces or other separators)",
+        "Use -DENABLE_UNEXPECTED_EOF to enable special exit code (by default, 8) in case of unexpected eof. It is good idea to use it in interactors",
+        "Use -DUSE_RND_AS_BEFORE_087 to compile in compatibility mode with random behavior of versions before 0.8.7",
+        "Fixed bug with nan in stringToDouble",
+        "Fixed issue around overloads for size_t on x64",
+        "Added attribute 'points' to the XML output in case of result=_points",
+        "Exit codes can be customized via macros, e.g. -DPE_EXIT_CODE=14",
+        "Introduced InStream function readWordTo/readTokenTo/readStringTo/readLineTo for faster reading",
+        "Introduced global functions: format(), englishEnding(), upperCase(), lowerCase(), compress()",
+        "Manual buffer in InStreams, some IO speed improvements",
+        "Introduced quitif(bool, const char* pattern, ...) which delegates to quitf() in case of first argument is true",
+        "Introduced guard against missed quitf() in checker or readEof() in validators",
+        "Supported readStrictReal/readStrictDouble - to use in validators to check strictly float numbers",
+        "Supported registerInteraction(argc, argv)",
+        "Print checker message to the stderr instead of stdout",
+        "Supported TResult _points to output calculated score, use quitp(...) functions",
+        "Fixed to be compilable on Mac",
+        "PC_BASE_EXIT_CODE=50 in case of defined TESTSYS",
+        "Fixed issues 19-21, added __attribute__ format printf",
+        "Some bug fixes",
+        "ouf.readInt(1, 100) and similar calls return WA",
+        "Modified random_t to avoid integer overflow",
+        "Truncated checker output [patch by Stepan Gatilov]",
+        "Renamed class random -> class random_t",
+        "Supported name parameter for read-and-validation methods, like readInt(1, 2, \"n\")",
+        "Fixed bug in readDouble()",
+        "Improved ensuref(), fixed nextLine to work in case of EOF, added startTest()",
+        "Supported \"partially correct\", example: quitf(_pc(13), \"result=%d\", result)",
+        "Added shuffle(begin, end), use it instead of random_shuffle(begin, end)",
+        "Added readLine(const string& ptrn), fixed the logic of readLine() in the validation mode",
+        "Package extended with samples of generators and validators",
+        "Written the documentation for classes and public methods in testlib.h",
+        "Implemented random routine to support generators, use registerGen() to switch it on",
+        "Implemented strict mode to validate tests, use registerValidation() to switch it on",
+        "Now ncmp.cpp and wcmp.cpp are return WA if answer is suffix or prefix of the output",
+        "Added InStream::readLong() and removed InStream::readLongint()",
+        "Now no footer added to each report by default (use directive FOOTER to switch on)",
+        "Now every checker has a name, use setName(const char* format, ...) to set it",
+        "Now it is compatible with TTS (by Kittens Computing)",
+        "Added \'ensure(condition, message = \"\")\' feature, it works like assert()",
+        "Fixed compatibility with MS C++ 7.1",
+        "Added footer with exit code information",
+        "Added compatibility with EJUDGE (compile with EJUDGE directive)",
+        "Added compatibility with Contester (compile with CONTESTER directive)"
+};
+
+#ifdef _MSC_VER
+#define _CRT_SECURE_NO_DEPRECATE
+#define _CRT_SECURE_NO_WARNINGS
+#define _CRT_NO_VA_START_VALIDATION
+#endif
+
+/* Overrides random() for Borland C++. */
+#define random __random_deprecated
+#include <stdlib.h>
+#include <cstdlib>
+#include <climits>
+#include <algorithm>
+#undef random
+
+#include <cstdio>
+#include <cctype>
+#include <string>
+#include <vector>
+#include <map>
+#include <set>
+#include <cmath>
+#include <iostream>
+#include <sstream>
+#include <fstream>
+#include <cstring>
+#include <limits>
+#include <stdarg.h>
+#include <fcntl.h>
+
+#if (_WIN32 || __WIN32__ || _WIN64 || __WIN64__ || __CYGWIN__)
+#   if !defined(_MSC_VER) || _MSC_VER > 1400
+#       define NOMINMAX 1
+#       include <windows.h>
+#   else
+#       define WORD unsigned short
+#       include <unistd.h>
+#   endif
+#   include <io.h>
+#   define ON_WINDOWS
+#   if defined(_MSC_VER) && _MSC_VER > 1400
+#       pragma warning( disable : 4127 )
+#       pragma warning( disable : 4146 )
+#       pragma warning( disable : 4458 )
+#   endif
+#else
+#   define WORD unsigned short
+#   include <unistd.h>
+#endif
+
+#if defined(FOR_WINDOWS) && defined(FOR_LINUX)
+#error Only one target system is allowed
+#endif
+
+#ifndef LLONG_MIN
+#define LLONG_MIN   (-9223372036854775807LL - 1)
+#endif
+
+#ifndef ULLONG_MAX
+#define ULLONG_MAX   (18446744073709551615)
+#endif
+
+#define LF ((char)10)
+#define CR ((char)13)
+#define TAB ((char)9)
+#define SPACE ((char)' ')
+#define EOFC (255)
+
+#ifndef OK_EXIT_CODE
+#   ifdef CONTESTER
+#       define OK_EXIT_CODE 0xAC
+#   else
+#       define OK_EXIT_CODE 0
+#   endif
+#endif
+
+#ifndef WA_EXIT_CODE
+#   ifdef EJUDGE
+#       define WA_EXIT_CODE 5
+#   elif defined(CONTESTER)
+#       define WA_EXIT_CODE 0xAB
+#   else
+#       define WA_EXIT_CODE 1
+#   endif
+#endif
+
+#ifndef PE_EXIT_CODE
+#   ifdef EJUDGE
+#       define PE_EXIT_CODE 4
+#   elif defined(CONTESTER)
+#       define PE_EXIT_CODE 0xAA
+#   else
+#       define PE_EXIT_CODE 2
+#   endif
+#endif
+
+#ifndef FAIL_EXIT_CODE
+#   ifdef EJUDGE
+#       define FAIL_EXIT_CODE 6
+#   elif defined(CONTESTER)
+#       define FAIL_EXIT_CODE 0xA3
+#   else
+#       define FAIL_EXIT_CODE 3
+#   endif
+#endif
+
+#ifndef DIRT_EXIT_CODE
+#   ifdef EJUDGE
+#       define DIRT_EXIT_CODE 6
+#   else
+#       define DIRT_EXIT_CODE 4
+#   endif
+#endif
+
+#ifndef POINTS_EXIT_CODE
+#   define POINTS_EXIT_CODE 7
+#endif
+
+#ifndef UNEXPECTED_EOF_EXIT_CODE
+#   define UNEXPECTED_EOF_EXIT_CODE 8
+#endif
+
+#ifndef PC_BASE_EXIT_CODE
+#   ifdef TESTSYS
+#       define PC_BASE_EXIT_CODE 50
+#   else
+#       define PC_BASE_EXIT_CODE 0
+#   endif
+#endif
+
+#ifdef __GNUC__
+#    define __TESTLIB_STATIC_ASSERT(condition) typedef void* __testlib_static_assert_type[(condition) ? 1 : -1] __attribute__((unused))
+#else
+#    define __TESTLIB_STATIC_ASSERT(condition) typedef void* __testlib_static_assert_type[(condition) ? 1 : -1]
+#endif
+
+#ifdef ON_WINDOWS
+#define I64 "%I64d"
+#define U64 "%I64u"
+#else
+#define I64 "%lld"
+#define U64 "%llu"
+#endif
+
+#ifdef _MSC_VER
+#   define NORETURN __declspec(noreturn)
+#elif defined __GNUC__
+#   define NORETURN __attribute__ ((noreturn))
+#else
+#   define NORETURN
+#endif
+
+static char __testlib_format_buffer[16777216];
+static int __testlib_format_buffer_usage_count = 0;
+
+#define FMT_TO_RESULT(fmt, cstr, result)  std::string result;                              \
+            if (__testlib_format_buffer_usage_count != 0)                                  \
+                __testlib_fail("FMT_TO_RESULT::__testlib_format_buffer_usage_count != 0"); \
+            __testlib_format_buffer_usage_count++;                                         \
+            va_list ap;                                                                    \
+            va_start(ap, fmt);                                                             \
+            vsnprintf(__testlib_format_buffer, sizeof(__testlib_format_buffer), cstr, ap); \
+            va_end(ap);                                                                    \
+            __testlib_format_buffer[sizeof(__testlib_format_buffer) - 1] = 0;              \
+            result = std::string(__testlib_format_buffer);                                 \
+            __testlib_format_buffer_usage_count--;                                         \
+
+const long long __TESTLIB_LONGLONG_MAX = 9223372036854775807LL;
+
+bool __testlib_hasTestCase;
+int __testlib_testCase = -1;
+
+void setTestCase(int testCase) {
+    __testlib_hasTestCase = true;
+    __testlib_testCase = testCase;
+}
+
+void unsetTestCase() {
+    __testlib_hasTestCase = false;
+    __testlib_testCase = -1;
+}
+
+NORETURN static void __testlib_fail(const std::string &message);
+
+template<typename T>
+static inline T __testlib_abs(const T &x) {
+    return x > 0 ? x : -x;
+}
+
+template<typename T>
+static inline T __testlib_min(const T &a, const T &b) {
+    return a < b ? a : b;
+}
+
+template<typename T>
+static inline T __testlib_max(const T &a, const T &b) {
+    return a > b ? a : b;
+}
+
+static bool __testlib_prelimIsNaN(double r) {
+    volatile double ra = r;
+#ifndef __BORLANDC__
+    return ((ra != ra) == true) && ((ra == ra) == false) && ((1.0 > ra) == false) && ((1.0 < ra) == false);
+#else
+    return std::_isnan(ra);
+#endif
+}
+
+static std::string removeDoubleTrailingZeroes(std::string value) {
+    while (!value.empty() && value[value.length() - 1] == '0' && value.find('.') != std::string::npos)
+        value = value.substr(0, value.length() - 1);
+    return value + '0';
+}
+
+#ifdef __GNUC__
+__attribute__ ((format (printf, 1, 2)))
+#endif
+std::string format(const char *fmt, ...) {
+    FMT_TO_RESULT(fmt, fmt, result);
+    return result;
+}
+
+std::string format(const std::string fmt, ...) {
+    FMT_TO_RESULT(fmt, fmt.c_str(), result);
+    return result;
+}
+
+static std::string __testlib_part(const std::string &s);
+
+static bool __testlib_isNaN(double r) {
+    __TESTLIB_STATIC_ASSERT(sizeof(double) == sizeof(long long));
+    volatile double ra = r;
+    long long llr1, llr2;
+    std::memcpy((void *) &llr1, (void *) &ra, sizeof(double));
+    ra = -ra;
+    std::memcpy((void *) &llr2, (void *) &ra, sizeof(double));
+    long long llnan = 0xFFF8000000000000LL;
+    return __testlib_prelimIsNaN(r) || llnan == llr1 || llnan == llr2;
+}
+
+static double __testlib_nan() {
+    __TESTLIB_STATIC_ASSERT(sizeof(double) == sizeof(long long));
+#ifndef NAN
+    long long llnan = 0xFFF8000000000000LL;
+    double nan;
+    std::memcpy(&nan, &llnan, sizeof(double));
+    return nan;
+#else
+    return NAN;
+#endif
+}
+
+static bool __testlib_isInfinite(double r) {
+    volatile double ra = r;
+    return (ra > 1E300 || ra < -1E300);
+}
+
+#ifdef __GNUC__
+__attribute__((const))
+#endif
+inline bool doubleCompare(double expected, double result, double MAX_DOUBLE_ERROR) {
+    if (__testlib_isNaN(expected)) {
+        return __testlib_isNaN(result);
+    } else if (__testlib_isInfinite(expected)) {
+        if (expected > 0) {
+            return result > 0 && __testlib_isInfinite(result);
+        } else {
+            return result < 0 && __testlib_isInfinite(result);
+        }
+    } else if (__testlib_isNaN(result) || __testlib_isInfinite(result)) {
+        return false;
+    } else if (__testlib_abs(result - expected) <= MAX_DOUBLE_ERROR + 1E-15) {
+        return true;
+    } else {
+        double minv = __testlib_min(expected * (1.0 - MAX_DOUBLE_ERROR),
+                                    expected * (1.0 + MAX_DOUBLE_ERROR));
+        double maxv = __testlib_max(expected * (1.0 - MAX_DOUBLE_ERROR),
+                                    expected * (1.0 + MAX_DOUBLE_ERROR));
+        return result + 1E-15 >= minv && result <= maxv + 1E-15;
+    }
+}
+
+#ifdef __GNUC__
+__attribute__((const))
+#endif
+inline double doubleDelta(double expected, double result) {
+    double absolute = __testlib_abs(result - expected);
+
+    if (__testlib_abs(expected) > 1E-9) {
+        double relative = __testlib_abs(absolute / expected);
+        return __testlib_min(absolute, relative);
+    } else
+        return absolute;
+}
+
+#if !defined(_MSC_VER) || _MSC_VER < 1900
+#ifndef _fileno
+#define _fileno(_stream)  ((_stream)->_file)
+#endif
+#endif
+
+#ifndef O_BINARY
+static void __testlib_set_binary(
+#ifdef __GNUC__
+    __attribute__((unused)) 
+#endif
+    std::FILE* file
+)
+#else
+static void __testlib_set_binary(std::FILE *file)
+#endif
+{
+#ifdef O_BINARY
+    if (NULL != file) {
+#ifndef __BORLANDC__
+        _setmode(_fileno(file), O_BINARY);
+#else
+        setmode(fileno(file), O_BINARY);
+#endif
+    }
+#endif
+}
+
+#if __cplusplus > 199711L || defined(_MSC_VER)
+/* opts */
+void prepareOpts(int argc, char* argv[]);
+#endif
+
+/*
+ * Very simple regex-like pattern.
+ * It used for two purposes: validation and generation.
+ * 
+ * For example, pattern("[a-z]{1,5}").next(rnd) will return
+ * random string from lowercase latin letters with length 
+ * from 1 to 5. It is easier to call rnd.next("[a-z]{1,5}") 
+ * for the same effect. 
+ * 
+ * Another samples:
+ * "mike|john" will generate (match) "mike" or "john";
+ * "-?[1-9][0-9]{0,3}" will generate (match) non-zero integers from -9999 to 9999;
+ * "id-([ac]|b{2})" will generate (match) "id-a", "id-bb", "id-c";
+ * "[^0-9]*" will match sequences (empty or non-empty) without digits, you can't 
+ * use it for generations.
+ *
+ * You can't use pattern for generation if it contains meta-symbol '*'. Also it
+ * is not recommended to use it for char-sets with meta-symbol '^' like [^a-z].
+ *
+ * For matching very simple greedy algorithm is used. For example, pattern
+ * "[0-9]?1" will not match "1", because of greedy nature of matching.
+ * Alternations (meta-symbols "|") are processed with brute-force algorithm, so 
+ * do not use many alternations in one expression.
+ *
+ * If you want to use one expression many times it is better to compile it into
+ * a single pattern like "pattern p("[a-z]+")". Later you can use 
+ * "p.matches(std::string s)" or "p.next(random_t& rd)" to check matching or generate
+ * new string by pattern.
+ * 
+ * Simpler way to read token and check it for pattern matching is "inf.readToken("[a-z]+")".
+ */
+class random_t;
+
+class pattern {
+public:
+    /* Create pattern instance by string. */
+    pattern(std::string s);
+
+    /* Generate new string by pattern and given random_t. */
+    std::string next(random_t &rnd) const;
+
+    /* Checks if given string match the pattern. */
+    bool matches(const std::string &s) const;
+
+    /* Returns source string of the pattern. */
+    std::string src() const;
+
+private:
+    bool matches(const std::string &s, size_t pos) const;
+
+    std::string s;
+    std::vector<pattern> children;
+    std::vector<char> chars;
+    int from;
+    int to;
+};
+
+/* 
+ * Use random_t instances to generate random values. It is preffered
+ * way to use randoms instead of rand() function or self-written 
+ * randoms.
+ *
+ * Testlib defines global variable "rnd" of random_t class.
+ * Use registerGen(argc, argv, 1) to setup random_t seed be command
+ * line (to use latest random generator version).
+ *
+ * Random generates uniformly distributed values if another strategy is
+ * not specified explicitly.
+ */
+class random_t {
+private:
+    unsigned long long seed;
+    static const unsigned long long multiplier;
+    static const unsigned long long addend;
+    static const unsigned long long mask;
+    static const int lim;
+
+    long long nextBits(int bits) {
+        if (bits <= 48) {
+            seed = (seed * multiplier + addend) & mask;
+            return (long long) (seed >> (48 - bits));
+        } else {
+            if (bits > 63)
+                __testlib_fail("random_t::nextBits(int bits): n must be less than 64");
+
+            int lowerBitCount = (random_t::version == 0 ? 31 : 32);
+
+            long long left = (nextBits(31) << 32);
+            long long right = nextBits(lowerBitCount);
+
+            return left ^ right;
+        }
+    }
+
+public:
+    static int version;
+
+    /* New random_t with fixed seed. */
+    random_t()
+            : seed(3905348978240129619LL) {
+    }
+
+    /* Sets seed by command line. */
+    void setSeed(int argc, char *argv[]) {
+        random_t p;
+
+        seed = 3905348978240129619LL;
+        for (int i = 1; i < argc; i++) {
+            std::size_t le = std::strlen(argv[i]);
+            for (std::size_t j = 0; j < le; j++)
+                seed = seed * multiplier + (unsigned int) (argv[i][j]) + addend;
+            seed += multiplier / addend;
+        }
+
+        seed = seed & mask;
+    }
+
+    /* Sets seed by given value. */
+    void setSeed(long long _seed) {
+        _seed = (_seed ^ multiplier) & mask;
+        seed = _seed;
+    }
+
+#ifndef __BORLANDC__
+
+    /* Random string value by given pattern (see pattern documentation). */
+    std::string next(const std::string &ptrn) {
+        pattern p(ptrn);
+        return p.next(*this);
+    }
+
+#else
+    /* Random string value by given pattern (see pattern documentation). */
+    std::string next(std::string ptrn)
+    {
+        pattern p(ptrn);
+        return p.next(*this);
+    }
+#endif
+
+    /* Random value in range [0, n-1]. */
+    int next(int n) {
+        if (n <= 0)
+            __testlib_fail("random_t::next(int n): n must be positive");
+
+        if ((n & -n) == n)  // n is a power of 2
+            return (int) ((n * (long long) nextBits(31)) >> 31);
+
+        const long long limit = INT_MAX / n * n;
+
+        long long bits;
+        do {
+            bits = nextBits(31);
+        } while (bits >= limit);
+
+        return int(bits % n);
+    }
+
+    /* Random value in range [0, n-1]. */
+    unsigned int next(unsigned int n) {
+        if (n >= INT_MAX)
+            __testlib_fail("random_t::next(unsigned int n): n must be less INT_MAX");
+        return (unsigned int) next(int(n));
+    }
+
+    /* Random value in range [0, n-1]. */
+    long long next(long long n) {
+        if (n <= 0)
+            __testlib_fail("random_t::next(long long n): n must be positive");
+
+        const long long limit = __TESTLIB_LONGLONG_MAX / n * n;
+
+        long long bits;
+        do {
+            bits = nextBits(63);
+        } while (bits >= limit);
+
+        return bits % n;
+    }
+
+    /* Random value in range [0, n-1]. */
+    unsigned long long next(unsigned long long n) {
+        if (n >= (unsigned long long) (__TESTLIB_LONGLONG_MAX))
+            __testlib_fail("random_t::next(unsigned long long n): n must be less LONGLONG_MAX");
+        return (unsigned long long) next((long long) (n));
+    }
+
+    /* Random value in range [0, n-1]. */
+    long next(long n) {
+        return (long) next((long long) (n));
+    }
+
+    /* Random value in range [0, n-1]. */
+    unsigned long next(unsigned long n) {
+        if (n >= (unsigned long) (LONG_MAX))
+            __testlib_fail("random_t::next(unsigned long n): n must be less LONG_MAX");
+        return (unsigned long) next((unsigned long long) (n));
+    }
+
+    /* Returns random value in range [from,to]. */
+    int next(int from, int to) {
+        return int(next((long long) to - from + 1) + from);
+    }
+
+    /* Returns random value in range [from,to]. */
+    unsigned int next(unsigned int from, unsigned int to) {
+        return (unsigned int) (next((long long) to - from + 1) + from);
+    }
+
+    /* Returns random value in range [from,to]. */
+    long long next(long long from, long long to) {
+        return next(to - from + 1) + from;
+    }
+
+    /* Returns random value in range [from,to]. */
+    unsigned long long next(unsigned long long from, unsigned long long to) {
+        if (from > to)
+            __testlib_fail("random_t::next(unsigned long long from, unsigned long long to): from can't not exceed to");
+        return next(to - from + 1) + from;
+    }
+
+    /* Returns random value in range [from,to]. */
+    long next(long from, long to) {
+        return next(to - from + 1) + from;
+    }
+
+    /* Returns random value in range [from,to]. */
+    unsigned long next(unsigned long from, unsigned long to) {
+        if (from > to)
+            __testlib_fail("random_t::next(unsigned long from, unsigned long to): from can't not exceed to");
+        return next(to - from + 1) + from;
+    }
+
+    /* Random double value in range [0, 1). */
+    double next() {
+        long long left = ((long long) (nextBits(26)) << 27);
+        long long right = nextBits(27);
+        return (double) (left + right) / (double) (1LL << 53);
+    }
+
+    /* Random double value in range [0, n). */
+    double next(double n) {
+        return n * next();
+    }
+
+    /* Random double value in range [from, to). */
+    double next(double from, double to) {
+        if (from > to)
+            __testlib_fail("random_t::next(double from, double to): from can't not exceed to");
+        return next(to - from) + from;
+    }
+
+    /* Returns random element from container. */
+    template<typename Container>
+    typename Container::value_type any(const Container &c) {
+        size_t size = c.size();
+        if (size <= 0)
+            __testlib_fail("random_t::any(const Container& c): c.size() must be positive");
+        return *(c.begin() + next(size));
+    }
+
+    /* Returns random element from iterator range. */
+    template<typename Iter>
+    typename Iter::value_type any(const Iter &begin, const Iter &end) {
+        int size = int(end - begin);
+        if (size <= 0)
+            __testlib_fail("random_t::any(const Iter& begin, const Iter& end): range must have positive length");
+        return *(begin + next(size));
+    }
+
+    /* Random string value by given pattern (see pattern documentation). */
+#ifdef __GNUC__
+    __attribute__ ((format (printf, 2, 3)))
+#endif
+    std::string next(const char *format, ...) {
+        FMT_TO_RESULT(format, format, ptrn);
+        return next(ptrn);
+    }
+
+    /* 
+     * Weighted next. If type == 0 than it is usual "next()".
+     *
+     * If type = 1, than it returns "max(next(), next())"
+     * (the number of "max" functions equals to "type").
+     *
+     * If type < 0, than "max" function replaces with "min".
+     */
+    int wnext(int n, int type) {
+        if (n <= 0)
+            __testlib_fail("random_t::wnext(int n, int type): n must be positive");
+
+        if (abs(type) < random_t::lim) {
+            int result = next(n);
+
+            for (int i = 0; i < +type; i++)
+                result = __testlib_max(result, next(n));
+
+            for (int i = 0; i < -type; i++)
+                result = __testlib_min(result, next(n));
+
+            return result;
+        } else {
+            double p;
+
+            if (type > 0)
+                p = std::pow(next() + 0.0, 1.0 / (type + 1));
+            else
+                p = 1 - std::pow(next() + 0.0, 1.0 / (-type + 1));
+
+            return int(n * p);
+        }
+    }
+
+    /* See wnext(int, int). It uses the same algorithms. */
+    long long wnext(long long n, int type) {
+        if (n <= 0)
+            __testlib_fail("random_t::wnext(long long n, int type): n must be positive");
+
+        if (abs(type) < random_t::lim) {
+            long long result = next(n);
+
+            for (int i = 0; i < +type; i++)
+                result = __testlib_max(result, next(n));
+
+            for (int i = 0; i < -type; i++)
+                result = __testlib_min(result, next(n));
+
+            return result;
+        } else {
+            double p;
+
+            if (type > 0)
+                p = std::pow(next() + 0.0, 1.0 / (type + 1));
+            else
+                p = std::pow(next() + 0.0, -type + 1);
+
+            return __testlib_min(__testlib_max((long long) (double(n) * p), 0LL), n - 1LL);
+        }
+    }
+
+    /* See wnext(int, int). It uses the same algorithms. */
+    double wnext(int type) {
+        if (abs(type) < random_t::lim) {
+            double result = next();
+
+            for (int i = 0; i < +type; i++)
+                result = __testlib_max(result, next());
+
+            for (int i = 0; i < -type; i++)
+                result = __testlib_min(result, next());
+
+            return result;
+        } else {
+            double p;
+
+            if (type > 0)
+                p = std::pow(next() + 0.0, 1.0 / (type + 1));
+            else
+                p = std::pow(next() + 0.0, -type + 1);
+
+            return p;
+        }
+    }
+
+    /* See wnext(int, int). It uses the same algorithms. */
+    double wnext(double n, int type) {
+        if (n <= 0)
+            __testlib_fail("random_t::wnext(double n, int type): n must be positive");
+
+        if (abs(type) < random_t::lim) {
+            double result = next();
+
+            for (int i = 0; i < +type; i++)
+                result = __testlib_max(result, next());
+
+            for (int i = 0; i < -type; i++)
+                result = __testlib_min(result, next());
+
+            return n * result;
+        } else {
+            double p;
+
+            if (type > 0)
+                p = std::pow(next() + 0.0, 1.0 / (type + 1));
+            else
+                p = std::pow(next() + 0.0, -type + 1);
+
+            return n * p;
+        }
+    }
+
+    /* See wnext(int, int). It uses the same algorithms. */
+    unsigned int wnext(unsigned int n, int type) {
+        if (n >= INT_MAX)
+            __testlib_fail("random_t::wnext(unsigned int n, int type): n must be less INT_MAX");
+        return (unsigned int) wnext(int(n), type);
+    }
+
+    /* See wnext(int, int). It uses the same algorithms. */
+    unsigned long long wnext(unsigned long long n, int type) {
+        if (n >= (unsigned long long) (__TESTLIB_LONGLONG_MAX))
+            __testlib_fail("random_t::wnext(unsigned long long n, int type): n must be less LONGLONG_MAX");
+
+        return (unsigned long long) wnext((long long) (n), type);
+    }
+
+    /* See wnext(int, int). It uses the same algorithms. */
+    long wnext(long n, int type) {
+        return (long) wnext((long long) (n), type);
+    }
+
+    /* See wnext(int, int). It uses the same algorithms. */
+    unsigned long wnext(unsigned long n, int type) {
+        if (n >= (unsigned long) (LONG_MAX))
+            __testlib_fail("random_t::wnext(unsigned long n, int type): n must be less LONG_MAX");
+
+        return (unsigned long) wnext((unsigned long long) (n), type);
+    }
+
+    /* Returns weighted random value in range [from, to]. */
+    int wnext(int from, int to, int type) {
+        if (from > to)
+            __testlib_fail("random_t::wnext(int from, int to, int type): from can't not exceed to");
+        return wnext(to - from + 1, type) + from;
+    }
+
+    /* Returns weighted random value in range [from, to]. */
+    int wnext(unsigned int from, unsigned int to, int type) {
+        if (from > to)
+            __testlib_fail("random_t::wnext(unsigned int from, unsigned int to, int type): from can't not exceed to");
+        return int(wnext(to - from + 1, type) + from);
+    }
+
+    /* Returns weighted random value in range [from, to]. */
+    long long wnext(long long from, long long to, int type) {
+        if (from > to)
+            __testlib_fail("random_t::wnext(long long from, long long to, int type): from can't not exceed to");
+        return wnext(to - from + 1, type) + from;
+    }
+
+    /* Returns weighted random value in range [from, to]. */
+    unsigned long long wnext(unsigned long long from, unsigned long long to, int type) {
+        if (from > to)
+            __testlib_fail(
+                    "random_t::wnext(unsigned long long from, unsigned long long to, int type): from can't not exceed to");
+        return wnext(to - from + 1, type) + from;
+    }
+
+    /* Returns weighted random value in range [from, to]. */
+    long wnext(long from, long to, int type) {
+        if (from > to)
+            __testlib_fail("random_t::wnext(long from, long to, int type): from can't not exceed to");
+        return wnext(to - from + 1, type) + from;
+    }
+
+    /* Returns weighted random value in range [from, to]. */
+    unsigned long wnext(unsigned long from, unsigned long to, int type) {
+        if (from > to)
+            __testlib_fail("random_t::wnext(unsigned long from, unsigned long to, int type): from can't not exceed to");
+        return wnext(to - from + 1, type) + from;
+    }
+
+    /* Returns weighted random double value in range [from, to). */
+    double wnext(double from, double to, int type) {
+        if (from > to)
+            __testlib_fail("random_t::wnext(double from, double to, int type): from can't not exceed to");
+        return wnext(to - from, type) + from;
+    }
+
+    /* Returns weighted random element from container. */
+    template<typename Container>
+    typename Container::value_type wany(const Container &c, int type) {
+        size_t size = c.size();
+        if (size <= 0)
+            __testlib_fail("random_t::wany(const Container& c, int type): c.size() must be positive");
+        return *(c.begin() + wnext(size, type));
+    }
+
+    /* Returns weighted random element from iterator range. */
+    template<typename Iter>
+    typename Iter::value_type wany(const Iter &begin, const Iter &end, int type) {
+        int size = int(end - begin);
+        if (size <= 0)
+            __testlib_fail(
+                    "random_t::any(const Iter& begin, const Iter& end, int type): range must have positive length");
+        return *(begin + wnext(size, type));
+    }
+
+    template<typename T, typename E>
+    std::vector<E> perm(T size, E first) {
+        if (size <= 0)
+            __testlib_fail("random_t::perm(T size, E first = 0): size must be positive");
+        std::vector<E> p(size);
+        for (T i = 0; i < size; i++)
+            p[i] = first + i;
+        if (size > 1)
+            for (T i = 1; i < size; i++)
+                std::swap(p[i], p[next(i + 1)]);
+        return p;
+    }
+
+    template<typename T>
+    std::vector<T> perm(T size) {
+        return perm(size, T(0));
+    }
+};
+
+const int random_t::lim = 25;
+const unsigned long long random_t::multiplier = 0x5DEECE66DLL;
+const unsigned long long random_t::addend = 0xBLL;
+const unsigned long long random_t::mask = (1LL << 48) - 1;
+int random_t::version = -1;
+
+/* Pattern implementation */
+bool pattern::matches(const std::string &s) const {
+    return matches(s, 0);
+}
+
+static bool __pattern_isSlash(const std::string &s, size_t pos) {
+    return s[pos] == '\\';
+}
+
+#ifdef __GNUC__
+__attribute__((pure))
+#endif
+static bool __pattern_isCommandChar(const std::string &s, size_t pos, char value) {
+    if (pos >= s.length())
+        return false;
+
+    int slashes = 0;
+
+    int before = int(pos) - 1;
+    while (before >= 0 && s[before] == '\\')
+        before--, slashes++;
+
+    return slashes % 2 == 0 && s[pos] == value;
+}
+
+static char __pattern_getChar(const std::string &s, size_t &pos) {
+    if (__pattern_isSlash(s, pos))
+        pos += 2;
+    else
+        pos++;
+
+    return s[pos - 1];
+}
+
+#ifdef __GNUC__
+__attribute__((pure))
+#endif
+static int __pattern_greedyMatch(const std::string &s, size_t pos, const std::vector<char> chars) {
+    int result = 0;
+
+    while (pos < s.length()) {
+        char c = s[pos++];
+        if (!std::binary_search(chars.begin(), chars.end(), c))
+            break;
+        else
+            result++;
+    }
+
+    return result;
+}
+
+std::string pattern::src() const {
+    return s;
+}
+
+bool pattern::matches(const std::string &s, size_t pos) const {
+    std::string result;
+
+    if (to > 0) {
+        int size = __pattern_greedyMatch(s, pos, chars);
+        if (size < from)
+            return false;
+        if (size > to)
+            size = to;
+        pos += size;
+    }
+
+    if (children.size() > 0) {
+        for (size_t child = 0; child < children.size(); child++)
+            if (children[child].matches(s, pos))
+                return true;
+        return false;
+    } else
+        return pos == s.length();
+}
+
+std::string pattern::next(random_t &rnd) const {
+    std::string result;
+    result.reserve(20);
+
+    if (to == INT_MAX)
+        __testlib_fail("pattern::next(random_t& rnd): can't process character '*' for generation");
+
+    if (to > 0) {
+        int count = rnd.next(to - from + 1) + from;
+        for (int i = 0; i < count; i++)
+            result += chars[rnd.next(int(chars.size()))];
+    }
+
+    if (children.size() > 0) {
+        int child = rnd.next(int(children.size()));
+        result += children[child].next(rnd);
+    }
+
+    return result;
+}
+
+static void __pattern_scanCounts(const std::string &s, size_t &pos, int &from, int &to) {
+    if (pos >= s.length()) {
+        from = to = 1;
+        return;
+    }
+
+    if (__pattern_isCommandChar(s, pos, '{')) {
+        std::vector<std::string> parts;
+        std::string part;
+
+        pos++;
+
+        while (pos < s.length() && !__pattern_isCommandChar(s, pos, '}')) {
+            if (__pattern_isCommandChar(s, pos, ','))
+                parts.push_back(part), part = "", pos++;
+            else
+                part += __pattern_getChar(s, pos);
+        }
+
+        if (part != "")
+            parts.push_back(part);
+
+        if (!__pattern_isCommandChar(s, pos, '}'))
+            __testlib_fail("pattern: Illegal pattern (or part) \"" + s + "\"");
+
+        pos++;
+
+        if (parts.size() < 1 || parts.size() > 2)
+            __testlib_fail("pattern: Illegal pattern (or part) \"" + s + "\"");
+
+        std::vector<int> numbers;
+
+        for (size_t i = 0; i < parts.size(); i++) {
+            if (parts[i].length() == 0)
+                __testlib_fail("pattern: Illegal pattern (or part) \"" + s + "\"");
+            int number;
+            if (std::sscanf(parts[i].c_str(), "%d", &number) != 1)
+                __testlib_fail("pattern: Illegal pattern (or part) \"" + s + "\"");
+            numbers.push_back(number);
+        }
+
+        if (numbers.size() == 1)
+            from = to = numbers[0];
+        else
+            from = numbers[0], to = numbers[1];
+
+        if (from > to)
+            __testlib_fail("pattern: Illegal pattern (or part) \"" + s + "\"");
+    } else {
+        if (__pattern_isCommandChar(s, pos, '?')) {
+            from = 0, to = 1, pos++;
+            return;
+        }
+
+        if (__pattern_isCommandChar(s, pos, '*')) {
+            from = 0, to = INT_MAX, pos++;
+            return;
+        }
+
+        if (__pattern_isCommandChar(s, pos, '+')) {
+            from = 1, to = INT_MAX, pos++;
+            return;
+        }
+
+        from = to = 1;
+    }
+}
+
+static std::vector<char> __pattern_scanCharSet(const std::string &s, size_t &pos) {
+    if (pos >= s.length())
+        __testlib_fail("pattern: Illegal pattern (or part) \"" + s + "\"");
+
+    std::vector<char> result;
+
+    if (__pattern_isCommandChar(s, pos, '[')) {
+        pos++;
+        bool negative = __pattern_isCommandChar(s, pos, '^');
+
+        char prev = 0;
+
+        while (pos < s.length() && !__pattern_isCommandChar(s, pos, ']')) {
+            if (__pattern_isCommandChar(s, pos, '-') && prev != 0) {
+                pos++;
+
+                if (pos + 1 == s.length() || __pattern_isCommandChar(s, pos, ']')) {
+                    result.push_back(prev);
+                    prev = '-';
+                    continue;
+                }
+
+                char next = __pattern_getChar(s, pos);
+                if (prev > next)
+                    __testlib_fail("pattern: Illegal pattern (or part) \"" + s + "\"");
+
+                for (char c = prev; c != next; c++)
+                    result.push_back(c);
+                result.push_back(next);
+
+                prev = 0;
+            } else {
+                if (prev != 0)
+                    result.push_back(prev);
+                prev = __pattern_getChar(s, pos);
+            }
+        }
+
+        if (prev != 0)
+            result.push_back(prev);
+
+        if (!__pattern_isCommandChar(s, pos, ']'))
+            __testlib_fail("pattern: Illegal pattern (or part) \"" + s + "\"");
+
+        pos++;
+
+        if (negative) {
+            std::sort(result.begin(), result.end());
+            std::vector<char> actuals;
+            for (int code = 0; code < 255; code++) {
+                char c = char(code);
+                if (!std::binary_search(result.begin(), result.end(), c))
+                    actuals.push_back(c);
+            }
+            result = actuals;
+        }
+
+        std::sort(result.begin(), result.end());
+    } else
+        result.push_back(__pattern_getChar(s, pos));
+
+    return result;
+}
+
+pattern::pattern(std::string s) : s(s), from(0), to(0) {
+    std::string t;
+    for (size_t i = 0; i < s.length(); i++)
+        if (!__pattern_isCommandChar(s, i, ' '))
+            t += s[i];
+    s = t;
+
+    int opened = 0;
+    int firstClose = -1;
+    std::vector<int> seps;
+
+    for (size_t i = 0; i < s.length(); i++) {
+        if (__pattern_isCommandChar(s, i, '(')) {
+            opened++;
+            continue;
+        }
+
+        if (__pattern_isCommandChar(s, i, ')')) {
+            opened--;
+            if (opened == 0 && firstClose == -1)
+                firstClose = int(i);
+            continue;
+        }
+
+        if (opened < 0)
+            __testlib_fail("pattern: Illegal pattern (or part) \"" + s + "\"");
+
+        if (__pattern_isCommandChar(s, i, '|') && opened == 0)
+            seps.push_back(int(i));
+    }
+
+    if (opened != 0)
+        __testlib_fail("pattern: Illegal pattern (or part) \"" + s + "\"");
+
+    if (seps.size() == 0 && firstClose + 1 == (int) s.length()
+        && __pattern_isCommandChar(s, 0, '(') && __pattern_isCommandChar(s, s.length() - 1, ')')) {
+        children.push_back(pattern(s.substr(1, s.length() - 2)));
+    } else {
+        if (seps.size() > 0) {
+            seps.push_back(int(s.length()));
+            int last = 0;
+
+            for (size_t i = 0; i < seps.size(); i++) {
+                children.push_back(pattern(s.substr(last, seps[i] - last)));
+                last = seps[i] + 1;
+            }
+        } else {
+            size_t pos = 0;
+            chars = __pattern_scanCharSet(s, pos);
+            __pattern_scanCounts(s, pos, from, to);
+            if (pos < s.length())
+                children.push_back(pattern(s.substr(pos)));
+        }
+    }
+}
+
+/* End of pattern implementation */
+
+template<typename C>
+inline bool isEof(C c) {
+    return c == EOFC;
+}
+
+template<typename C>
+inline bool isEoln(C c) {
+    return (c == LF || c == CR);
+}
+
+template<typename C>
+inline bool isBlanks(C c) {
+    return (c == LF || c == CR || c == SPACE || c == TAB);
+}
+
+inline std::string trim(const std::string &s) {
+    if (s.empty())
+        return s;
+
+    int left = 0;
+    while (left < int(s.length()) && isBlanks(s[left]))
+        left++;
+    if (left >= int(s.length()))
+        return "";
+
+    int right = int(s.length()) - 1;
+    while (right >= 0 && isBlanks(s[right]))
+        right--;
+    if (right < 0)
+        return "";
+
+    return s.substr(left, right - left + 1);
+}
+
+enum TMode {
+    _input, _output, _answer
+};
+
+/* Outcomes 6-15 are reserved for future use. */
+enum TResult {
+    _ok = 0,
+    _wa = 1,
+    _pe = 2,
+    _fail = 3,
+    _dirt = 4,
+    _points = 5,
+    _unexpected_eof = 8,
+    _partially = 16
+};
+
+enum TTestlibMode {
+    _unknown, _checker, _validator, _generator, _interactor
+};
+
+#define _pc(exitCode) (TResult(_partially + (exitCode)))
+
+/* Outcomes 6-15 are reserved for future use. */
+const std::string outcomes[] = {
+        "accepted",
+        "wrong-answer",
+        "presentation-error",
+        "fail",
+        "fail",
+#ifndef PCMS2
+        "points",
+#else
+        "relative-scoring",
+#endif
+        "reserved",
+        "reserved",
+        "unexpected-eof",
+        "reserved",
+        "reserved",
+        "reserved",
+        "reserved",
+        "reserved",
+        "reserved",
+        "reserved",
+        "partially-correct"
+};
+
+class InputStreamReader {
+public:
+    virtual int curChar() = 0;
+
+    virtual int nextChar() = 0;
+
+    virtual void skipChar() = 0;
+
+    virtual void unreadChar(int c) = 0;
+
+    virtual std::string getName() = 0;
+
+    virtual bool eof() = 0;
+
+    virtual void close() = 0;
+
+    virtual int getLine() = 0;
+
+    virtual ~InputStreamReader() = 0;
+};
+
+InputStreamReader::~InputStreamReader() {
+    // No operations.
+}
+
+class StringInputStreamReader : public InputStreamReader {
+private:
+    std::string s;
+    size_t pos;
+
+public:
+    StringInputStreamReader(const std::string &content) : s(content), pos(0) {
+        // No operations.
+    }
+
+    int curChar() {
+        if (pos >= s.length())
+            return EOFC;
+        else
+            return s[pos];
+    }
+
+    int nextChar() {
+        if (pos >= s.length()) {
+            pos++;
+            return EOFC;
+        } else
+            return s[pos++];
+    }
+
+    void skipChar() {
+        pos++;
+    }
+
+    void unreadChar(int c) {
+        if (pos == 0)
+            __testlib_fail("FileFileInputStreamReader::unreadChar(int): pos == 0.");
+        pos--;
+        if (pos < s.length())
+            s[pos] = char(c);
+    }
+
+    std::string getName() {
+        return __testlib_part(s);
+    }
+
+    int getLine() {
+        return -1;
+    }
+
+    bool eof() {
+        return pos >= s.length();
+    }
+
+    void close() {
+        // No operations.
+    }
+};
+
+class FileInputStreamReader : public InputStreamReader {
+private:
+    std::FILE *file;
+    std::string name;
+    int line;
+    std::vector<int> undoChars;
+
+    inline int postprocessGetc(int getcResult) {
+        if (getcResult != EOF)
+            return getcResult;
+        else
+            return EOFC;
+    }
+
+    int getc(FILE *file) {
+        int c;
+        if (undoChars.empty())
+            c = ::getc(file);
+        else {
+            c = undoChars.back();
+            undoChars.pop_back();
+        }
+
+        if (c == LF)
+            line++;
+        return c;
+    }
+
+    int ungetc(int c/*, FILE* file*/) {
+        if (c == LF)
+            line--;
+        undoChars.push_back(c);
+        return c;
+    }
+
+public:
+    FileInputStreamReader(std::FILE *file, const std::string &name) : file(file), name(name), line(1) {
+        // No operations.
+    }
+
+    int curChar() {
+        if (feof(file))
+            return EOFC;
+        else {
+            int c = getc(file);
+            ungetc(c/*, file*/);
+            return postprocessGetc(c);
+        }
+    }
+
+    int nextChar() {
+        if (feof(file))
+            return EOFC;
+        else
+            return postprocessGetc(getc(file));
+    }
+
+    void skipChar() {
+        getc(file);
+    }
+
+    void unreadChar(int c) {
+        ungetc(c/*, file*/);
+    }
+
+    std::string getName() {
+        return name;
+    }
+
+    int getLine() {
+        return line;
+    }
+
+    bool eof() {
+        if (NULL == file || feof(file))
+            return true;
+        else {
+            int c = nextChar();
+            if (c == EOFC || (c == EOF && feof(file)))
+                return true;
+            unreadChar(c);
+            return false;
+        }
+    }
+
+    void close() {
+        if (NULL != file) {
+            fclose(file);
+            file = NULL;
+        }
+    }
+};
+
+class BufferedFileInputStreamReader : public InputStreamReader {
+private:
+    static const size_t BUFFER_SIZE;
+    static const size_t MAX_UNREAD_COUNT;
+
+    std::FILE *file;
+    char *buffer;
+    bool *isEof;
+    int bufferPos;
+    size_t bufferSize;
+
+    std::string name;
+    int line;
+
+    bool refill() {
+        if (NULL == file)
+            __testlib_fail("BufferedFileInputStreamReader: file == NULL (" + getName() + ")");
+
+        if (bufferPos >= int(bufferSize)) {
+            size_t readSize = fread(
+                    buffer + MAX_UNREAD_COUNT,
+                    1,
+                    BUFFER_SIZE - MAX_UNREAD_COUNT,
+                    file
+            );
+
+            if (readSize < BUFFER_SIZE - MAX_UNREAD_COUNT
+                && ferror(file))
+                __testlib_fail("BufferedFileInputStreamReader: unable to read (" + getName() + ")");
+
+            bufferSize = MAX_UNREAD_COUNT + readSize;
+            bufferPos = int(MAX_UNREAD_COUNT);
+            std::memset(isEof + MAX_UNREAD_COUNT, 0, sizeof(isEof[0]) * readSize);
+
+            return readSize > 0;
+        } else
+            return true;
+    }
+
+    char increment() {
+        char c;
+        if ((c = buffer[bufferPos++]) == LF)
+            line++;
+        return c;
+    }
+
+public:
+    BufferedFileInputStreamReader(std::FILE *file, const std::string &name) : file(file), name(name), line(1) {
+        buffer = new char[BUFFER_SIZE];
+        isEof = new bool[BUFFER_SIZE];
+        bufferSize = MAX_UNREAD_COUNT;
+        bufferPos = int(MAX_UNREAD_COUNT);
+    }
+
+    ~BufferedFileInputStreamReader() {
+        if (NULL != buffer) {
+            delete[] buffer;
+            buffer = NULL;
+        }
+        if (NULL != isEof) {
+            delete[] isEof;
+            isEof = NULL;
+        }
+    }
+
+    int curChar() {
+        if (!refill())
+            return EOFC;
+
+        return isEof[bufferPos] ? EOFC : buffer[bufferPos];
+    }
+
+    int nextChar() {
+        if (!refill())
+            return EOFC;
+
+        return isEof[bufferPos] ? EOFC : increment();
+    }
+
+    void skipChar() {
+        increment();
+    }
+
+    void unreadChar(int c) {
+        bufferPos--;
+        if (bufferPos < 0)
+            __testlib_fail("BufferedFileInputStreamReader::unreadChar(int): bufferPos < 0");
+        isEof[bufferPos] = (c == EOFC);
+        buffer[bufferPos] = char(c);
+        if (c == LF)
+            line--;
+    }
+
+    std::string getName() {
+        return name;
+    }
+
+    int getLine() {
+        return line;
+    }
+
+    bool eof() {
+        return !refill() || EOFC == curChar();
+    }
+
+    void close() {
+        if (NULL != file) {
+            fclose(file);
+            file = NULL;
+        }
+    }
+};
+
+const size_t BufferedFileInputStreamReader::BUFFER_SIZE = 2000000;
+const size_t BufferedFileInputStreamReader::MAX_UNREAD_COUNT = BufferedFileInputStreamReader::BUFFER_SIZE / 2;
+
+/*
+ * Streams to be used for reading data in checkers or validators.
+ * Each read*() method moves pointer to the next character after the
+ * read value.
+ */
+struct InStream {
+    /* Do not use them. */
+    InStream();
+
+    ~InStream();
+
+    /* Wrap std::string with InStream. */
+    InStream(const InStream &baseStream, std::string content);
+
+    InputStreamReader *reader;
+    int lastLine;
+
+    std::string name;
+    TMode mode;
+    bool opened;
+    bool stdfile;
+    bool strict;
+
+    int wordReserveSize;
+    std::string _tmpReadToken;
+
+    int readManyIteration;
+    size_t maxFileSize;
+    size_t maxTokenLength;
+    size_t maxMessageLength;
+
+    void init(std::string fileName, TMode mode);
+
+    void init(std::FILE *f, TMode mode);
+
+    /* Moves stream pointer to the first non-white-space character or EOF. */
+    void skipBlanks();
+
+    /* Returns current character in the stream. Doesn't remove it from stream. */
+    char curChar();
+
+    /* Moves stream pointer one character forward. */
+    void skipChar();
+
+    /* Returns current character and moves pointer one character forward. */
+    char nextChar();
+
+    /* Returns current character and moves pointer one character forward. */
+    char readChar();
+
+    /* As "readChar()" but ensures that the result is equal to given parameter. */
+    char readChar(char c);
+
+    /* As "readChar()" but ensures that the result is equal to the space (code=32). */
+    char readSpace();
+
+    /* Puts back the character into the stream. */
+    void unreadChar(char c);
+
+    /* Reopens stream, you should not use it. */
+    void reset(std::FILE *file = NULL);
+
+    /* Checks that current position is EOF. If not it doesn't move stream pointer. */
+    bool eof();
+
+    /* Moves pointer to the first non-white-space character and calls "eof()". */
+    bool seekEof();
+
+    /* 
+     * Checks that current position contains EOLN. 
+     * If not it doesn't move stream pointer. 
+     * In strict mode expects "#13#10" for windows or "#10" for other platforms.
+     */
+    bool eoln();
+
+    /* Moves pointer to the first non-space and non-tab character and calls "eoln()". */
+    bool seekEoln();
+
+    /* Moves stream pointer to the first character of the next line (if exists). */
+    void nextLine();
+
+    /* 
+     * Reads new token. Ignores white-spaces into the non-strict mode 
+     * (strict mode is used in validators usually). 
+     */
+    std::string readWord();
+
+    /* The same as "readWord()", it is preffered to use "readToken()". */
+    std::string readToken();
+
+    /* The same as "readWord()", but ensures that token matches to given pattern. */
+    std::string readWord(const std::string &ptrn, const std::string &variableName = "");
+
+    std::string readWord(const pattern &p, const std::string &variableName = "");
+
+    std::vector<std::string>
+    readWords(int size, const std::string &ptrn, const std::string &variablesName = "", int indexBase = 1);
+
+    std::vector<std::string>
+    readWords(int size, const pattern &p, const std::string &variablesName = "", int indexBase = 1);
+
+    std::vector<std::string> readWords(int size, int indexBase = 1);
+
+    /* The same as "readToken()", but ensures that token matches to given pattern. */
+    std::string readToken(const std::string &ptrn, const std::string &variableName = "");
+
+    std::string readToken(const pattern &p, const std::string &variableName = "");
+
+    std::vector<std::string>
+    readTokens(int size, const std::string &ptrn, const std::string &variablesName = "", int indexBase = 1);
+
+    std::vector<std::string>
+    readTokens(int size, const pattern &p, const std::string &variablesName = "", int indexBase = 1);
+
+    std::vector<std::string> readTokens(int size, int indexBase = 1);
+
+    void readWordTo(std::string &result);
+
+    void readWordTo(std::string &result, const pattern &p, const std::string &variableName = "");
+
+    void readWordTo(std::string &result, const std::string &ptrn, const std::string &variableName = "");
+
+    void readTokenTo(std::string &result);
+
+    void readTokenTo(std::string &result, const pattern &p, const std::string &variableName = "");
+
+    void readTokenTo(std::string &result, const std::string &ptrn, const std::string &variableName = "");
+
+    /* 
+     * Reads new long long value. Ignores white-spaces into the non-strict mode 
+     * (strict mode is used in validators usually). 
+     */
+    long long readLong();
+
+    unsigned long long readUnsignedLong();
+
+    /*
+     * Reads new int. Ignores white-spaces into the non-strict mode 
+     * (strict mode is used in validators usually). 
+     */
+    int readInteger();
+
+    /*
+     * Reads new int. Ignores white-spaces into the non-strict mode 
+     * (strict mode is used in validators usually). 
+     */
+    int readInt();
+
+    /* As "readLong()" but ensures that value in the range [minv,maxv]. */
+    long long readLong(long long minv, long long maxv, const std::string &variableName = "");
+
+    /* Reads space-separated sequence of long longs. */
+    std::vector<long long>
+    readLongs(int size, long long minv, long long maxv, const std::string &variablesName = "", int indexBase = 1);
+
+    /* Reads space-separated sequence of long longs. */
+    std::vector<long long> readLongs(int size, int indexBase = 1);
+
+    unsigned long long
+    readUnsignedLong(unsigned long long minv, unsigned long long maxv, const std::string &variableName = "");
+
+    std::vector<unsigned long long>
+    readUnsignedLongs(int size, unsigned long long minv, unsigned long long maxv, const std::string &variablesName = "",
+                      int indexBase = 1);
+
+    std::vector<unsigned long long> readUnsignedLongs(int size, int indexBase = 1);
+
+    unsigned long long readLong(unsigned long long minv, unsigned long long maxv, const std::string &variableName = "");
+
+    std::vector<unsigned long long>
+    readLongs(int size, unsigned long long minv, unsigned long long maxv, const std::string &variablesName = "",
+              int indexBase = 1);
+
+    /* As "readInteger()" but ensures that value in the range [minv,maxv]. */
+    int readInteger(int minv, int maxv, const std::string &variableName = "");
+
+    /* As "readInt()" but ensures that value in the range [minv,maxv]. */
+    int readInt(int minv, int maxv, const std::string &variableName = "");
+
+    /* Reads space-separated sequence of integers. */
+    std::vector<int>
+    readIntegers(int size, int minv, int maxv, const std::string &variablesName = "", int indexBase = 1);
+
+    /* Reads space-separated sequence of integers. */
+    std::vector<int> readIntegers(int size, int indexBase = 1);
+
+    /* Reads space-separated sequence of integers. */
+    std::vector<int> readInts(int size, int minv, int maxv, const std::string &variablesName = "", int indexBase = 1);
+
+    /* Reads space-separated sequence of integers. */
+    std::vector<int> readInts(int size, int indexBase = 1);
+
+    /* 
+     * Reads new double. Ignores white-spaces into the non-strict mode 
+     * (strict mode is used in validators usually). 
+     */
+    double readReal();
+
+    /*
+     * Reads new double. Ignores white-spaces into the non-strict mode 
+     * (strict mode is used in validators usually). 
+     */
+    double readDouble();
+
+    /* As "readReal()" but ensures that value in the range [minv,maxv]. */
+    double readReal(double minv, double maxv, const std::string &variableName = "");
+
+    std::vector<double>
+    readReals(int size, double minv, double maxv, const std::string &variablesName = "", int indexBase = 1);
+
+    std::vector<double> readReals(int size, int indexBase = 1);
+
+    /* As "readDouble()" but ensures that value in the range [minv,maxv]. */
+    double readDouble(double minv, double maxv, const std::string &variableName = "");
+
+    std::vector<double>
+    readDoubles(int size, double minv, double maxv, const std::string &variablesName = "", int indexBase = 1);
+
+    std::vector<double> readDoubles(int size, int indexBase = 1);
+
+    /* 
+     * As "readReal()" but ensures that value in the range [minv,maxv] and
+     * number of digit after the decimal point is in range [minAfterPointDigitCount,maxAfterPointDigitCount]
+     * and number is in the form "[-]digit(s)[.digit(s)]".
+     */
+    double readStrictReal(double minv, double maxv,
+                          int minAfterPointDigitCount, int maxAfterPointDigitCount,
+                          const std::string &variableName = "");
+
+    std::vector<double> readStrictReals(int size, double minv, double maxv,
+                                        int minAfterPointDigitCount, int maxAfterPointDigitCount,
+                                        const std::string &variablesName = "", int indexBase = 1);
+
+    /* 
+     * As "readDouble()" but ensures that value in the range [minv,maxv] and
+     * number of digit after the decimal point is in range [minAfterPointDigitCount,maxAfterPointDigitCount]
+     * and number is in the form "[-]digit(s)[.digit(s)]".
+     */
+    double readStrictDouble(double minv, double maxv,
+                            int minAfterPointDigitCount, int maxAfterPointDigitCount,
+                            const std::string &variableName = "");
+
+    std::vector<double> readStrictDoubles(int size, double minv, double maxv,
+                                          int minAfterPointDigitCount, int maxAfterPointDigitCount,
+                                          const std::string &variablesName = "", int indexBase = 1);
+
+    /* As readLine(). */
+    std::string readString();
+
+    /* Read many lines. */
+    std::vector<std::string> readStrings(int size, int indexBase = 1);
+
+    /* See readLine(). */
+    void readStringTo(std::string &result);
+
+    /* The same as "readLine()/readString()", but ensures that line matches to the given pattern. */
+    std::string readString(const pattern &p, const std::string &variableName = "");
+
+    /* The same as "readLine()/readString()", but ensures that line matches to the given pattern. */
+    std::string readString(const std::string &ptrn, const std::string &variableName = "");
+
+    /* Read many lines. */
+    std::vector<std::string>
+    readStrings(int size, const pattern &p, const std::string &variableName = "", int indexBase = 1);
+
+    /* Read many lines. */
+    std::vector<std::string>
+    readStrings(int size, const std::string &ptrn, const std::string &variableName = "", int indexBase = 1);
+
+    /* The same as "readLine()/readString()", but ensures that line matches to the given pattern. */
+    void readStringTo(std::string &result, const pattern &p, const std::string &variableName = "");
+
+    /* The same as "readLine()/readString()", but ensures that line matches to the given pattern. */
+    void readStringTo(std::string &result, const std::string &ptrn, const std::string &variableName = "");
+
+    /* 
+     * Reads line from the current position to EOLN or EOF. Moves stream pointer to 
+     * the first character of the new line (if possible). 
+     */
+    std::string readLine();
+
+    /* Read many lines. */
+    std::vector<std::string> readLines(int size, int indexBase = 1);
+
+    /* See readLine(). */
+    void readLineTo(std::string &result);
+
+    /* The same as "readLine()", but ensures that line matches to the given pattern. */
+    std::string readLine(const pattern &p, const std::string &variableName = "");
+
+    /* The same as "readLine()", but ensures that line matches to the given pattern. */
+    std::string readLine(const std::string &ptrn, const std::string &variableName = "");
+
+    /* Read many lines. */
+    std::vector<std::string>
+    readLines(int size, const pattern &p, const std::string &variableName = "", int indexBase = 1);
+
+    /* Read many lines. */
+    std::vector<std::string>
+    readLines(int size, const std::string &ptrn, const std::string &variableName = "", int indexBase = 1);
+
+    /* The same as "readLine()", but ensures that line matches to the given pattern. */
+    void readLineTo(std::string &result, const pattern &p, const std::string &variableName = "");
+
+    /* The same as "readLine()", but ensures that line matches to the given pattern. */
+    void readLineTo(std::string &result, const std::string &ptrn, const std::string &variableName = "");
+
+    /* Reads EOLN or fails. Use it in validators. Calls "eoln()" method internally. */
+    void readEoln();
+
+    /* Reads EOF or fails. Use it in validators. Calls "eof()" method internally. */
+    void readEof();
+
+    /* 
+     * Quit-functions aborts program with <result> and <message>:
+     * input/answer streams replace any result to FAIL.
+     */
+    NORETURN void quit(TResult result, const char *msg);
+    /* 
+     * Quit-functions aborts program with <result> and <message>:
+     * input/answer streams replace any result to FAIL.
+     */
+    NORETURN void quitf(TResult result, const char *msg, ...);
+
+    /*
+     * Quit-functions aborts program with <result> and <message>:
+     * input/answer streams replace any result to FAIL.
+     */
+    void quitif(bool condition, TResult result, const char *msg, ...);
+    /* 
+     * Quit-functions aborts program with <result> and <message>:
+     * input/answer streams replace any result to FAIL.
+     */
+    NORETURN void quits(TResult result, std::string msg);
+
+    /* 
+     * Checks condition and aborts a program if codition is false.
+     * Returns _wa for ouf and _fail on any other streams.
+     */
+#ifdef __GNUC__
+    __attribute__ ((format (printf, 3, 4)))
+#endif
+    void ensuref(bool cond, const char *format, ...);
+
+    void __testlib_ensure(bool cond, std::string message);
+
+    void close();
+
+    const static int NO_INDEX = INT_MAX;
+    const static char OPEN_BRACKET = char(11);
+    const static char CLOSE_BRACKET = char(17);
+
+    const static WORD LightGray = 0x07;
+    const static WORD LightRed = 0x0c;
+    const static WORD LightCyan = 0x0b;
+    const static WORD LightGreen = 0x0a;
+    const static WORD LightYellow = 0x0e;
+
+    static void textColor(WORD color);
+
+    static void quitscr(WORD color, const char *msg);
+
+    static void quitscrS(WORD color, std::string msg);
+
+    void xmlSafeWrite(std::FILE *file, const char *msg);
+
+private:
+    InStream(const InStream &);
+
+    InStream &operator=(const InStream &);
+};
+
+InStream inf;
+InStream ouf;
+InStream ans;
+bool appesMode;
+std::string resultName;
+std::string checkerName = "untitled checker";
+random_t rnd;
+TTestlibMode testlibMode = _unknown;
+double __testlib_points = std::numeric_limits<float>::infinity();
+
+struct ValidatorBoundsHit {
+    static const double EPS;
+    bool minHit;
+    bool maxHit;
+
+    ValidatorBoundsHit(bool minHit = false, bool maxHit = false) : minHit(minHit), maxHit(maxHit) {
+    };
+
+    ValidatorBoundsHit merge(const ValidatorBoundsHit &validatorBoundsHit) {
+        return ValidatorBoundsHit(
+                __testlib_max(minHit, validatorBoundsHit.minHit),
+                __testlib_max(maxHit, validatorBoundsHit.maxHit)
+        );
+    }
+};
+
+const double ValidatorBoundsHit::EPS = 1E-12;
+
+class Validator {
+private:
+    std::string _testset;
+    std::string _group;
+    std::string _testOverviewLogFileName;
+    std::map<std::string, ValidatorBoundsHit> _boundsHitByVariableName;
+    std::set<std::string> _features;
+    std::set<std::string> _hitFeatures;
+
+    bool isVariableNameBoundsAnalyzable(const std::string &variableName) {
+        for (size_t i = 0; i < variableName.length(); i++)
+            if ((variableName[i] >= '0' && variableName[i] <= '9') || variableName[i] < ' ')
+                return false;
+        return true;
+    }
+
+    bool isFeatureNameAnalyzable(const std::string &featureName) {
+        for (size_t i = 0; i < featureName.length(); i++)
+            if (featureName[i] < ' ')
+                return false;
+        return true;
+    }
+
+public:
+    Validator() : _testset("tests"), _group() {
+    }
+
+    std::string testset() const {
+        return _testset;
+    }
+
+    std::string group() const {
+        return _group;
+    }
+
+    std::string testOverviewLogFileName() const {
+        return _testOverviewLogFileName;
+    }
+
+    void setTestset(const char *const testset) {
+        _testset = testset;
+    }
+
+    void setGroup(const char *const group) {
+        _group = group;
+    }
+
+    void setTestOverviewLogFileName(const char *const testOverviewLogFileName) {
+        _testOverviewLogFileName = testOverviewLogFileName;
+    }
+
+    void addBoundsHit(const std::string &variableName, ValidatorBoundsHit boundsHit) {
+        if (isVariableNameBoundsAnalyzable(variableName)) {
+            _boundsHitByVariableName[variableName]
+                    = boundsHit.merge(_boundsHitByVariableName[variableName]);
+        }
+    }
+
+    std::string getBoundsHitLog() {
+        std::string result;
+        for (std::map<std::string, ValidatorBoundsHit>::iterator i = _boundsHitByVariableName.begin();
+             i != _boundsHitByVariableName.end();
+             i++) {
+            result += "\"" + i->first + "\":";
+            if (i->second.minHit)
+                result += " min-value-hit";
+            if (i->second.maxHit)
+                result += " max-value-hit";
+            result += "\n";
+        }
+        return result;
+    }
+
+    std::string getFeaturesLog() {
+        std::string result;
+        for (std::set<std::string>::iterator i = _features.begin();
+             i != _features.end();
+             i++) {
+            result += "feature \"" + *i + "\":";
+            if (_hitFeatures.count(*i))
+                result += " hit";
+            result += "\n";
+        }
+        return result;
+    }
+
+    void writeTestOverviewLog() {
+        if (!_testOverviewLogFileName.empty()) {
+            std::string fileName(_testOverviewLogFileName);
+            _testOverviewLogFileName = "";
+            FILE *testOverviewLogFile = fopen(fileName.c_str(), "w");
+            if (NULL == testOverviewLogFile)
+                __testlib_fail("Validator::writeTestOverviewLog: can't test overview log to (" + fileName + ")");
+            fprintf(testOverviewLogFile, "%s%s", getBoundsHitLog().c_str(), getFeaturesLog().c_str());
+            if (fclose(testOverviewLogFile))
+                __testlib_fail(
+                        "Validator::writeTestOverviewLog: can't close test overview log file (" + fileName + ")");
+        }
+    }
+
+    void addFeature(const std::string &feature) {
+        if (_features.count(feature))
+            __testlib_fail("Feature " + feature + " registered twice.");
+        if (!isFeatureNameAnalyzable(feature))
+            __testlib_fail("Feature name '" + feature + "' contains restricted characters.");
+
+        _features.insert(feature);
+    }
+
+    void feature(const std::string &feature) {
+        if (!isFeatureNameAnalyzable(feature))
+            __testlib_fail("Feature name '" + feature + "' contains restricted characters.");
+
+        if (!_features.count(feature))
+            __testlib_fail("Feature " + feature + " didn't registered via addFeature(feature).");
+
+        _hitFeatures.insert(feature);
+    }
+} validator;
+
+struct TestlibFinalizeGuard {
+    static bool alive;
+    int quitCount, readEofCount;
+
+    TestlibFinalizeGuard() : quitCount(0), readEofCount(0) {
+        // No operations.
+    }
+
+    ~TestlibFinalizeGuard() {
+        bool _alive = alive;
+        alive = false;
+
+        if (_alive) {
+            if (testlibMode == _checker && quitCount == 0)
+                __testlib_fail("Checker must end with quit or quitf call.");
+
+            if (testlibMode == _validator && readEofCount == 0 && quitCount == 0)
+                __testlib_fail("Validator must end with readEof call.");
+        }
+
+        validator.writeTestOverviewLog();
+    }
+};
+
+bool TestlibFinalizeGuard::alive = true;
+TestlibFinalizeGuard testlibFinalizeGuard;
+
+/*
+ * Call it to disable checks on finalization.
+ */
+void disableFinalizeGuard() {
+    TestlibFinalizeGuard::alive = false;
+}
+
+/* Interactor streams.
+ */
+std::fstream tout;
+
+/* implementation
+ */
+
+#if __cplusplus > 199711L || defined(_MSC_VER)
+template<typename T>
+static std::string vtos(const T &t, std::true_type) {
+    if (t == 0)
+        return "0";
+    else {
+        T n(t);
+        bool negative = n < 0;
+        std::string s;
+        while (n != 0) {
+            T digit = n % 10;
+            if (digit < 0)
+                digit = -digit;
+            s += char('0' + digit);
+            n /= 10;
+        }
+        std::reverse(s.begin(), s.end());
+        return negative ? "-" + s : s;
+    }
+}
+
+template<typename T>
+static std::string vtos(const T &t, std::false_type) {
+    std::string s;
+    static std::stringstream ss;
+    ss.str(std::string());
+    ss.clear();
+    ss << t;
+    ss >> s;
+    return s;
+}
+
+template<typename T>
+static std::string vtos(const T &t) {
+    return vtos(t, std::is_integral<T>());
+}
+
+#else
+template<typename T>
+static std::string vtos(const T& t)
+{
+    std::string s;
+    static std::stringstream ss;
+    ss.str(std::string());
+    ss.clear();
+    ss << t;
+    ss >> s;
+    return s;
+}
+#endif
+
+template<typename T>
+static std::string toString(const T &t) {
+    return vtos(t);
+}
+
+InStream::InStream() {
+    reader = NULL;
+    lastLine = -1;
+    name = "";
+    mode = _input;
+    strict = false;
+    stdfile = false;
+    wordReserveSize = 4;
+    readManyIteration = NO_INDEX;
+    maxFileSize = 128 * 1024 * 1024; // 128MB.
+    maxTokenLength = 32 * 1024 * 1024; // 32MB.
+    maxMessageLength = 32000;
+}
+
+InStream::InStream(const InStream &baseStream, std::string content) {
+    reader = new StringInputStreamReader(content);
+    lastLine = -1;
+    opened = true;
+    strict = baseStream.strict;
+    mode = baseStream.mode;
+    name = "based on " + baseStream.name;
+    readManyIteration = NO_INDEX;
+    maxFileSize = 128 * 1024 * 1024; // 128MB.
+    maxTokenLength = 32 * 1024 * 1024; // 32MB.
+    maxMessageLength = 32000;
+}
+
+InStream::~InStream() {
+    if (NULL != reader) {
+        reader->close();
+        delete reader;
+        reader = NULL;
+    }
+}
+
+#ifdef __GNUC__
+__attribute__((const))
+#endif
+int resultExitCode(TResult r) {
+    if (r == _ok)
+        return OK_EXIT_CODE;
+    if (r == _wa)
+        return WA_EXIT_CODE;
+    if (r == _pe)
+        return PE_EXIT_CODE;
+    if (r == _fail)
+        return FAIL_EXIT_CODE;
+    if (r == _dirt)
+        return DIRT_EXIT_CODE;
+    if (r == _points)
+        return POINTS_EXIT_CODE;
+    if (r == _unexpected_eof)
+#ifdef ENABLE_UNEXPECTED_EOF
+        return UNEXPECTED_EOF_EXIT_CODE;
+#else
+        return PE_EXIT_CODE;
+#endif
+    if (r >= _partially)
+        return PC_BASE_EXIT_CODE + (r - _partially);
+    return FAIL_EXIT_CODE;
+}
+
+void InStream::textColor(
+#if !(defined(ON_WINDOWS) && (!defined(_MSC_VER) || _MSC_VER > 1400)) && defined(__GNUC__)
+        __attribute__((unused))
+#endif
+        WORD color
+) {
+#if defined(ON_WINDOWS) && (!defined(_MSC_VER) || _MSC_VER > 1400)
+    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
+    SetConsoleTextAttribute(handle, color);
+#endif
+#if !defined(ON_WINDOWS) && defined(__GNUC__)
+    if (isatty(2))
+    {
+        switch (color)
+        {
+        case LightRed:
+            fprintf(stderr, "\033[1;31m");
+            break;
+        case LightCyan:
+            fprintf(stderr, "\033[1;36m");
+            break;
+        case LightGreen:
+            fprintf(stderr, "\033[1;32m");
+            break;
+        case LightYellow:
+            fprintf(stderr, "\033[1;33m");
+            break;
+        case LightGray:
+        default:
+            fprintf(stderr, "\033[0m");
+        }
+    }
+#endif
+}
+
+NORETURN void halt(int exitCode) {
+#ifdef FOOTER
+    InStream::textColor(InStream::LightGray);
+    std::fprintf(stderr, "Checker: \"%s\"\n", checkerName.c_str());
+    std::fprintf(stderr, "Exit code: %d\n", exitCode);
+    InStream::textColor(InStream::LightGray);
+#endif
+    std::exit(exitCode);
+}
+
+static bool __testlib_shouldCheckDirt(TResult result) {
+    return result == _ok || result == _points || result >= _partially;
+}
+
+static std::string __testlib_appendMessage(const std::string &message, const std::string &extra) {
+    int openPos = -1, closePos = -1;
+    for (size_t i = 0; i < message.length(); i++) {
+        if (message[i] == InStream::OPEN_BRACKET) {
+            if (openPos == -1)
+                openPos = i;
+            else
+                openPos = INT_MAX;
+        }
+        if (message[i] == InStream::CLOSE_BRACKET) {
+            if (closePos == -1)
+                closePos = i;
+            else
+                closePos = INT_MAX;
+        }
+    }
+    if (openPos != -1 && openPos != INT_MAX
+        && closePos != -1 && closePos != INT_MAX
+        && openPos < closePos) {
+        size_t index = message.find(extra, openPos);
+        if (index == std::string::npos || int(index) >= closePos) {
+            std::string result(message);
+            result.insert(closePos, ", " + extra);
+            return result;
+        }
+        return message;
+    }
+
+    return message + " " + InStream::OPEN_BRACKET + extra + InStream::CLOSE_BRACKET;
+}
+
+static std::string __testlib_toPrintableMessage(const std::string &message) {
+    int openPos = -1, closePos = -1;
+    for (size_t i = 0; i < message.length(); i++) {
+        if (message[i] == InStream::OPEN_BRACKET) {
+            if (openPos == -1)
+                openPos = i;
+            else
+                openPos = INT_MAX;
+        }
+        if (message[i] == InStream::CLOSE_BRACKET) {
+            if (closePos == -1)
+                closePos = i;
+            else
+                closePos = INT_MAX;
+        }
+    }
+    if (openPos != -1 && openPos != INT_MAX
+        && closePos != -1 && closePos != INT_MAX
+        && openPos < closePos) {
+        std::string result(message);
+        result[openPos] = '(';
+        result[closePos] = ')';
+        return result;
+    }
+
+    return message;
+}
+
+NORETURN void InStream::quit(TResult result, const char *msg) {
+    if (TestlibFinalizeGuard::alive)
+        testlibFinalizeGuard.quitCount++;
+
+    std::string message(msg);
+    message = trim(message);
+
+    if (__testlib_hasTestCase) {
+        if (result != _ok)
+            message = __testlib_appendMessage(message, "test case " + vtos(__testlib_testCase));
+        else {
+            if (__testlib_testCase == 1)
+                message = __testlib_appendMessage(message, vtos(__testlib_testCase) + " test case");
+            else
+                message = __testlib_appendMessage(message, vtos(__testlib_testCase) + " test cases");
+        }
+    }
+
+    // You can change maxMessageLength.
+    // Example: 'inf.maxMessageLength = 1024 * 1024;'.
+    if (message.length() > maxMessageLength) {
+        std::string warn = "message length exceeds " + vtos(maxMessageLength)
+                           + ", the message is truncated: ";
+        message = warn + message.substr(0, maxMessageLength - warn.length());
+    }
+
+#ifndef ENABLE_UNEXPECTED_EOF
+    if (result == _unexpected_eof)
+        result = _pe;
+#endif
+
+    if (mode != _output && result != _fail) {
+        if (mode == _input && testlibMode == _validator && lastLine != -1)
+            quits(_fail, __testlib_appendMessage(__testlib_appendMessage(message, name), "line " + vtos(lastLine)));
+        else
+            quits(_fail, __testlib_appendMessage(message, name));
+    }
+
+    std::FILE *resultFile;
+    std::string errorName;
+
+    if (__testlib_shouldCheckDirt(result)) {
+        if (testlibMode != _interactor && !ouf.seekEof())
+            quit(_dirt, "Extra information in the output file");
+    }
+
+    int pctype = result - _partially;
+    bool isPartial = false;
+
+    switch (result) {
+        case _ok:
+            errorName = "ok ";
+            quitscrS(LightGreen, errorName);
+            break;
+        case _wa:
+            errorName = "wrong answer ";
+            quitscrS(LightRed, errorName);
+            break;
+        case _pe:
+            errorName = "wrong output format ";
+            quitscrS(LightRed, errorName);
+            break;
+        case _fail:
+            errorName = "FAIL ";
+            quitscrS(LightRed, errorName);
+            break;
+        case _dirt:
+            errorName = "wrong output format ";
+            quitscrS(LightCyan, errorName);
+            result = _pe;
+            break;
+        case _points:
+            errorName = "points ";
+            quitscrS(LightYellow, errorName);
+            break;
+        case _unexpected_eof:
+            errorName = "unexpected eof ";
+            quitscrS(LightCyan, errorName);
+            break;
+        default:
+            if (result >= _partially) {
+                errorName = format("partially correct (%d) ", pctype);
+                isPartial = true;
+                quitscrS(LightYellow, errorName);
+            } else
+                quit(_fail, "What is the code ??? ");
+    }
+
+    if (resultName != "") {
+        resultFile = std::fopen(resultName.c_str(), "w");
+        if (resultFile == NULL) {
+            resultName = "";
+            quit(_fail, "Can not write to the result file");
+        }
+        if (appesMode) {
+            std::fprintf(resultFile, "<?xml version=\"1.0\" encoding=\"windows-1251\"?>");
+            if (isPartial)
+                std::fprintf(resultFile, "<result outcome = \"%s\" pctype = \"%d\">",
+                             outcomes[(int) _partially].c_str(), pctype);
+            else {
+                if (result != _points)
+                    std::fprintf(resultFile, "<result outcome = \"%s\">", outcomes[(int) result].c_str());
+                else {
+                    if (__testlib_points == std::numeric_limits<float>::infinity())
+                        quit(_fail, "Expected points, but infinity found");
+                    std::string stringPoints = removeDoubleTrailingZeroes(format("%.10f", __testlib_points));
+                    std::fprintf(resultFile, "<result outcome = \"%s\" points = \"%s\">",
+                                 outcomes[(int) result].c_str(), stringPoints.c_str());
+                }
+            }
+            xmlSafeWrite(resultFile, __testlib_toPrintableMessage(message).c_str());
+            std::fprintf(resultFile, "</result>\n");
+        } else
+            std::fprintf(resultFile, "%s", __testlib_toPrintableMessage(message).c_str());
+        if (NULL == resultFile || fclose(resultFile) != 0) {
+            resultName = "";
+            quit(_fail, "Can not write to the result file");
+        }
+    }
+
+    quitscr(LightGray, __testlib_toPrintableMessage(message).c_str());
+    std::fprintf(stderr, "\n");
+
+    inf.close();
+    ouf.close();
+    ans.close();
+    if (tout.is_open())
+        tout.close();
+
+    textColor(LightGray);
+
+    if (resultName != "")
+        std::fprintf(stderr, "See file to check exit message\n");
+
+    halt(resultExitCode(result));
+}
+
+#ifdef __GNUC__
+__attribute__ ((format (printf, 3, 4)))
+#endif
+NORETURN void InStream::quitf(TResult result, const char *msg, ...) {
+    FMT_TO_RESULT(msg, msg, message);
+    InStream::quit(result, message.c_str());
+}
+
+#ifdef __GNUC__
+__attribute__ ((format (printf, 4, 5)))
+#endif
+void InStream::quitif(bool condition, TResult result, const char *msg, ...) {
+    if (condition) {
+        FMT_TO_RESULT(msg, msg, message);
+        InStream::quit(result, message.c_str());
+    }
+}
+
+NORETURN void InStream::quits(TResult result, std::string msg) {
+    InStream::quit(result, msg.c_str());
+}
+
+void InStream::xmlSafeWrite(std::FILE *file, const char *msg) {
+    size_t lmsg = strlen(msg);
+    for (size_t i = 0; i < lmsg; i++) {
+        if (msg[i] == '&') {
+            std::fprintf(file, "%s", "&amp;");
+            continue;
+        }
+        if (msg[i] == '<') {
+            std::fprintf(file, "%s", "&lt;");
+            continue;
+        }
+        if (msg[i] == '>') {
+            std::fprintf(file, "%s", "&gt;");
+            continue;
+        }
+        if (msg[i] == '"') {
+            std::fprintf(file, "%s", "&quot;");
+            continue;
+        }
+        if (0 <= msg[i] && msg[i] <= 31) {
+            std::fprintf(file, "%c", '.');
+            continue;
+        }
+        std::fprintf(file, "%c", msg[i]);
+    }
+}
+
+void InStream::quitscrS(WORD color, std::string msg) {
+    quitscr(color, msg.c_str());
+}
+
+void InStream::quitscr(WORD color, const char *msg) {
+    if (resultName == "") {
+        textColor(color);
+        std::fprintf(stderr, "%s", msg);
+        textColor(LightGray);
+    }
+}
+
+void InStream::reset(std::FILE *file) {
+    if (opened && stdfile)
+        quit(_fail, "Can't reset standard handle");
+
+    if (opened)
+        close();
+
+    if (!stdfile)
+        if (NULL == (file = std::fopen(name.c_str(), "rb"))) {
+            if (mode == _output)
+                quits(_pe, std::string("Output file not found: \"") + name + "\"");
+
+            if (mode == _answer)
+                quits(_fail, std::string("Answer file not found: \"") + name + "\"");
+        }
+
+    if (NULL != file) {
+        opened = true;
+
+        __testlib_set_binary(file);
+
+        if (stdfile)
+            reader = new FileInputStreamReader(file, name);
+        else
+            reader = new BufferedFileInputStreamReader(file, name);
+    } else {
+        opened = false;
+        reader = NULL;
+    }
+}
+
+void InStream::init(std::string fileName, TMode mode) {
+    opened = false;
+    name = fileName;
+    stdfile = false;
+    this->mode = mode;
+
+    std::ifstream stream;
+    stream.open(fileName.c_str(), std::ios::in);
+    if (stream.is_open()) {
+        std::streampos start = stream.tellg();
+        stream.seekg(0, std::ios::end);
+        std::streampos end = stream.tellg();
+        size_t fileSize = size_t(end - start);
+        stream.close();
+
+        // You can change maxFileSize.
+        // Example: 'inf.maxFileSize = 256 * 1024 * 1024;'.
+        if (fileSize > maxFileSize)
+            quitf(_pe, "File size exceeds %d bytes, size is %d", int(maxFileSize), int(fileSize));
+    }
+
+    reset();
+}
+
+void InStream::init(std::FILE *f, TMode mode) {
+    opened = false;
+    name = "untitled";
+    this->mode = mode;
+
+    if (f == stdin)
+        name = "stdin", stdfile = true;
+    if (f == stdout)
+        name = "stdout", stdfile = true;
+    if (f == stderr)
+        name = "stderr", stdfile = true;
+
+    reset(f);
+}
+
+char InStream::curChar() {
+    return char(reader->curChar());
+}
+
+char InStream::nextChar() {
+    return char(reader->nextChar());
+}
+
+char InStream::readChar() {
+    return nextChar();
+}
+
+char InStream::readChar(char c) {
+    lastLine = reader->getLine();
+    char found = readChar();
+    if (c != found) {
+        if (!isEoln(found))
+            quit(_pe, ("Unexpected character '" + std::string(1, found) + "', but '" + std::string(1, c) +
+                       "' expected").c_str());
+        else
+            quit(_pe, ("Unexpected character " + ("#" + vtos(int(found))) + ", but '" + std::string(1, c) +
+                       "' expected").c_str());
+    }
+    return found;
+}
+
+char InStream::readSpace() {
+    return readChar(' ');
+}
+
+void InStream::unreadChar(char c) {
+    reader->unreadChar(c);
+}
+
+void InStream::skipChar() {
+    reader->skipChar();
+}
+
+void InStream::skipBlanks() {
+    while (isBlanks(reader->curChar()))
+        reader->skipChar();
+}
+
+std::string InStream::readWord() {
+    readWordTo(_tmpReadToken);
+    return _tmpReadToken;
+}
+
+void InStream::readWordTo(std::string &result) {
+    if (!strict)
+        skipBlanks();
+
+    lastLine = reader->getLine();
+    int cur = reader->nextChar();
+
+    if (cur == EOFC)
+        quit(_unexpected_eof, "Unexpected end of file - token expected");
+
+    if (isBlanks(cur))
+        quit(_pe, "Unexpected white-space - token expected");
+
+    result.clear();
+
+    while (!(isBlanks(cur) || cur == EOFC)) {
+        result += char(cur);
+
+        // You can change maxTokenLength.
+        // Example: 'inf.maxTokenLength = 128 * 1024 * 1024;'.
+        if (result.length() > maxTokenLength)
+            quitf(_pe, "Length of token exceeds %d, token is '%s...'", int(maxTokenLength),
+                  __testlib_part(result).c_str());
+
+        cur = reader->nextChar();
+    }
+
+    reader->unreadChar(cur);
+
+    if (result.length() == 0)
+        quit(_unexpected_eof, "Unexpected end of file or white-space - token expected");
+}
+
+std::string InStream::readToken() {
+    return readWord();
+}
+
+void InStream::readTokenTo(std::string &result) {
+    readWordTo(result);
+}
+
+static std::string __testlib_part(const std::string &s) {
+    if (s.length() <= 64)
+        return s;
+    else
+        return s.substr(0, 30) + "..." + s.substr(s.length() - 31, 31);
+}
+
+#define __testlib_readMany(readMany, readOne, typeName, space)                  \
+    if (size < 0)                                                               \
+        quit(_fail, #readMany ": size should be non-negative.");                \
+    if (size > 100000000)                                                       \
+        quit(_fail, #readMany ": size should be at most 100000000.");           \
+                                                                                \
+    std::vector<typeName> result(size);                                         \
+    readManyIteration = indexBase;                                              \
+                                                                                \
+    for (int i = 0; i < size; i++)                                              \
+    {                                                                           \
+        result[i] = readOne;                                                    \
+        readManyIteration++;                                                    \
+        if (strict && space && i + 1 < size)                                              \
+            readSpace();                                                        \
+    }                                                                           \
+                                                                                \
+    readManyIteration = NO_INDEX;                                               \
+    return result;                                                              \
+
+
+std::string InStream::readWord(const pattern &p, const std::string &variableName) {
+    readWordTo(_tmpReadToken);
+    if (!p.matches(_tmpReadToken)) {
+        if (readManyIteration == NO_INDEX) {
+            if (variableName.empty())
+                quit(_wa,
+                     ("Token \"" + __testlib_part(_tmpReadToken) + "\" doesn't correspond to pattern \"" + p.src() +
+                      "\"").c_str());
+            else
+                quit(_wa, ("Token parameter [name=" + variableName + "] equals to \"" + __testlib_part(_tmpReadToken) +
+                           "\", doesn't correspond to pattern \"" + p.src() + "\"").c_str());
+        } else {
+            if (variableName.empty())
+                quit(_wa, ("Token element [index=" + vtos(readManyIteration) + "] equals to \"" +
+                           __testlib_part(_tmpReadToken) + "\" doesn't correspond to pattern \"" + p.src() +
+                           "\"").c_str());
+            else
+                quit(_wa, ("Token element " + variableName + "[" + vtos(readManyIteration) + "] equals to \"" +
+                           __testlib_part(_tmpReadToken) + "\", doesn't correspond to pattern \"" + p.src() +
+                           "\"").c_str());
+        }
+    }
+    return _tmpReadToken;
+}
+
+std::vector<std::string>
+InStream::readWords(int size, const pattern &p, const std::string &variablesName, int indexBase) {
+    __testlib_readMany(readWords, readWord(p, variablesName), std::string, true);
+}
+
+std::vector<std::string> InStream::readWords(int size, int indexBase) {
+    __testlib_readMany(readWords, readWord(), std::string, true);
+}
+
+std::string InStream::readWord(const std::string &ptrn, const std::string &variableName) {
+    return readWord(pattern(ptrn), variableName);
+}
+
+std::vector<std::string>
+InStream::readWords(int size, const std::string &ptrn, const std::string &variablesName, int indexBase) {
+    pattern p(ptrn);
+    __testlib_readMany(readWords, readWord(p, variablesName), std::string, true);
+}
+
+std::string InStream::readToken(const pattern &p, const std::string &variableName) {
+    return readWord(p, variableName);
+}
+
+std::vector<std::string>
+InStream::readTokens(int size, const pattern &p, const std::string &variablesName, int indexBase) {
+    __testlib_readMany(readTokens, readToken(p, variablesName), std::string, true);
+}
+
+std::vector<std::string> InStream::readTokens(int size, int indexBase) {
+    __testlib_readMany(readTokens, readToken(), std::string, true);
+}
+
+std::string InStream::readToken(const std::string &ptrn, const std::string &variableName) {
+    return readWord(ptrn, variableName);
+}
+
+std::vector<std::string>
+InStream::readTokens(int size, const std::string &ptrn, const std::string &variablesName, int indexBase) {
+    pattern p(ptrn);
+    __testlib_readMany(readTokens, readWord(p, variablesName), std::string, true);
+}
+
+void InStream::readWordTo(std::string &result, const pattern &p, const std::string &variableName) {
+    readWordTo(result);
+    if (!p.matches(result)) {
+        if (variableName.empty())
+            quit(_wa, ("Token \"" + __testlib_part(result) + "\" doesn't correspond to pattern \"" + p.src() +
+                       "\"").c_str());
+        else
+            quit(_wa, ("Token parameter [name=" + variableName + "] equals to \"" + __testlib_part(result) +
+                       "\", doesn't correspond to pattern \"" + p.src() + "\"").c_str());
+    }
+}
+
+void InStream::readWordTo(std::string &result, const std::string &ptrn, const std::string &variableName) {
+    return readWordTo(result, pattern(ptrn), variableName);
+}
+
+void InStream::readTokenTo(std::string &result, const pattern &p, const std::string &variableName) {
+    return readWordTo(result, p, variableName);
+}
+
+void InStream::readTokenTo(std::string &result, const std::string &ptrn, const std::string &variableName) {
+    return readWordTo(result, ptrn, variableName);
+}
+
+#ifdef __GNUC__
+__attribute__((pure))
+#endif
+static inline bool equals(long long integer, const char *s) {
+    if (integer == LLONG_MIN)
+        return strcmp(s, "-9223372036854775808") == 0;
+
+    if (integer == 0LL)
+        return strcmp(s, "0") == 0;
+
+    size_t length = strlen(s);
+
+    if (length == 0)
+        return false;
+
+    if (integer < 0 && s[0] != '-')
+        return false;
+
+    if (integer < 0)
+        s++, length--, integer = -integer;
+
+    if (length == 0)
+        return false;
+
+    while (integer > 0) {
+        int digit = int(integer % 10);
+
+        if (s[length - 1] != '0' + digit)
+            return false;
+
+        length--;
+        integer /= 10;
+    }
+
+    return length == 0;
+}
+
+#ifdef __GNUC__
+__attribute__((pure))
+#endif
+static inline bool equals(unsigned long long integer, const char *s) {
+    if (integer == ULLONG_MAX)
+        return strcmp(s, "18446744073709551615") == 0;
+
+    if (integer == 0ULL)
+        return strcmp(s, "0") == 0;
+
+    size_t length = strlen(s);
+
+    if (length == 0)
+        return false;
+
+    while (integer > 0) {
+        int digit = int(integer % 10);
+
+        if (s[length - 1] != '0' + digit)
+            return false;
+
+        length--;
+        integer /= 10;
+    }
+
+    return length == 0;
+}
+
+static inline double stringToDouble(InStream &in, const char *buffer) {
+    double retval;
+
+    size_t length = strlen(buffer);
+
+    int minusCount = 0;
+    int plusCount = 0;
+    int decimalPointCount = 0;
+    int digitCount = 0;
+    int eCount = 0;
+
+    for (size_t i = 0; i < length; i++) {
+        if (('0' <= buffer[i] && buffer[i] <= '9') || buffer[i] == '.'
+            || buffer[i] == 'e' || buffer[i] == 'E'
+            || buffer[i] == '-' || buffer[i] == '+') {
+            if ('0' <= buffer[i] && buffer[i] <= '9')
+                digitCount++;
+            if (buffer[i] == 'e' || buffer[i] == 'E')
+                eCount++;
+            if (buffer[i] == '-')
+                minusCount++;
+            if (buffer[i] == '+')
+                plusCount++;
+            if (buffer[i] == '.')
+                decimalPointCount++;
+        } else
+            in.quit(_pe, ("Expected double, but \"" + __testlib_part(buffer) + "\" found").c_str());
+    }
+
+    // If for sure is not a number in standard notation or in e-notation.
+    if (digitCount == 0 || minusCount > 2 || plusCount > 2 || decimalPointCount > 1 || eCount > 1)
+        in.quit(_pe, ("Expected double, but \"" + __testlib_part(buffer) + "\" found").c_str());
+
+    char *suffix = new char[length + 1];
+    int scanned = std::sscanf(buffer, "%lf%s", &retval, suffix);
+    bool empty = strlen(suffix) == 0;
+    delete[] suffix;
+
+    if (scanned == 1 || (scanned == 2 && empty)) {
+        if (__testlib_isNaN(retval))
+            in.quit(_pe, ("Expected double, but \"" + __testlib_part(buffer) + "\" found").c_str());
+        return retval;
+    } else
+        in.quit(_pe, ("Expected double, but \"" + __testlib_part(buffer) + "\" found").c_str());
+}
+
+static inline double
+stringToStrictDouble(InStream &in, const char *buffer, int minAfterPointDigitCount, int maxAfterPointDigitCount) {
+    if (minAfterPointDigitCount < 0)
+        in.quit(_fail, "stringToStrictDouble: minAfterPointDigitCount should be non-negative.");
+
+    if (minAfterPointDigitCount > maxAfterPointDigitCount)
+        in.quit(_fail,
+                "stringToStrictDouble: minAfterPointDigitCount should be less or equal to maxAfterPointDigitCount.");
+
+    double retval;
+
+    size_t length = strlen(buffer);
+
+    if (length == 0 || length > 1000)
+        in.quit(_pe, ("Expected strict double, but \"" + __testlib_part(buffer) + "\" found").c_str());
+
+    if (buffer[0] != '-' && (buffer[0] < '0' || buffer[0] > '9'))
+        in.quit(_pe, ("Expected strict double, but \"" + __testlib_part(buffer) + "\" found").c_str());
+
+    int pointPos = -1;
+    for (size_t i = 1; i + 1 < length; i++) {
+        if (buffer[i] == '.') {
+            if (pointPos > -1)
+                in.quit(_pe, ("Expected strict double, but \"" + __testlib_part(buffer) + "\" found").c_str());
+            pointPos = int(i);
+        }
+        if (buffer[i] != '.' && (buffer[i] < '0' || buffer[i] > '9'))
+            in.quit(_pe, ("Expected strict double, but \"" + __testlib_part(buffer) + "\" found").c_str());
+    }
+
+    if (buffer[length - 1] < '0' || buffer[length - 1] > '9')
+        in.quit(_pe, ("Expected strict double, but \"" + __testlib_part(buffer) + "\" found").c_str());
+
+    int afterDigitsCount = (pointPos == -1 ? 0 : int(length) - pointPos - 1);
+    if (afterDigitsCount < minAfterPointDigitCount || afterDigitsCount > maxAfterPointDigitCount)
+        in.quit(_pe, ("Expected strict double with number of digits after point in range ["
+                      + vtos(minAfterPointDigitCount)
+                      + ","
+                      + vtos(maxAfterPointDigitCount)
+                      + "], but \"" + __testlib_part(buffer) + "\" found").c_str()
+        );
+
+    int firstDigitPos = -1;
+    for (size_t i = 0; i < length; i++)
+        if (buffer[i] >= '0' && buffer[i] <= '9') {
+            firstDigitPos = int(i);
+            break;
+        }
+
+    if (firstDigitPos > 1 || firstDigitPos == -1)
+        in.quit(_pe, ("Expected strict double, but \"" + __testlib_part(buffer) + "\" found").c_str());
+
+    if (buffer[firstDigitPos] == '0' && firstDigitPos + 1 < int(length)
+        && buffer[firstDigitPos + 1] >= '0' && buffer[firstDigitPos + 1] <= '9')
+        in.quit(_pe, ("Expected strict double, but \"" + __testlib_part(buffer) + "\" found").c_str());
+
+    char *suffix = new char[length + 1];
+    int scanned = std::sscanf(buffer, "%lf%s", &retval, suffix);
+    bool empty = strlen(suffix) == 0;
+    delete[] suffix;
+
+    if (scanned == 1 || (scanned == 2 && empty)) {
+        if (__testlib_isNaN(retval) || __testlib_isInfinite(retval))
+            in.quit(_pe, ("Expected double, but \"" + __testlib_part(buffer) + "\" found").c_str());
+        if (buffer[0] == '-' && retval >= 0)
+            in.quit(_pe, ("Redundant minus in \"" + __testlib_part(buffer) + "\" found").c_str());
+        return retval;
+    } else
+        in.quit(_pe, ("Expected double, but \"" + __testlib_part(buffer) + "\" found").c_str());
+}
+
+static inline long long stringToLongLong(InStream &in, const char *buffer) {
+    if (strcmp(buffer, "-9223372036854775808") == 0)
+        return LLONG_MIN;
+
+    bool minus = false;
+    size_t length = strlen(buffer);
+
+    if (length > 1 && buffer[0] == '-')
+        minus = true;
+
+    if (length > 20)
+        in.quit(_pe, ("Expected integer, but \"" + __testlib_part(buffer) + "\" found").c_str());
+
+    long long retval = 0LL;
+
+    int zeroes = 0;
+    int processingZeroes = true;
+
+    for (int i = (minus ? 1 : 0); i < int(length); i++) {
+        if (buffer[i] == '0' && processingZeroes)
+            zeroes++;
+        else
+            processingZeroes = false;
+
+        if (buffer[i] < '0' || buffer[i] > '9')
+            in.quit(_pe, ("Expected integer, but \"" + __testlib_part(buffer) + "\" found").c_str());
+        retval = retval * 10 + (buffer[i] - '0');
+    }
+
+    if (retval < 0)
+        in.quit(_pe, ("Expected integer, but \"" + __testlib_part(buffer) + "\" found").c_str());
+
+    if ((zeroes > 0 && (retval != 0 || minus)) || zeroes > 1)
+        in.quit(_pe, ("Expected integer, but \"" + __testlib_part(buffer) + "\" found").c_str());
+
+    retval = (minus ? -retval : +retval);
+
+    if (length < 19)
+        return retval;
+
+    if (equals(retval, buffer))
+        return retval;
+    else
+        in.quit(_pe, ("Expected int64, but \"" + __testlib_part(buffer) + "\" found").c_str());
+}
+
+static inline unsigned long long stringToUnsignedLongLong(InStream &in, const char *buffer) {
+    size_t length = strlen(buffer);
+
+    if (length > 20)
+        in.quit(_pe, ("Expected unsigned integer, but \"" + __testlib_part(buffer) + "\" found").c_str());
+    if (length > 1 && buffer[0] == '0')
+        in.quit(_pe, ("Expected unsigned integer, but \"" + __testlib_part(buffer) + "\" found").c_str());
+
+    unsigned long long retval = 0LL;
+    for (int i = 0; i < int(length); i++) {
+        if (buffer[i] < '0' || buffer[i] > '9')
+            in.quit(_pe, ("Expected unsigned integer, but \"" + __testlib_part(buffer) + "\" found").c_str());
+        retval = retval * 10 + (buffer[i] - '0');
+    }
+
+    if (length < 19)
+        return retval;
+
+    if (length == 20 && strcmp(buffer, "18446744073709551615") == 1)
+        in.quit(_pe, ("Expected unsigned int64, but \"" + __testlib_part(buffer) + "\" found").c_str());
+
+    if (equals(retval, buffer))
+        return retval;
+    else
+        in.quit(_pe, ("Expected unsigned int64, but \"" + __testlib_part(buffer) + "\" found").c_str());
+}
+
+int InStream::readInteger() {
+    if (!strict && seekEof())
+        quit(_unexpected_eof, "Unexpected end of file - int32 expected");
+
+    readWordTo(_tmpReadToken);
+
+    long long value = stringToLongLong(*this, _tmpReadToken.c_str());
+    if (value < INT_MIN || value > INT_MAX)
+        quit(_pe, ("Expected int32, but \"" + __testlib_part(_tmpReadToken) + "\" found").c_str());
+
+    return int(value);
+}
+
+long long InStream::readLong() {
+    if (!strict && seekEof())
+        quit(_unexpected_eof, "Unexpected end of file - int64 expected");
+
+    readWordTo(_tmpReadToken);
+
+    return stringToLongLong(*this, _tmpReadToken.c_str());
+}
+
+unsigned long long InStream::readUnsignedLong() {
+    if (!strict && seekEof())
+        quit(_unexpected_eof, "Unexpected end of file - int64 expected");
+
+    readWordTo(_tmpReadToken);
+
+    return stringToUnsignedLongLong(*this, _tmpReadToken.c_str());
+}
+
+long long InStream::readLong(long long minv, long long maxv, const std::string &variableName) {
+    long long result = readLong();
+
+    if (result < minv || result > maxv) {
+        if (readManyIteration == NO_INDEX) {
+            if (variableName.empty())
+                quit(_wa, ("Integer " + vtos(result) + " violates the range [" + vtos(minv) + ", " + vtos(maxv) +
+                           "]").c_str());
+            else
+                quit(_wa, ("Integer parameter [name=" + std::string(variableName) + "] equals to " + vtos(result) +
+                           ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str());
+        } else {
+            if (variableName.empty())
+                quit(_wa, ("Integer element [index=" + vtos(readManyIteration) + "] equals to " + vtos(result) +
+                           ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str());
+            else
+                quit(_wa,
+                     ("Integer element " + std::string(variableName) + "[" + vtos(readManyIteration) + "] equals to " +
+                      vtos(result) + ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str());
+        }
+    }
+
+    if (strict && !variableName.empty())
+        validator.addBoundsHit(variableName, ValidatorBoundsHit(minv == result, maxv == result));
+
+    return result;
+}
+
+std::vector<long long>
+InStream::readLongs(int size, long long minv, long long maxv, const std::string &variablesName, int indexBase) {
+    __testlib_readMany(readLongs, readLong(minv, maxv, variablesName), long long, true)
+}
+
+std::vector<long long> InStream::readLongs(int size, int indexBase) {
+    __testlib_readMany(readLongs, readLong(), long long, true)
+}
+
+unsigned long long
+InStream::readUnsignedLong(unsigned long long minv, unsigned long long maxv, const std::string &variableName) {
+    unsigned long long result = readUnsignedLong();
+
+    if (result < minv || result > maxv) {
+        if (readManyIteration == NO_INDEX) {
+            if (variableName.empty())
+                quit(_wa,
+                     ("Unsigned integer " + vtos(result) + " violates the range [" + vtos(minv) + ", " + vtos(maxv) +
+                      "]").c_str());
+            else
+                quit(_wa,
+                     ("Unsigned integer parameter [name=" + std::string(variableName) + "] equals to " + vtos(result) +
+                      ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str());
+        } else {
+            if (variableName.empty())
+                quit(_wa,
+                     ("Unsigned integer element [index=" + vtos(readManyIteration) + "] equals to " + vtos(result) +
+                      ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str());
+            else
+                quit(_wa, ("Unsigned integer element " + std::string(variableName) + "[" + vtos(readManyIteration) +
+                           "] equals to " + vtos(result) + ", violates the range [" + vtos(minv) + ", " + vtos(maxv) +
+                           "]").c_str());
+        }
+    }
+
+    if (strict && !variableName.empty())
+        validator.addBoundsHit(variableName, ValidatorBoundsHit(minv == result, maxv == result));
+
+    return result;
+}
+
+std::vector<unsigned long long> InStream::readUnsignedLongs(int size, unsigned long long minv, unsigned long long maxv,
+                                                            const std::string &variablesName, int indexBase) {
+    __testlib_readMany(readUnsignedLongs, readUnsignedLong(minv, maxv, variablesName), unsigned long long, true)
+}
+
+std::vector<unsigned long long> InStream::readUnsignedLongs(int size, int indexBase) {
+    __testlib_readMany(readUnsignedLongs, readUnsignedLong(), unsigned long long, true)
+}
+
+unsigned long long
+InStream::readLong(unsigned long long minv, unsigned long long maxv, const std::string &variableName) {
+    return readUnsignedLong(minv, maxv, variableName);
+}
+
+int InStream::readInt() {
+    return readInteger();
+}
+
+int InStream::readInt(int minv, int maxv, const std::string &variableName) {
+    int result = readInt();
+
+    if (result < minv || result > maxv) {
+        if (readManyIteration == NO_INDEX) {
+            if (variableName.empty())
+                quit(_wa, ("Integer " + vtos(result) + " violates the range [" + vtos(minv) + ", " + vtos(maxv) +
+                           "]").c_str());
+            else
+                quit(_wa, ("Integer parameter [name=" + std::string(variableName) + "] equals to " + vtos(result) +
+                           ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str());
+        } else {
+            if (variableName.empty())
+                quit(_wa, ("Integer element [index=" + vtos(readManyIteration) + "] equals to " + vtos(result) +
+                           ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str());
+            else
+                quit(_wa,
+                     ("Integer element " + std::string(variableName) + "[" + vtos(readManyIteration) + "] equals to " +
+                      vtos(result) + ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str());
+        }
+    }
+
+    if (strict && !variableName.empty())
+        validator.addBoundsHit(variableName, ValidatorBoundsHit(minv == result, maxv == result));
+
+    return result;
+}
+
+int InStream::readInteger(int minv, int maxv, const std::string &variableName) {
+    return readInt(minv, maxv, variableName);
+}
+
+std::vector<int> InStream::readInts(int size, int minv, int maxv, const std::string &variablesName, int indexBase) {
+    __testlib_readMany(readInts, readInt(minv, maxv, variablesName), int, true)
+}
+
+std::vector<int> InStream::readInts(int size, int indexBase) {
+    __testlib_readMany(readInts, readInt(), int, true)
+}
+
+std::vector<int> InStream::readIntegers(int size, int minv, int maxv, const std::string &variablesName, int indexBase) {
+    __testlib_readMany(readIntegers, readInt(minv, maxv, variablesName), int, true)
+}
+
+std::vector<int> InStream::readIntegers(int size, int indexBase) {
+    __testlib_readMany(readIntegers, readInt(), int, true)
+}
+
+double InStream::readReal() {
+    if (!strict && seekEof())
+        quit(_unexpected_eof, "Unexpected end of file - double expected");
+
+    return stringToDouble(*this, readWord().c_str());
+}
+
+double InStream::readDouble() {
+    return readReal();
+}
+
+double InStream::readReal(double minv, double maxv, const std::string &variableName) {
+    double result = readReal();
+
+    if (result < minv || result > maxv) {
+        if (readManyIteration == NO_INDEX) {
+            if (variableName.empty())
+                quit(_wa, ("Double " + vtos(result) + " violates the range [" + vtos(minv) + ", " + vtos(maxv) +
+                           "]").c_str());
+            else
+                quit(_wa, ("Double parameter [name=" + std::string(variableName) + "] equals to " + vtos(result) +
+                           ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str());
+        } else {
+            if (variableName.empty())
+                quit(_wa, ("Double element [index=" + vtos(readManyIteration) + "] equals to " + vtos(result) +
+                           ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str());
+            else
+                quit(_wa,
+                     ("Double element " + std::string(variableName) + "[" + vtos(readManyIteration) + "] equals to " +
+                      vtos(result) + ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str());
+        }
+    }
+
+    if (strict && !variableName.empty())
+        validator.addBoundsHit(variableName, ValidatorBoundsHit(
+                doubleDelta(minv, result) < ValidatorBoundsHit::EPS,
+                doubleDelta(maxv, result) < ValidatorBoundsHit::EPS
+        ));
+
+    return result;
+}
+
+std::vector<double>
+InStream::readReals(int size, double minv, double maxv, const std::string &variablesName, int indexBase) {
+    __testlib_readMany(readReals, readReal(minv, maxv, variablesName), double, true)
+}
+
+std::vector<double> InStream::readReals(int size, int indexBase) {
+    __testlib_readMany(readReals, readReal(), double, true)
+}
+
+double InStream::readDouble(double minv, double maxv, const std::string &variableName) {
+    return readReal(minv, maxv, variableName);
+}
+
+std::vector<double>
+InStream::readDoubles(int size, double minv, double maxv, const std::string &variablesName, int indexBase) {
+    __testlib_readMany(readDoubles, readDouble(minv, maxv, variablesName), double, true)
+}
+
+std::vector<double> InStream::readDoubles(int size, int indexBase) {
+    __testlib_readMany(readDoubles, readDouble(), double, true)
+}
+
+double InStream::readStrictReal(double minv, double maxv,
+                                int minAfterPointDigitCount, int maxAfterPointDigitCount,
+                                const std::string &variableName) {
+    if (!strict && seekEof())
+        quit(_unexpected_eof, "Unexpected end of file - strict double expected");
+
+    double result = stringToStrictDouble(*this, readWord().c_str(),
+                                         minAfterPointDigitCount, maxAfterPointDigitCount);
+
+    if (result < minv || result > maxv) {
+        if (readManyIteration == NO_INDEX) {
+            if (variableName.empty())
+                quit(_wa, ("Strict double " + vtos(result) + " violates the range [" + vtos(minv) + ", " + vtos(maxv) +
+                           "]").c_str());
+            else
+                quit(_wa,
+                     ("Strict double parameter [name=" + std::string(variableName) + "] equals to " + vtos(result) +
+                      ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str());
+        } else {
+            if (variableName.empty())
+                quit(_wa, ("Strict double element [index=" + vtos(readManyIteration) + "] equals to " + vtos(result) +
+                           ", violates the range [" + vtos(minv) + ", " + vtos(maxv) + "]").c_str());
+            else
+                quit(_wa, ("Strict double element " + std::string(variableName) + "[" + vtos(readManyIteration) +
+                           "] equals to " + vtos(result) + ", violates the range [" + vtos(minv) + ", " + vtos(maxv) +
+                           "]").c_str());
+        }
+    }
+
+    if (strict && !variableName.empty())
+        validator.addBoundsHit(variableName, ValidatorBoundsHit(
+                doubleDelta(minv, result) < ValidatorBoundsHit::EPS,
+                doubleDelta(maxv, result) < ValidatorBoundsHit::EPS
+        ));
+
+    return result;
+}
+
+std::vector<double> InStream::readStrictReals(int size, double minv, double maxv,
+                                              int minAfterPointDigitCount, int maxAfterPointDigitCount,
+                                              const std::string &variablesName, int indexBase) {
+    __testlib_readMany(readStrictReals,
+                       readStrictReal(minv, maxv, minAfterPointDigitCount, maxAfterPointDigitCount, variablesName),
+                       double, true)
+}
+
+double InStream::readStrictDouble(double minv, double maxv,
+                                  int minAfterPointDigitCount, int maxAfterPointDigitCount,
+                                  const std::string &variableName) {
+    return readStrictReal(minv, maxv,
+                          minAfterPointDigitCount, maxAfterPointDigitCount,
+                          variableName);
+}
+
+std::vector<double> InStream::readStrictDoubles(int size, double minv, double maxv,
+                                                int minAfterPointDigitCount, int maxAfterPointDigitCount,
+                                                const std::string &variablesName, int indexBase) {
+    __testlib_readMany(readStrictDoubles,
+                       readStrictDouble(minv, maxv, minAfterPointDigitCount, maxAfterPointDigitCount, variablesName),
+                       double, true)
+}
+
+bool InStream::eof() {
+    if (!strict && NULL == reader)
+        return true;
+
+    return reader->eof();
+}
+
+bool InStream::seekEof() {
+    if (!strict && NULL == reader)
+        return true;
+    skipBlanks();
+    return eof();
+}
+
+bool InStream::eoln() {
+    if (!strict && NULL == reader)
+        return true;
+
+    int c = reader->nextChar();
+
+    if (!strict) {
+        if (c == EOFC)
+            return true;
+
+        if (c == CR) {
+            c = reader->nextChar();
+
+            if (c != LF) {
+                reader->unreadChar(c);
+                reader->unreadChar(CR);
+                return false;
+            } else
+                return true;
+        }
+
+        if (c == LF)
+            return true;
+
+        reader->unreadChar(c);
+        return false;
+    } else {
+        bool returnCr = false;
+
+#if (defined(ON_WINDOWS) && !defined(FOR_LINUX)) || defined(FOR_WINDOWS)
+        if (c != CR) {
+            reader->unreadChar(c);
+            return false;
+        } else {
+            if (!returnCr)
+                returnCr = true;
+            c = reader->nextChar();
+        }
+#endif
+        if (c != LF) {
+            reader->unreadChar(c);
+            if (returnCr)
+                reader->unreadChar(CR);
+            return false;
+        }
+
+        return true;
+    }
+}
+
+void InStream::readEoln() {
+    lastLine = reader->getLine();
+    if (!eoln())
+        quit(_pe, "Expected EOLN");
+}
+
+void InStream::readEof() {
+    lastLine = reader->getLine();
+    if (!eof())
+        quit(_pe, "Expected EOF");
+
+    if (TestlibFinalizeGuard::alive && this == &inf)
+        testlibFinalizeGuard.readEofCount++;
+}
+
+bool InStream::seekEoln() {
+    if (!strict && NULL == reader)
+        return true;
+
+    int cur;
+    do {
+        cur = reader->nextChar();
+    } while (cur == SPACE || cur == TAB);
+
+    reader->unreadChar(cur);
+    return eoln();
+}
+
+void InStream::nextLine() {
+    readLine();
+}
+
+void InStream::readStringTo(std::string &result) {
+    if (NULL == reader)
+        quit(_pe, "Expected line");
+
+    result.clear();
+
+    for (;;) {
+        int cur = reader->curChar();
+
+        if (cur == LF || cur == EOFC)
+            break;
+
+        if (cur == CR) {
+            cur = reader->nextChar();
+            if (reader->curChar() == LF) {
+                reader->unreadChar(cur);
+                break;
+            }
+        }
+
+        lastLine = reader->getLine();
+        result += char(reader->nextChar());
+    }
+
+    if (strict)
+        readEoln();
+    else
+        eoln();
+}
+
+std::string InStream::readString() {
+    readStringTo(_tmpReadToken);
+    return _tmpReadToken;
+}
+
+std::vector<std::string> InStream::readStrings(int size, int indexBase) {
+    __testlib_readMany(readStrings, readString(), std::string, false)
+}
+
+void InStream::readStringTo(std::string &result, const pattern &p, const std::string &variableName) {
+    readStringTo(result);
+    if (!p.matches(result)) {
+        if (readManyIteration == NO_INDEX) {
+            if (variableName.empty())
+                quit(_wa, ("Line \"" + __testlib_part(result) + "\" doesn't correspond to pattern \"" + p.src() +
+                           "\"").c_str());
+            else
+                quit(_wa, ("Line [name=" + variableName + "] equals to \"" + __testlib_part(result) +
+                           "\", doesn't correspond to pattern \"" + p.src() + "\"").c_str());
+        } else {
+            if (variableName.empty())
+                quit(_wa,
+                     ("Line element [index=" + vtos(readManyIteration) + "] equals to \"" + __testlib_part(result) +
+                      "\" doesn't correspond to pattern \"" + p.src() + "\"").c_str());
+            else
+                quit(_wa,
+                     ("Line element " + std::string(variableName) + "[" + vtos(readManyIteration) + "] equals to \"" +
+                      __testlib_part(result) + "\", doesn't correspond to pattern \"" + p.src() + "\"").c_str());
+        }
+    }
+}
+
+void InStream::readStringTo(std::string &result, const std::string &ptrn, const std::string &variableName) {
+    readStringTo(result, pattern(ptrn), variableName);
+}
+
+std::string InStream::readString(const pattern &p, const std::string &variableName) {
+    readStringTo(_tmpReadToken, p, variableName);
+    return _tmpReadToken;
+}
+
+std::vector<std::string>
+InStream::readStrings(int size, const pattern &p, const std::string &variablesName, int indexBase) {
+    __testlib_readMany(readStrings, readString(p, variablesName), std::string, false)
+}
+
+std::string InStream::readString(const std::string &ptrn, const std::string &variableName) {
+    readStringTo(_tmpReadToken, ptrn, variableName);
+    return _tmpReadToken;
+}
+
+std::vector<std::string>
+InStream::readStrings(int size, const std::string &ptrn, const std::string &variablesName, int indexBase) {
+    pattern p(ptrn);
+    __testlib_readMany(readStrings, readString(p, variablesName), std::string, false)
+}
+
+void InStream::readLineTo(std::string &result) {
+    readStringTo(result);
+}
+
+std::string InStream::readLine() {
+    return readString();
+}
+
+std::vector<std::string> InStream::readLines(int size, int indexBase) {
+    __testlib_readMany(readLines, readString(), std::string, false)
+}
+
+void InStream::readLineTo(std::string &result, const pattern &p, const std::string &variableName) {
+    readStringTo(result, p, variableName);
+}
+
+void InStream::readLineTo(std::string &result, const std::string &ptrn, const std::string &variableName) {
+    readStringTo(result, ptrn, variableName);
+}
+
+std::string InStream::readLine(const pattern &p, const std::string &variableName) {
+    return readString(p, variableName);
+}
+
+std::vector<std::string>
+InStream::readLines(int size, const pattern &p, const std::string &variablesName, int indexBase) {
+    __testlib_readMany(readLines, readString(p, variablesName), std::string, false)
+}
+
+std::string InStream::readLine(const std::string &ptrn, const std::string &variableName) {
+    return readString(ptrn, variableName);
+}
+
+std::vector<std::string>
+InStream::readLines(int size, const std::string &ptrn, const std::string &variablesName, int indexBase) {
+    pattern p(ptrn);
+    __testlib_readMany(readLines, readString(p, variablesName), std::string, false)
+}
+
+#ifdef __GNUC__
+__attribute__ ((format (printf, 3, 4)))
+#endif
+void InStream::ensuref(bool cond, const char *format, ...) {
+    if (!cond) {
+        FMT_TO_RESULT(format, format, message);
+        this->__testlib_ensure(cond, message);
+    }
+}
+
+void InStream::__testlib_ensure(bool cond, std::string message) {
+    if (!cond)
+        this->quit(_wa, message.c_str());
+}
+
+void InStream::close() {
+    if (NULL != reader) {
+        reader->close();
+        delete reader;
+        reader = NULL;
+    }
+
+    opened = false;
+}
+
+NORETURN void quit(TResult result, const std::string &msg) {
+    ouf.quit(result, msg.c_str());
+}
+
+NORETURN void quit(TResult result, const char *msg) {
+    ouf.quit(result, msg);
+}
+
+NORETURN void __testlib_quitp(double points, const char *message) {
+    __testlib_points = points;
+    std::string stringPoints = removeDoubleTrailingZeroes(format("%.10f", points));
+
+    std::string quitMessage;
+    if (NULL == message || 0 == strlen(message))
+        quitMessage = stringPoints;
+    else
+        quitMessage = stringPoints + " " + message;
+
+    quit(_points, quitMessage.c_str());
+}
+
+NORETURN void __testlib_quitp(int points, const char *message) {
+    __testlib_points = points;
+    std::string stringPoints = format("%d", points);
+
+    std::string quitMessage;
+    if (NULL == message || 0 == strlen(message))
+        quitMessage = stringPoints;
+    else
+        quitMessage = stringPoints + " " + message;
+
+    quit(_points, quitMessage.c_str());
+}
+
+NORETURN void quitp(float points, const std::string &message = "") {
+    __testlib_quitp(double(points), message.c_str());
+}
+
+NORETURN void quitp(double points, const std::string &message = "") {
+    __testlib_quitp(points, message.c_str());
+}
+
+NORETURN void quitp(long double points, const std::string &message = "") {
+    __testlib_quitp(double(points), message.c_str());
+}
+
+NORETURN void quitp(int points, const std::string &message = "") {
+    __testlib_quitp(points, message.c_str());
+}
+
+template<typename F>
+#ifdef __GNUC__
+__attribute__ ((format (printf, 2, 3)))
+#endif
+NORETURN void quitp(F points, const char *format, ...) {
+    FMT_TO_RESULT(format, format, message);
+    quitp(points, message);
+}
+
+#ifdef __GNUC__
+__attribute__ ((format (printf, 2, 3)))
+#endif
+NORETURN void quitf(TResult result, const char *format, ...) {
+    FMT_TO_RESULT(format, format, message);
+    quit(result, message);
+}
+
+#ifdef __GNUC__
+__attribute__ ((format (printf, 3, 4)))
+#endif
+void quitif(bool condition, TResult result, const char *format, ...) {
+    if (condition) {
+        FMT_TO_RESULT(format, format, message);
+        quit(result, message);
+    }
+}
+
+NORETURN void __testlib_help() {
+    InStream::textColor(InStream::LightCyan);
+    std::fprintf(stderr, "TESTLIB %s, https://github.com/MikeMirzayanov/testlib/ ", VERSION);
+    std::fprintf(stderr, "by Mike Mirzayanov, copyright(c) 2005-2020\n");
+    std::fprintf(stderr, "Checker name: \"%s\"\n", checkerName.c_str());
+    InStream::textColor(InStream::LightGray);
+
+    std::fprintf(stderr, "\n");
+    std::fprintf(stderr, "Latest features: \n");
+    for (size_t i = 0; i < sizeof(latestFeatures) / sizeof(char *); i++) {
+        std::fprintf(stderr, "*) %s\n", latestFeatures[i]);
+    }
+    std::fprintf(stderr, "\n");
+
+    std::fprintf(stderr, "Program must be run with the following arguments: \n");
+    std::fprintf(stderr, "    <input-file> <output-file> <answer-file> [<report-file> [<-appes>]]\n\n");
+
+    std::exit(FAIL_EXIT_CODE);
+}
+
+static void __testlib_ensuresPreconditions() {
+    // testlib assumes: sizeof(int) = 4.
+    __TESTLIB_STATIC_ASSERT(sizeof(int) == 4);
+
+    // testlib assumes: INT_MAX == 2147483647.
+    __TESTLIB_STATIC_ASSERT(INT_MAX == 2147483647);
+
+    // testlib assumes: sizeof(long long) = 8.
+    __TESTLIB_STATIC_ASSERT(sizeof(long long) == 8);
+
+    // testlib assumes: sizeof(double) = 8.
+    __TESTLIB_STATIC_ASSERT(sizeof(double) == 8);
+
+    // testlib assumes: no -ffast-math.
+    if (!__testlib_isNaN(+__testlib_nan()))
+        quit(_fail, "Function __testlib_isNaN is not working correctly: possible reason is '-ffast-math'");
+    if (!__testlib_isNaN(-__testlib_nan()))
+        quit(_fail, "Function __testlib_isNaN is not working correctly: possible reason is '-ffast-math'");
+}
+
+void registerGen(int argc, char *argv[], int randomGeneratorVersion) {
+    if (randomGeneratorVersion < 0 || randomGeneratorVersion > 1)
+        quitf(_fail, "Random generator version is expected to be 0 or 1.");
+    random_t::version = randomGeneratorVersion;
+
+    __testlib_ensuresPreconditions();
+
+    testlibMode = _generator;
+    __testlib_set_binary(stdin);
+    rnd.setSeed(argc, argv);
+
+#if __cplusplus > 199711L || defined(_MSC_VER)
+    prepareOpts(argc, argv);
+#endif
+}
+
+#ifdef USE_RND_AS_BEFORE_087
+void registerGen(int argc, char* argv[])
+{
+    registerGen(argc, argv, 0);
+}
+#else
+#ifdef __GNUC__
+#if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 4))
+__attribute__ ((deprecated("Use registerGen(argc, argv, 0) or registerGen(argc, argv, 1)."
+" The third parameter stands for the random generator version."
+" If you are trying to compile old generator use macro -DUSE_RND_AS_BEFORE_087 or registerGen(argc, argv, 0)."
+" Version 1 has been released on Spring, 2013. Use it to write new generators.")))
+#else
+__attribute__ ((deprecated))
+#endif
+#endif
+#ifdef _MSC_VER
+__declspec(deprecated("Use registerGen(argc, argv, 0) or registerGen(argc, argv, 1)."
+        " The third parameter stands for the random generator version."
+        " If you are trying to compile old generator use macro -DUSE_RND_AS_BEFORE_087 or registerGen(argc, argv, 0)."
+        " Version 1 has been released on Spring, 2013. Use it to write new generators."))
+#endif
+void registerGen(int argc, char *argv[]) {
+    std::fprintf(stderr, "Use registerGen(argc, argv, 0) or registerGen(argc, argv, 1)."
+                         " The third parameter stands for the random generator version."
+                         " If you are trying to compile old generator use macro -DUSE_RND_AS_BEFORE_087 or registerGen(argc, argv, 0)."
+                         " Version 1 has been released on Spring, 2013. Use it to write new generators.\n\n");
+    registerGen(argc, argv, 0);
+}
+#endif
+
+void registerInteraction(int argc, char *argv[]) {
+    __testlib_ensuresPreconditions();
+
+    testlibMode = _interactor;
+    __testlib_set_binary(stdin);
+
+    if (argc > 1 && !strcmp("--help", argv[1]))
+        __testlib_help();
+
+    if (argc < 3 || argc > 6) {
+        quit(_fail, std::string("Program must be run with the following arguments: ") +
+                    std::string("<input-file> <output-file> [<answer-file> [<report-file> [<-appes>]]]") +
+                    "\nUse \"--help\" to get help information");
+    }
+
+    if (argc <= 4) {
+        resultName = "";
+        appesMode = false;
+    }
+
+#ifndef EJUDGE
+    if (argc == 5) {
+        resultName = argv[4];
+        appesMode = false;
+    }
+
+    if (argc == 6) {
+        if (strcmp("-APPES", argv[5]) && strcmp("-appes", argv[5])) {
+            quit(_fail, std::string("Program must be run with the following arguments: ") +
+                        "<input-file> <output-file> <answer-file> [<report-file> [<-appes>]]");
+        } else {
+            resultName = argv[4];
+            appesMode = true;
+        }
+    }
+#endif
+
+    inf.init(argv[1], _input);
+
+    tout.open(argv[2], std::ios_base::out);
+    if (tout.fail() || !tout.is_open())
+        quit(_fail, std::string("Can not write to the test-output-file '") + argv[2] + std::string("'"));
+
+    ouf.init(stdin, _output);
+
+    if (argc >= 4)
+        ans.init(argv[3], _answer);
+    else
+        ans.name = "unopened answer stream";
+}
+
+void registerValidation() {
+    __testlib_ensuresPreconditions();
+
+    testlibMode = _validator;
+    __testlib_set_binary(stdin);
+
+    inf.init(stdin, _input);
+    inf.strict = true;
+}
+
+void registerValidation(int argc, char *argv[]) {
+    registerValidation();
+
+    for (int i = 1; i < argc; i++) {
+        if (!strcmp("--testset", argv[i])) {
+            if (i + 1 < argc && strlen(argv[i + 1]) > 0)
+                validator.setTestset(argv[++i]);
+            else
+                quit(_fail, std::string("Validator must be run with the following arguments: ") +
+                            "[--testset testset] [--group group] [--testOverviewLogFileName fileName]");
+        }
+        if (!strcmp("--group", argv[i])) {
+            if (i + 1 < argc)
+                validator.setGroup(argv[++i]);
+            else
+                quit(_fail, std::string("Validator must be run with the following arguments: ") +
+                            "[--testset testset] [--group group] [--testOverviewLogFileName fileName]");
+        }
+        if (!strcmp("--testOverviewLogFileName", argv[i])) {
+            if (i + 1 < argc)
+                validator.setTestOverviewLogFileName(argv[++i]);
+            else
+                quit(_fail, std::string("Validator must be run with the following arguments: ") +
+                            "[--testset testset] [--group group] [--testOverviewLogFileName fileName]");
+        }
+    }
+}
+
+void addFeature(const std::string &feature) {
+    if (testlibMode != _validator)
+        quit(_fail, "Features are supported in validators only.");
+    validator.addFeature(feature);
+}
+
+void feature(const std::string &feature) {
+    if (testlibMode != _validator)
+        quit(_fail, "Features are supported in validators only.");
+    validator.feature(feature);
+}
+
+void registerTestlibCmd(int argc, char *argv[]) {
+    __testlib_ensuresPreconditions();
+
+    testlibMode = _checker;
+    __testlib_set_binary(stdin);
+
+    if (argc > 1 && !strcmp("--help", argv[1]))
+        __testlib_help();
+
+    if (argc < 4 || argc > 6) {
+        quit(_fail, std::string("Program must be run with the following arguments: ") +
+                    std::string("<input-file> <output-file> <answer-file> [<report-file> [<-appes>]]") +
+                    "\nUse \"--help\" to get help information");
+    }
+
+    if (argc == 4) {
+        resultName = "";
+        appesMode = false;
+    }
+
+    if (argc == 5) {
+        resultName = argv[4];
+        appesMode = false;
+    }
+
+    if (argc == 6) {
+        if (strcmp("-APPES", argv[5]) && strcmp("-appes", argv[5])) {
+            quit(_fail, std::string("Program must be run with the following arguments: ") +
+                        "<input-file> <output-file> <answer-file> [<report-file> [<-appes>]]");
+        } else {
+            resultName = argv[4];
+            appesMode = true;
+        }
+    }
+
+    inf.init(argv[1], _input);
+    ouf.init(argv[2], _output);
+    ans.init(argv[3], _answer);
+}
+
+void registerTestlib(int argc, ...) {
+    if (argc < 3 || argc > 5)
+        quit(_fail, std::string("Program must be run with the following arguments: ") +
+                    "<input-file> <output-file> <answer-file> [<report-file> [<-appes>]]");
+
+    char **argv = new char *[argc + 1];
+
+    va_list ap;
+    va_start(ap, argc);
+    argv[0] = NULL;
+    for (int i = 0; i < argc; i++) {
+        argv[i + 1] = va_arg(ap, char*);
+    }
+    va_end(ap);
+
+    registerTestlibCmd(argc + 1, argv);
+    delete[] argv;
+}
+
+static inline void __testlib_ensure(bool cond, const std::string &msg) {
+    if (!cond)
+        quit(_fail, msg.c_str());
+}
+
+#ifdef __GNUC__
+__attribute__((unused))
+#endif
+static inline void __testlib_ensure(bool cond, const char *msg) {
+    if (!cond)
+        quit(_fail, msg);
+}
+
+#define ensure(cond) __testlib_ensure(cond, "Condition failed: \"" #cond "\"")
+
+#ifdef __GNUC__
+__attribute__ ((format (printf, 2, 3)))
+#endif
+inline void ensuref(bool cond, const char *format, ...) {
+    if (!cond) {
+        FMT_TO_RESULT(format, format, message);
+        __testlib_ensure(cond, message);
+    }
+}
+
+NORETURN static void __testlib_fail(const std::string &message) {
+    quitf(_fail, "%s", message.c_str());
+}
+
+#ifdef __GNUC__
+__attribute__ ((format (printf, 1, 2)))
+#endif
+void setName(const char *format, ...) {
+    FMT_TO_RESULT(format, format, name);
+    checkerName = name;
+}
+
+/* 
+ * Do not use random_shuffle, because it will produce different result
+ * for different C++ compilers.
+ *
+ * This implementation uses testlib random_t to produce random numbers, so
+ * it is stable.
+ */
+template<typename _RandomAccessIter>
+void shuffle(_RandomAccessIter __first, _RandomAccessIter __last) {
+    if (__first == __last) return;
+    for (_RandomAccessIter __i = __first + 1; __i != __last; ++__i)
+        std::iter_swap(__i, __first + rnd.next(int(__i - __first) + 1));
+}
+
+
+template<typename _RandomAccessIter>
+#if defined(__GNUC__) && !defined(__clang__)
+__attribute__ ((error("Don't use random_shuffle(), use shuffle() instead")))
+#endif
+void random_shuffle(_RandomAccessIter, _RandomAccessIter) {
+    quitf(_fail, "Don't use random_shuffle(), use shuffle() instead");
+}
+
+#ifdef __GLIBC__
+#  define RAND_THROW_STATEMENT throw()
+#else
+#  define RAND_THROW_STATEMENT
+#endif
+
+#if defined(__GNUC__) && !defined(__clang__)
+
+__attribute__ ((error("Don't use rand(), use rnd.next() instead")))
+#endif
+#ifdef _MSC_VER
+#   pragma warning( disable : 4273 )
+#endif
+int rand() RAND_THROW_STATEMENT
+{
+    quitf(_fail, "Don't use rand(), use rnd.next() instead");
+
+    /* This line never runs. */
+    //throw "Don't use rand(), use rnd.next() instead";
+}
+
+#if defined(__GNUC__) && !defined(__clang__)
+
+__attribute__ ((error("Don't use srand(), you should use "
+"'registerGen(argc, argv, 1);' to initialize generator seed "
+"by hash code of the command line params. The third parameter "
+"is randomGeneratorVersion (currently the latest is 1).")))
+#endif
+#ifdef _MSC_VER
+#   pragma warning( disable : 4273 )
+#endif
+void srand(unsigned int seed) RAND_THROW_STATEMENT
+{
+    quitf(_fail, "Don't use srand(), you should use "
+                 "'registerGen(argc, argv, 1);' to initialize generator seed "
+                 "by hash code of the command line params. The third parameter "
+                 "is randomGeneratorVersion (currently the latest is 1) [ignored seed=%d].", seed);
+}
+
+void startTest(int test) {
+    const std::string testFileName = vtos(test);
+    if (NULL == freopen(testFileName.c_str(), "wt", stdout))
+        __testlib_fail("Unable to write file '" + testFileName + "'");
+}
+
+inline std::string upperCase(std::string s) {
+    for (size_t i = 0; i < s.length(); i++)
+        if ('a' <= s[i] && s[i] <= 'z')
+            s[i] = char(s[i] - 'a' + 'A');
+    return s;
+}
+
+inline std::string lowerCase(std::string s) {
+    for (size_t i = 0; i < s.length(); i++)
+        if ('A' <= s[i] && s[i] <= 'Z')
+            s[i] = char(s[i] - 'A' + 'a');
+    return s;
+}
+
+inline std::string compress(const std::string &s) {
+    return __testlib_part(s);
+}
+
+inline std::string englishEnding(int x) {
+    x %= 100;
+    if (x / 10 == 1)
+        return "th";
+    if (x % 10 == 1)
+        return "st";
+    if (x % 10 == 2)
+        return "nd";
+    if (x % 10 == 3)
+        return "rd";
+    return "th";
+}
+
+template<typename _ForwardIterator, typename _Separator>
+std::string join(_ForwardIterator first, _ForwardIterator last, _Separator separator) {
+    std::stringstream ss;
+    bool repeated = false;
+    for (_ForwardIterator i = first; i != last; i++) {
+        if (repeated)
+            ss << separator;
+        else
+            repeated = true;
+        ss << *i;
+    }
+    return ss.str();
+}
+
+template<typename _ForwardIterator>
+std::string join(_ForwardIterator first, _ForwardIterator last) {
+    return join(first, last, ' ');
+}
+
+template<typename _Collection, typename _Separator>
+std::string join(const _Collection &collection, _Separator separator) {
+    return join(collection.begin(), collection.end(), separator);
+}
+
+template<typename _Collection>
+std::string join(const _Collection &collection) {
+    return join(collection, ' ');
+}
+
+/**
+ * Splits string s by character separator returning exactly k+1 items,
+ * where k is the number of separator occurences.
+ */
+std::vector<std::string> split(const std::string &s, char separator) {
+    std::vector<std::string> result;
+    std::string item;
+    for (size_t i = 0; i < s.length(); i++)
+        if (s[i] == separator) {
+            result.push_back(item);
+            item = "";
+        } else
+            item += s[i];
+    result.push_back(item);
+    return result;
+}
+
+/**
+ * Splits string s by character separators returning exactly k+1 items,
+ * where k is the number of separator occurences.
+ */
+std::vector<std::string> split(const std::string &s, const std::string &separators) {
+    if (separators.empty())
+        return std::vector<std::string>(1, s);
+
+    std::vector<bool> isSeparator(256);
+    for (size_t i = 0; i < separators.size(); i++)
+        isSeparator[(unsigned char) (separators[i])] = true;
+
+    std::vector<std::string> result;
+    std::string item;
+    for (size_t i = 0; i < s.length(); i++)
+        if (isSeparator[(unsigned char) (s[i])]) {
+            result.push_back(item);
+            item = "";
+        } else
+            item += s[i];
+    result.push_back(item);
+    return result;
+}
+
+/**
+ * Splits string s by character separator returning non-empty items.
+ */
+std::vector<std::string> tokenize(const std::string &s, char separator) {
+    std::vector<std::string> result;
+    std::string item;
+    for (size_t i = 0; i < s.length(); i++)
+        if (s[i] == separator) {
+            if (!item.empty())
+                result.push_back(item);
+            item = "";
+        } else
+            item += s[i];
+    if (!item.empty())
+        result.push_back(item);
+    return result;
+}
+
+/**
+ * Splits string s by character separators returning non-empty items.
+ */
+std::vector<std::string> tokenize(const std::string &s, const std::string &separators) {
+    if (separators.empty())
+        return std::vector<std::string>(1, s);
+
+    std::vector<bool> isSeparator(256);
+    for (size_t i = 0; i < separators.size(); i++)
+        isSeparator[(unsigned char) (separators[i])] = true;
+
+    std::vector<std::string> result;
+    std::string item;
+    for (size_t i = 0; i < s.length(); i++)
+        if (isSeparator[(unsigned char) (s[i])]) {
+            if (!item.empty())
+                result.push_back(item);
+            item = "";
+        } else
+            item += s[i];
+
+    if (!item.empty())
+        result.push_back(item);
+
+    return result;
+}
+
+NORETURN void __testlib_expectedButFound(TResult result, std::string expected, std::string found, const char *prepend) {
+    std::string message;
+    if (strlen(prepend) != 0)
+        message = format("%s: expected '%s', but found '%s'",
+                         compress(prepend).c_str(), compress(expected).c_str(), compress(found).c_str());
+    else
+        message = format("expected '%s', but found '%s'",
+                         compress(expected).c_str(), compress(found).c_str());
+    quit(result, message);
+}
+
+NORETURN void __testlib_expectedButFound(TResult result, double expected, double found, const char *prepend) {
+    std::string expectedString = removeDoubleTrailingZeroes(format("%.12f", expected));
+    std::string foundString = removeDoubleTrailingZeroes(format("%.12f", found));
+    __testlib_expectedButFound(result, expectedString, foundString, prepend);
+}
+
+template<typename T>
+#ifdef __GNUC__
+__attribute__ ((format (printf, 4, 5)))
+#endif
+NORETURN void expectedButFound(TResult result, T expected, T found, const char *prependFormat = "", ...) {
+    FMT_TO_RESULT(prependFormat, prependFormat, prepend);
+    std::string expectedString = vtos(expected);
+    std::string foundString = vtos(found);
+    __testlib_expectedButFound(result, expectedString, foundString, prepend.c_str());
+}
+
+template<>
+#ifdef __GNUC__
+__attribute__ ((format (printf, 4, 5)))
+#endif
+NORETURN void
+expectedButFound<std::string>(TResult result, std::string expected, std::string found, const char *prependFormat, ...) {
+    FMT_TO_RESULT(prependFormat, prependFormat, prepend);
+    __testlib_expectedButFound(result, expected, found, prepend.c_str());
+}
+
+template<>
+#ifdef __GNUC__
+__attribute__ ((format (printf, 4, 5)))
+#endif
+NORETURN void expectedButFound<double>(TResult result, double expected, double found, const char *prependFormat, ...) {
+    FMT_TO_RESULT(prependFormat, prependFormat, prepend);
+    std::string expectedString = removeDoubleTrailingZeroes(format("%.12f", expected));
+    std::string foundString = removeDoubleTrailingZeroes(format("%.12f", found));
+    __testlib_expectedButFound(result, expectedString, foundString, prepend.c_str());
+}
+
+template<>
+#ifdef __GNUC__
+__attribute__ ((format (printf, 4, 5)))
+#endif
+NORETURN void
+expectedButFound<const char *>(TResult result, const char *expected, const char *found, const char *prependFormat,
+                               ...) {
+    FMT_TO_RESULT(prependFormat, prependFormat, prepend);
+    __testlib_expectedButFound(result, std::string(expected), std::string(found), prepend.c_str());
+}
+
+template<>
+#ifdef __GNUC__
+__attribute__ ((format (printf, 4, 5)))
+#endif
+NORETURN void expectedButFound<float>(TResult result, float expected, float found, const char *prependFormat, ...) {
+    FMT_TO_RESULT(prependFormat, prependFormat, prepend);
+    __testlib_expectedButFound(result, double(expected), double(found), prepend.c_str());
+}
+
+template<>
+#ifdef __GNUC__
+__attribute__ ((format (printf, 4, 5)))
+#endif
+NORETURN void
+expectedButFound<long double>(TResult result, long double expected, long double found, const char *prependFormat, ...) {
+    FMT_TO_RESULT(prependFormat, prependFormat, prepend);
+    __testlib_expectedButFound(result, double(expected), double(found), prepend.c_str());
+}
+
+#if __cplusplus > 199711L || defined(_MSC_VER)
+template<typename T>
+struct is_iterable {
+    template<typename U>
+    static char test(typename U::iterator *x);
+
+    template<typename U>
+    static long test(U *x);
+
+    static const bool value = sizeof(test<T>(0)) == 1;
+};
+
+template<bool B, class T = void>
+struct __testlib_enable_if {
+};
+
+template<class T>
+struct __testlib_enable_if<true, T> {
+    typedef T type;
+};
+
+template<typename T>
+typename __testlib_enable_if<!is_iterable<T>::value, void>::type __testlib_print_one(const T &t) {
+    std::cout << t;
+}
+
+template<typename T>
+typename __testlib_enable_if<is_iterable<T>::value, void>::type __testlib_print_one(const T &t) {
+    bool first = true;
+    for (typename T::const_iterator i = t.begin(); i != t.end(); i++) {
+        if (first)
+            first = false;
+        else
+            std::cout << " ";
+        std::cout << *i;
+    }
+}
+
+template<>
+typename __testlib_enable_if<is_iterable<std::string>::value, void>::type
+__testlib_print_one<std::string>(const std::string &t) {
+    std::cout << t;
+}
+
+template<typename A, typename B>
+void __println_range(A begin, B end) {
+    bool first = true;
+    for (B i = B(begin); i != end; i++) {
+        if (first)
+            first = false;
+        else
+            std::cout << " ";
+        __testlib_print_one(*i);
+    }
+    std::cout << std::endl;
+}
+
+template<class T, class Enable = void>
+struct is_iterator {
+    static T makeT();
+
+    typedef void *twoptrs[2];
+
+    static twoptrs &test(...);
+
+    template<class R>
+    static typename R::iterator_category *test(R);
+
+    template<class R>
+    static void *test(R *);
+
+    static const bool value = sizeof(test(makeT())) == sizeof(void *);
+};
+
+template<class T>
+struct is_iterator<T, typename __testlib_enable_if<std::is_array<T>::value>::type> {
+    static const bool value = false;
+};
+
+template<typename A, typename B>
+typename __testlib_enable_if<!is_iterator<B>::value, void>::type println(const A &a, const B &b) {
+    __testlib_print_one(a);
+    std::cout << " ";
+    __testlib_print_one(b);
+    std::cout << std::endl;
+}
+
+template<typename A, typename B>
+typename __testlib_enable_if<is_iterator<B>::value, void>::type println(const A &a, const B &b) {
+    __println_range(a, b);
+}
+
+template<typename A>
+void println(const A *a, const A *b) {
+    __println_range(a, b);
+}
+
+template<>
+void println<char>(const char *a, const char *b) {
+    __testlib_print_one(a);
+    std::cout << " ";
+    __testlib_print_one(b);
+    std::cout << std::endl;
+}
+
+template<typename T>
+void println(const T &x) {
+    __testlib_print_one(x);
+    std::cout << std::endl;
+}
+
+template<typename A, typename B, typename C>
+void println(const A &a, const B &b, const C &c) {
+    __testlib_print_one(a);
+    std::cout << " ";
+    __testlib_print_one(b);
+    std::cout << " ";
+    __testlib_print_one(c);
+    std::cout << std::endl;
+}
+
+template<typename A, typename B, typename C, typename D>
+void println(const A &a, const B &b, const C &c, const D &d) {
+    __testlib_print_one(a);
+    std::cout << " ";
+    __testlib_print_one(b);
+    std::cout << " ";
+    __testlib_print_one(c);
+    std::cout << " ";
+    __testlib_print_one(d);
+    std::cout << std::endl;
+}
+
+template<typename A, typename B, typename C, typename D, typename E>
+void println(const A &a, const B &b, const C &c, const D &d, const E &e) {
+    __testlib_print_one(a);
+    std::cout << " ";
+    __testlib_print_one(b);
+    std::cout << " ";
+    __testlib_print_one(c);
+    std::cout << " ";
+    __testlib_print_one(d);
+    std::cout << " ";
+    __testlib_print_one(e);
+    std::cout << std::endl;
+}
+
+template<typename A, typename B, typename C, typename D, typename E, typename F>
+void println(const A &a, const B &b, const C &c, const D &d, const E &e, const F &f) {
+    __testlib_print_one(a);
+    std::cout << " ";
+    __testlib_print_one(b);
+    std::cout << " ";
+    __testlib_print_one(c);
+    std::cout << " ";
+    __testlib_print_one(d);
+    std::cout << " ";
+    __testlib_print_one(e);
+    std::cout << " ";
+    __testlib_print_one(f);
+    std::cout << std::endl;
+}
+
+template<typename A, typename B, typename C, typename D, typename E, typename F, typename G>
+void println(const A &a, const B &b, const C &c, const D &d, const E &e, const F &f, const G &g) {
+    __testlib_print_one(a);
+    std::cout << " ";
+    __testlib_print_one(b);
+    std::cout << " ";
+    __testlib_print_one(c);
+    std::cout << " ";
+    __testlib_print_one(d);
+    std::cout << " ";
+    __testlib_print_one(e);
+    std::cout << " ";
+    __testlib_print_one(f);
+    std::cout << " ";
+    __testlib_print_one(g);
+    std::cout << std::endl;
+}
+
+/* opts */
+size_t getOptType(char* s) {
+    if (!s || strlen(s) <= 1)
+        return false;
+
+    if (s[0] == '-') {
+        if (isalpha(s[1]))
+            return 1;
+        else if (s[1] == '-')
+            return isalpha(s[2]) ? 2 : 0;
+    }
+
+    return 0;
+}
+
+size_t parseOpt(size_t argc, char* argv[], size_t index, std::map<std::string, std::string>& opts) {
+    if (index >= argc)
+        return 0;
+
+    size_t type = getOptType(argv[index]), inc = 1;
+    if (type > 0) {
+        std::string key(argv[index] + type), val;
+        size_t sep = key.find('=');
+        if (sep != std::string::npos) {
+            val = key.substr(sep + 1);
+            key = key.substr(0, sep);
+        } else {
+            if (index + 1 < argc && getOptType(argv[index + 1]) == 0) {
+                val = argv[index + 1];
+                inc = 2;
+            } else {
+                if (key.length() > 1 && isdigit(key[1])) {
+                    val = key.substr(1);
+                    key = key.substr(0, 1);
+                } else {
+                    val = "true";
+                }
+            }
+        }
+        opts[key] = val;
+    } else {
+        return inc;
+    }
+
+    return inc;
+}
+
+std::vector<std::string> __testlib_argv;
+std::map<std::string, std::string> __testlib_opts;
+
+void prepareOpts(int argc, char* argv[]) {
+    if (argc <= 0)
+        __testlib_fail("Opts: expected argc>=0 but found " + toString(argc));
+    size_t n = static_cast<size_t>(argc); // NOLINT(hicpp-use-auto,modernize-use-auto)
+    __testlib_opts = std::map<std::string, std::string>();
+    for (size_t index = 1; index < n; index += parseOpt(n, argv, index, __testlib_opts));
+    __testlib_argv = std::vector<std::string>(n);
+    for (size_t index = 0; index < n; index++)
+        __testlib_argv[index] = argv[index];
+}
+
+std::string __testlib_indexToArgv(int index) {
+    if (index < 0 || index >= int(__testlib_argv.size()))
+        __testlib_fail("Opts: index '" + toString(index) + "' is out of range [0," + toString(__testlib_argv.size()) + ")");
+    return __testlib_argv[size_t(index)];
+}
+
+std::string __testlib_keyToOpts(const std::string& key) {
+    if (__testlib_opts.count(key) == 0)
+        __testlib_fail("Opts: unknown key '" + compress(key) + "'");
+    return __testlib_opts[key];
+}
+
+template<typename T>
+T optValueToIntegral(const std::string& s, bool nonnegative);
+
+long double optValueToLongDouble(const std::string& s);
+
+std::string parseExponentialOptValue(const std::string& s) {
+    size_t pos = std::string::npos;
+    for (size_t i = 0; i < s.length(); i++)
+        if (s[i] == 'e' || s[i] == 'E') {
+            if (pos >= 0)
+                __testlib_fail("Opts: expected typical exponential notation but '" + compress(s) + "' found");
+            pos = i;
+        }
+    if (pos == std::string::npos)
+        return s;
+    std::string e = s.substr(pos + 1);
+    if (!e.empty() && e[0] == '+')
+        e = e.substr(1);
+    if (e.empty())
+        __testlib_fail("Opts: expected typical exponential notation but '" + compress(s) + "' found");
+    if (e.length() > 20)
+        __testlib_fail("Opts: expected typical exponential notation but '" + compress(s) + "' found");
+    int ne = optValueToIntegral<int>(e, false);
+    std::string num = s.substr(0, pos);
+    if (num.length() > 20)
+        __testlib_fail("Opts: expected typical exponential notation but '" + compress(s) + "' found");
+    if (!num.empty() && num[0] == '+')
+        num = num.substr(1);
+    optValueToLongDouble(num);
+    bool minus = false;
+    if (num[0] == '-') {
+        minus = true;
+        num = num.substr(1);
+    }
+    for (int i = 0; i < +ne; i++) {
+        size_t sep = num.find('.');
+        if (sep == std::string::npos)
+            num += '0';
+        else {
+            if (sep + 1 == num.length())
+                num[sep] = '0';
+            else
+                std::swap(num[sep], num[sep + 1]);
+        }
+    }
+    for (int i = 0; i < -ne; i++) {
+        size_t sep = num.find('.');
+        if (sep == std::string::npos)
+            num.insert(num.begin() + int(num.length()) - 1, '.');
+        else {
+            if (sep == 0)
+                num.insert(num.begin() + 1, '0');
+            else
+                std::swap(num[sep - 1], num[sep]);
+        }
+    }
+    while (!num.empty() && num[0] == '0')
+        num = num.substr(1);
+    while (num.find('.') != std::string::npos && num.back() == '0')
+        num = num.substr(0, num.length() - 1);
+    if (!num.empty() && num.back() == '.')
+        num = num.substr(0, num.length() - 1);
+    if ((!num.empty() && num[0] == '.') || num.empty())
+        num.insert(num.begin(), '0');
+    return (minus ? "-" : "") + num;
+}
+
+template<typename T>
+T optValueToIntegral(const std::string& s_, bool nonnegative) {
+    std::string s(parseExponentialOptValue(s_));
+    if (s.empty())
+        __testlib_fail("Opts: expected integer but '" + compress(s_) + "' found");
+    T value = 0;
+    long double about = 0.0;
+    signed char sign = +1;
+    size_t pos = 0;
+    if (s[pos] == '-') {
+        if (nonnegative)
+            __testlib_fail("Opts: expected non-negative integer but '" + compress(s_) + "' found");
+        sign = -1;
+        pos++;
+    }
+    for (size_t i = pos; i < s.length(); i++) {
+        if (s[i] < '0' || s[i] > '9')
+            __testlib_fail("Opts: expected integer but '" + compress(s_) + "' found");
+        value = value * 10 + s[i] - '0';
+        about = about * 10 + s[i] - '0';
+    }
+    value *= sign;
+    about *= sign;
+    if (fabsl(value - about) > 0.1)
+        __testlib_fail("Opts: integer overflow: expected integer but '" + compress(s_) + "' found");
+    return value;
+}
+
+long double optValueToLongDouble(const std::string& s_) {
+    std::string s(parseExponentialOptValue(s_));
+    if (s.empty())
+        __testlib_fail("Opts: expected float number but '" + compress(s_) + "' found");
+    long double value = 0.0;
+    signed char sign = +1;
+    size_t pos = 0;
+    if (s[pos] == '-') {
+        sign = -1;
+        pos++;
+    }
+    bool period = false;
+    long double mul = 1.0;
+    for (size_t i = pos; i < s.length(); i++) {
+        if (s[i] == '.') {
+            if (period)
+                __testlib_fail("Opts: expected float number but '" + compress(s_) + "' found");
+            else {
+                period = true;
+                continue;
+            }
+        }
+        if (period)
+            mul *= 10.0;
+        if (s[i] < '0' || s[i] > '9')
+            __testlib_fail("Opts: expected float number but '" + compress(s_) + "' found");
+        if (period)
+            value += (s[i] - '0') / mul;
+        else
+            value = value * 10 + s[i] - '0';
+    }
+    value *= sign;
+    return value;
+}
+
+template<typename T>
+T opt(std::false_type, int index);
+
+template<>
+std::string opt(std::false_type, int index) {
+    return __testlib_indexToArgv(index);
+}
+
+template<typename T>
+T opt(std::true_type, int index) {
+    return T(optValueToLongDouble(__testlib_indexToArgv(index)));
+}
+
+template<typename T, typename U>
+T opt(std::false_type, U, int index) {
+    return opt<T>(std::is_floating_point<T>(), index);
+}
+
+template<typename T>
+T opt(std::true_type, std::false_type, int index) {
+    return optValueToIntegral<T>(__testlib_indexToArgv(index), false);
+}
+
+template<typename T>
+T opt(std::true_type, std::true_type, int index) {
+    return optValueToIntegral<T>(__testlib_indexToArgv(index), true);
+}
+
+template<>
+bool opt(std::true_type, std::true_type, int index) {
+    std::string value = __testlib_indexToArgv(index);
+    if (value == "true" || value == "1")
+        return true;
+    if (value == "false" || value == "0")
+        return false;
+    __testlib_fail("Opts: opt by index '" + toString(index) + "': expected bool true/false or 0/1 but '" + compress(value) + "' found");
+}
+
+template<typename T>
+T opt(int index) {
+    return opt<T>(std::is_integral<T>(), std::is_unsigned<T>(), index);
+}
+
+std::string opt(int index) {
+    return opt<std::string>(index);
+}
+
+template<typename T>
+T opt(std::false_type, const std::string& key);
+
+template<>
+std::string opt(std::false_type, const std::string& key) {
+    return __testlib_keyToOpts(key);
+}
+
+template<typename T>
+T opt(std::true_type, const std::string& key) {
+    return T(optValueToLongDouble(__testlib_keyToOpts(key)));
+}
+
+template<typename T, typename U>
+T opt(std::false_type, U, const std::string& key) {
+    return opt<T>(std::is_floating_point<T>(), key);
+}
+
+template<typename T>
+T opt(std::true_type, std::false_type, const std::string& key) {
+    return optValueToIntegral<T>(__testlib_keyToOpts(key), false);
+}
+
+template<typename T>
+T opt(std::true_type, std::true_type, const std::string& key) {
+    return optValueToIntegral<T>(__testlib_keyToOpts(key), true);
+}
+
+template<>
+bool opt(std::true_type, std::true_type, const std::string& key) {
+    std::string value = __testlib_keyToOpts(key);
+    if (value == "true" || value == "1")
+        return true;
+    if (value == "false" || value == "0")
+        return false;
+    __testlib_fail("Opts: key '" + compress(key) + "': expected bool true/false or 0/1 but '" + compress(value) + "' found");
+}
+
+template<typename T>
+T opt(const std::string key) {
+    return opt<T>(std::is_integral<T>(), std::is_unsigned<T>(), key);
+}
+
+std::string opt(const std::string key) {
+    return opt<std::string>(key);
+}
+#endif
+#endif

BIN
tests/lib/windows-kit-10.0.19041.0-include.7z


+ 238 - 0
tests/run.sh

@@ -0,0 +1,238 @@
+#!/bin/bash
+set -eo pipefail
+
+TESTS_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
+export TESTS_DIR="$TESTS_DIR"
+
+CYAN='\033[1;36m'
+BLUE='\033[1;34m'
+YELLOW='\033[1;33m'
+GREEN='\033[1;32m'
+NC='\033[0m'
+
+ARGS_CPP=""
+ARGS_VALID_CPP_STANDARDS=",11,14,17,20,23,"
+ARGS_CPP_STANDARDS=","
+ARGS_CPP_VERSIONS=","
+ARGS_TESTS=","
+ARGS_CPP_BITS=""
+for arg in "$@"; do
+  if [[ "$arg" == test-* ]]; then
+    ARGS_TESTS="${ARGS_TESTS}${arg},"
+    continue
+  fi
+  if [[ "$arg" == "32" || "$arg" == "64" ]]; then
+    if [[ -n "$ARGS_CPP_BITS" ]]; then
+      echo Expected at most one bits-argument: 32 or 64
+      exit 1
+    fi
+    ARGS_CPP_BITS="$arg"
+    continue
+  fi
+  if [[ "$ARGS_VALID_CPP_STANDARDS" == *,$arg,* ]]; then
+    ARGS_CPP_STANDARDS="$ARGS_CPP_STANDARDS$arg,"
+  else
+    if [[ "$arg" == v* ]]; then
+      ARGS_CPP_VERSIONS="$ARGS_CPP_VERSIONS${arg:1},"
+    else
+      if [[ -n "$ARGS_CPP" ]]; then
+        echo Expected just one non-version argument: g++, clang++ or msvc
+        exit 1
+      fi
+      if [[ ! "$arg" == "g++" && ! "$arg" == "clang++" && ! "$arg" == "msvc" ]]; then
+        echo Expected just one non-version argument: g++, clang++ or msvc, but "$arg" found
+        exit 1
+      fi
+      ARGS_CPP="$arg"
+    fi
+  fi
+done
+
+if [[ ! "$ARGS_TESTS" == "," ]]; then
+  echo [INFO] Filter tests: ${ARGS_TESTS:1:${#ARGS_TESTS}-2}
+fi
+
+if [[ ! "$ARGS_CPP" == "" ]]; then
+  echo [INFO] Filter CPP: "$ARGS_CPP"
+fi
+
+if [[ ! "$ARGS_CPP_STANDARDS" == "," ]]; then
+  echo [INFO] Filter standards: ${ARGS_CPP_STANDARDS:1:${#ARGS_CPP_STANDARDS}-2}
+fi
+
+if [[ ! "$ARGS_CPP_VERSIONS" == "," ]]; then
+  echo [INFO] Filter versions: ${ARGS_CPP_VERSIONS:1:${#ARGS_CPP_VERSIONS}-2}
+fi
+
+if [[ ! "$ARGS_CPP_BITS" == "" ]]; then
+  echo [INFO] Filter target arch.: "$ARGS_CPP_BITS"
+fi
+
+COMPILERS=('clang++' 'g++')
+if [[ ! "$ARGS_CPP" == "" ]]; then
+  if [[ "$ARGS_CPP" == "msvc" ]]; then
+    COMPILERS=()
+  else
+    COMPILERS=("$ARGS_CPP")
+  fi
+fi
+echo ""
+echo GNU-like compilers:
+printf '    %s\n' "${COMPILERS[@]}"
+
+CPP_STANDARDS=()
+MSVC_CPP_STANDARDS=()
+for v in 11 14 17 20 23; do
+  if [[ "$ARGS_CPP_STANDARDS" == "," || "$ARGS_CPP_STANDARDS" == *,$v,* ]]; then
+    CPP_STANDARDS+=("--std=c++$v")
+    MSVC_CPP_STANDARDS+=("-std:c++$v")
+  fi
+done
+echo ""
+echo Standards:
+printf '    %s\n' "${CPP_STANDARDS[@]}"
+
+uname_output="$(uname -s)"
+case "${uname_output}" in
+Linux*) machine=Linux ;;
+Darwin*) machine=Mac ;;
+CYGWIN*) machine=Windows ;;
+MINGW*) machine=Windows ;;
+MSYS*) machine=Windows ;;
+*) echo "Unknown system '${uname_output}'" && exit 1 ;;
+esac
+export MACHINE="$machine"
+
+if [[ "$machine" == "Windows" && ("$ARGS_CPP" == "" || "$ARGS_CPP" == "msvc") ]]; then
+  for f in msvc-2022-include windows-kit-10.0.19041.0-include; do
+    rm -rf "${TESTS_DIR:?}"/lib/$f && mkdir -p "$TESTS_DIR"/lib/$f
+    7z x -o"${TESTS_DIR:?}"/lib/$f "$TESTS_DIR"/lib/$f.7z
+  done
+fi
+
+run_tests() {
+  export INVOCATION_ID=$RANDOM
+  export CPP="$1"
+  export CPP_STANDARD="$2"
+
+  echo -e Running tests \(${CYAN}"$CPP"@"$CPP_STANDARD"${NC}\)
+  echo ""
+
+  for test_dir in "$TESTS_DIR"/*/; do
+    test=$(basename "$test_dir")
+    if [[ $test == test-* ]]; then
+      if [[ "$ARGS_TESTS" == "," || "$ARGS_TESTS" == *,$test,* ]]; then
+        echo -e Running "${BLUE}${test}${NC}" \("$CPP"@"$CPP_STANDARD"\)
+        export TEST_DIR="$test_dir"
+        export CPP_INCLUDE_DIR=${TESTS_DIR}/..
+        export CPP_OPTS=""
+        if [[ -n "$ARGS_CPP_BITS" ]]; then
+          export CPP_OPTS="-m$ARGS_CPP_BITS"
+        fi
+        pushd "$test_dir" 1>/dev/null 2>&1
+        bash "${test_dir}run.sh"
+        echo -e Done "${BLUE}$test${NC}" \(${CYAN}"$CPP"@"$CPP_STANDARD"${NC}\)
+        echo ""
+        popd 1>&2 1>/dev/null 2>&1
+      fi
+    fi
+  done
+
+  rm -rf "$TESTS_DIR"/tester-lcmp
+  echo -e Done all tests \(${CYAN}"$CPP"@"$CPP_STANDARD"${NC}\)
+  echo ""
+}
+
+VALGRIND=""
+valgrind_output=$(valgrind 2>&1 || true)
+if [[ $valgrind_output == *"--help"* ]]; then
+  VALGRIND="valgrind -q"
+  echo Valgrind mode is ON
+else
+  echo Valgrind mode is OFF
+fi
+export VALGRIND="$VALGRIND"
+
+done=""
+if [[ "$machine" == "Windows" && ("$ARGS_CPP" == "" || "$ARGS_CPP" == "msvc") ]]; then
+  VS_RELEASES=("Professional" "Enterprise" "Community")
+  PROGRAM_FILES=("${PROGRAMFILES}" "${PROGRAMFILES} (x86)")
+
+  for vs_release in "${VS_RELEASES[@]}"; do
+    for program_files in "${PROGRAM_FILES[@]}"; do
+      for ((version = 2000; version <= 2100; version++)); do
+        vs_dir="$program_files\\Microsoft Visual Studio\\$version\\$vs_release\\VC\\Auxiliary\\Build"
+        if [[ -d $vs_dir ]]; then
+          for bits in 32 64; do
+            vcvars_bat_file="$vs_dir\\vcvars$bits.bat"
+            if [[ -f $vcvars_bat_file ]]; then
+              echo "Compiler Visual Studio $version ($vs_release-$bits) has been found"
+              echo call \""$vcvars_bat_file"\" >do-vcvars.bat
+              echo "bash -c export > vcvars.env" >>do-vcvars.bat
+              ./do-vcvars.bat
+              source vcvars.env
+              rm -f do-vcvars.bat vcvars.env
+              for cpp_standard in "${MSVC_CPP_STANDARDS[@]}"; do
+                touch empty_file.cpp
+                cpp_output=$(cl.exe "$cpp_standard" empty_file.cpp 2>&1 || true)
+                rm -f empty_file.*
+                if [[ ! $cpp_output == *"unknown"* ]]; then
+                  echo Testing msvc-"$version"-$bits@"$cpp_standard"
+                  run_tests "cl.exe" "$cpp_standard"
+                  if [[ ! "$done" == "" ]]; then
+                    done="$done, "
+                  fi
+                  done="${done}msvc-$version-$bits@$cpp_standard"
+                fi
+              done
+            fi
+          done
+        fi
+      done
+    done
+  done
+fi
+
+for compiler in "${COMPILERS[@]}"; do
+  for version in 0 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20; do
+    if [[ "$ARGS_CPP_VERSIONS" == "," || "$ARGS_CPP_VERSIONS" == *,$version,* ]]; then
+      cpp="$compiler-$version"
+      if [[ "$version" == 0 ]]; then
+        cpp="$compiler"
+      fi
+      cpp_output=$($cpp 2>&1 || true)
+      if [[ $cpp_output == *"no input files"* ]]; then
+        echo "Compiler '$cpp' has been found"
+        for cpp_standard in "${CPP_STANDARDS[@]}"; do
+          touch empty_file.cpp
+          cpp_output=$($cpp "$cpp_standard" empty_file.cpp 2>&1 || true)
+          if [[ ! $cpp_output == *"unrecognized"* && ! $cpp_output == *"standard"* ]]; then
+            if [[ "$machine" == "Windows" && "$cpp" == "clang++" && "$cpp_standard" == "--std=c++11" ]]; then
+              echo Ignore "$cpp" "$cpp_standard" on $machine
+              continue
+            fi
+            run_tests "$cpp" "$cpp_standard"
+            if [[ ! "$done" == "" ]]; then
+              done="$done, "
+            fi
+            done="$done$cpp@$cpp_standard"
+          fi
+          rm -f empty_file.*
+        done
+      fi
+    fi
+  done
+done
+
+if [[ "$machine" == "Windows" ]]; then
+  for f in msvc-2022-include windows-kit-10.0.19041.0-include; do
+    rm -rf "${TESTS_DIR:?}"/lib/$f
+  done
+fi
+
+if [[ -z "$done" ]]; then
+  echo "[ERROR] No compilers found"
+  exit 1
+fi
+
+echo -e "${GREEN}Successfully tested on${NC}: $done\n"

+ 62 - 0
tests/scripts/compile

@@ -0,0 +1,62 @@
+#!/bin/bash
+set -eo pipefail
+
+src_file=$1
+exe_file=$(basename "${src_file%.*}")
+
+if [[ -z "$MACHINE" ]]; then
+  echo "Must provide MACHINE for compile"
+  exit 1
+fi
+
+if [[ "$MACHINE" == "Windows" ]]; then
+  exe_file=$(basename "${src_file%.*}".exe)
+fi
+
+if [[ -z "$src_file" ]]; then
+  echo "Must provide \$1 for compile"
+  exit 1
+fi
+
+if [[ -z "$CPP_INCLUDE_DIR" ]]; then
+  echo "Must provide CPP_INCLUDE_DIR in environment"
+  exit 1
+fi
+
+if [[ -z "$CPP" ]]; then
+  echo "Must provide CPP in environment"
+  exit 1
+fi
+
+rm -f "$exe_file"
+
+EXTRA_ARGS=""
+if [[ "$CPP" == "clang++" && "$MACHINE" == "Windows" ]]; then
+  msvc_version="2022"
+  wk_version="10.0.19041.0"
+  EXTRA_ARGS=" -I\"$TESTS_DIR/lib/msvc-$msvc_version-include\""
+  for s in cppwinrt shared ucrt um winrt; do
+    EXTRA_ARGS="$EXTRA_ARGS -I\"$TESTS_DIR/lib/windows-kit-$wk_version-include/$s\""
+  done
+fi
+
+if [[ "$CPP" == "cl.exe" ]]; then
+  echo "Compiling $src_file, running:" "$CPP" "$CPP_STANDARD" "-F268435456" "-EHsc" "-O2" -I"${CPP_INCLUDE_DIR}" -Fe"$exe_file" "$src_file"
+  "$CPP" "$CPP_STANDARD" "-F268435456" "-EHsc" "-O2" -I"${CPP_INCLUDE_DIR}" -Fe"$exe_file" "$src_file" &>/dev/null
+else
+  echo "Compiling $src_file, running:" "$CPP" "$CPP_OPTS" "$CPP_STANDARD" -Wpedantic -Werror -I"${CPP_INCLUDE_DIR}""$EXTRA_ARGS" -o"$exe_file" -O2 "$src_file"
+  eval "$CPP" "$CPP_OPTS" "$CPP_STANDARD" -Wpedantic -Werror -I"${CPP_INCLUDE_DIR}""$EXTRA_ARGS" -o"$exe_file" -O2 "$src_file"
+fi
+
+rm -f *.o *.obj
+
+if [ ! -f "$exe_file" ]; then
+  echo "Compilation failed: file $exe_file not found"
+  exit 1
+fi
+
+echo "[SUCCESS] $src_file compiled"
+
+if [[ "$2" == "--check-only" ]]; then
+  rm -rf "$exe_file"
+fi

+ 110 - 0
tests/scripts/test-ref

@@ -0,0 +1,110 @@
+#!/bin/bash
+set -eo pipefail
+
+# Check $TESTS_DIR is given
+if [[ -z "$TESTS_DIR" ]]; then
+  echo "Must provide \$TESTS_DIR env variable"
+  exit 1
+fi
+
+# Check $INVOCATION_ID is given: we use it as cache key to be sure compiled tester-lcmp is actual (see file $TESTS_DIR/tester-lcmp/tester-lcmp.$INVOCATION_ID)
+if [[ -z "$INVOCATION_ID" ]]; then
+  echo "Must provide \$INVOCATION_ID env variable"
+  exit 1
+fi
+
+mkdir -p "$TESTS_DIR"/tester-lcmp
+
+# If compiled tester-lcmp is not actual
+if [ ! -f "$TESTS_DIR"/tester-lcmp/tester-lcmp."$INVOCATION_ID" ]; then
+  # Clear
+  rm -f "$TESTS_DIR"/tester-lcmp/*
+
+  # Compile it
+  CPP_INCLUDE_DIR_=$CPP_INCLUDE_DIR
+  export CPP_INCLUDE_DIR=$TESTS_DIR/lib
+  bash "$TESTS_DIR"/scripts/compile "$TESTS_DIR"/src/tester-lcmp.cpp
+  export CPP_INCLUDE_DIR=$CPP_INCLUDE_DIR_
+
+  # Move from current dir to /tester-lcmp
+  if [ ! -d ./tester-lcmp ]; then
+    mv -f ./tester-lcmp "$TESTS_DIR"/tester-lcmp
+  else
+    mv -f ./tester-lcmp.exe "$TESTS_DIR"/tester-lcmp
+  fi
+
+  # Create file to inform that it was compiled to this $INVOCATION_ID
+  touch "$TESTS_DIR"/tester-lcmp/tester-lcmp."$INVOCATION_ID"
+fi
+
+# First argument as a subdirectory in ./refs
+ref_dir=$1
+if [[ -z "$ref_dir" ]]; then
+  echo "Must provide \$1 for test-ref"
+  exit 1
+fi
+
+refs=refs/$1
+
+shift 1
+
+# Check if we don't have invocation reference files
+if [ ! -d "$refs" ]; then
+  if [[ "$TEST_REF_FORBID_GEN_REFS" == "true" ]]; then
+    echo "You forgot to run push ref files for invocation: "$*""
+    echo "Run test locally, it will produce ref files and push it into the repo"
+    exit 1
+  fi
+  # Create them
+  mkdir -p "$refs"
+  echo Generating tester refs: "$*"
+  exit_code=0
+  # shellcheck disable=SC2048
+  $* 1>"$refs"/stdout 2>"$refs"/stderr || exit_code=$?
+  echo $exit_code >"$refs"/exit_code
+  echo "[SUCCESS] generated execution reference files (exit code: $exit_code)"
+else
+  # Do invocation
+  echo Testing refs: "$*"
+  exit_code=0
+  # shellcheck disable=SC2048
+  $* 1>"$refs"/stdout.aux 2>"$refs"/stderr.aux || exit_code=$?
+  echo $exit_code >"$refs"/exit_code.aux
+
+  # Check exit code is the same
+  tester_lcmp_exit_code=0
+  "$TESTS_DIR"/tester-lcmp/tester-lcmp "$TESTS_DIR"/tester-lcmp/tester-lcmp."$INVOCATION_ID" "$refs"/exit_code.aux "$refs"/exit_code 2>tester-lcmp.out || tester_lcmp_exit_code=$?
+  if [[ ! "$tester_lcmp_exit_code" == "0" ]]; then
+    echo "[FAILED] exit_code mismatched"
+    cat tester-lcmp.out
+    rm -f tester-lcmp.out
+    rm -f "$refs"/*.aux
+    exit 1
+  fi
+  echo $exit_code >"$refs"/exit_code.aux
+
+  # Check stdout is the same
+  "$TESTS_DIR"/tester-lcmp/tester-lcmp "$TESTS_DIR"/tester-lcmp/tester-lcmp."$INVOCATION_ID" "$refs"/stdout.aux "$refs"/stdout 2>tester-lcmp.out || tester_lcmp_exit_code=$?
+  if [[ ! "$tester_lcmp_exit_code" == "0" ]]; then
+    echo "[FAILED] stdout mismatched"
+    cat tester-lcmp.out
+    rm -f tester-lcmp.out
+    rm -f "$refs"/*.aux
+    exit 1
+  fi
+
+  # Check stderr is the same
+  "$TESTS_DIR"/tester-lcmp/tester-lcmp "$TESTS_DIR"/tester-lcmp/tester-lcmp."$INVOCATION_ID" "$refs"/stderr.aux "$refs"/stderr 2>tester-lcmp.out || tester_lcmp_exit_code=$?
+  if [[ ! "$tester_lcmp_exit_code" == "0" ]]; then
+    echo "[FAILED] stderr mismatched"
+    cat tester-lcmp.out
+    rm -f tester-lcmp.out
+    rm -f "$refs"/*.aux
+    exit 1
+  fi
+
+  # Invocation is the same!
+  echo "[SUCCESS] execution matched with reference (exit code: $exit_code)"
+  rm -f tester-lcmp.out
+  rm -f "$refs"/*.aux
+fi

+ 54 - 0
tests/src/tester-lcmp.cpp

@@ -0,0 +1,54 @@
+#include "testlib.h"
+#include <string>
+#include <vector>
+#include <sstream>
+
+using namespace std;
+
+bool compareWords(string a, string b)
+{
+    vector<string> va, vb;
+    stringstream sa;
+    
+    sa << a;
+    string cur;
+    while (sa >> cur)
+        va.push_back(cur);
+
+    stringstream sb;
+    sb << b;
+    while (sb >> cur)
+        vb.push_back(cur);
+
+    return (va == vb);
+}
+
+int main(int argc, char * argv[])
+{
+    setName("compare files as sequence of tokens in lines");
+    registerTestlibCmd(argc, argv);
+
+    std::string strAnswer;
+
+    int n = 0;
+    while (!ans.eof()) 
+    {
+        std::string j = ans.readString();
+
+        if (j == "" && ans.eof())
+          break;
+        
+        std::string p = ouf.readString();
+        strAnswer = p;
+
+        n++;
+
+        if (!compareWords(j, p))
+            quitf(_wa, "%d%s lines differ - expected: '%s', found: '%s'", n, englishEnding(n).c_str(), compress(j).c_str(), compress(p).c_str());
+    }
+    
+    if (n == 1)
+        quitf(_ok, "single line: '%s'", compress(strAnswer).c_str());
+    
+    quitf(_ok, "%d lines", n);
+}

+ 2 - 0
tests/t.bat

@@ -0,0 +1,2 @@
+rem Shortcut to run `run.sh g++ 11 v0 %*`
+bash "%~dp0\t.sh" %*

+ 6 - 0
tests/t.sh

@@ -0,0 +1,6 @@
+#!/bin/bash
+set -eo pipefail
+
+# Shortcut to run `run.sh g++ 11 v0 $*`
+TESTS_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
+bash $TESTS_DIR/run.sh g++ 11 v0 $*

+ 10 - 0
tests/test-000_compile-all-cpp/run.sh

@@ -0,0 +1,10 @@
+#!/bin/bash
+set -eo pipefail
+
+(IFS='
+'
+for f in $(find "$TESTS_DIR"/.. -name "*.cpp"); do
+   if [[ ! "$f" == *../tests/* ]]; then
+     bash ../scripts/compile "$f" --check-only
+   fi
+done)

+ 0 - 0
tests/test-001_run-sval/files/unix/sval/input.01


+ 1 - 0
tests/test-001_run-sval/files/unix/sval/input.02

@@ -0,0 +1 @@
+

+ 1 - 0
tests/test-001_run-sval/files/unix/sval/input.03

@@ -0,0 +1 @@
+a

+ 1 - 0
tests/test-001_run-sval/files/unix/sval/input.04

@@ -0,0 +1 @@
+abacabazwetyuiop

+ 1 - 0
tests/test-001_run-sval/files/unix/sval/input.05

@@ -0,0 +1 @@
+abacabazwetyuiopasdfghjklxcvbnmegeeekdddabacabazwetyuiopasdfghjklxcvbnmegeeekdddabacabazwetyuiopasda

+ 1 - 0
tests/test-001_run-sval/files/unix/sval/input.06

@@ -0,0 +1 @@
+asbacabazwetyuiopasdfghjklxcvbnmegeeekdddabacabazwetyuiopasdfghjklxcvbnmegeeekdddabacabazwetyuiopasda

+ 1 - 0
tests/test-001_run-sval/files/unix/sval/input.07

@@ -0,0 +1 @@
+asbacabazwetyuiopasdfghjklxcvbnmegeeekdddabacabazwetyuiopasdfghjklxcvbnmegeeekdddabacabazwetyuiopa.a

+ 1 - 0
tests/test-001_run-sval/files/unix/sval/input.08

@@ -0,0 +1 @@
+abacabazwetyuiopasdfghjklxcvbnmegeeekdddabacabazwetyuiopasdfghjklxcvbnmegeeekdddabacabazwetyuiopasda

+ 1 - 0
tests/test-001_run-sval/files/unix/sval/input.09

@@ -0,0 +1 @@
+abacabazwetyuiopasdfghjklxcvbnmegeeekdddabacabazwetyuiopasdfghjklxcvbnmegeeekdddabacabazwetyuiopasda

+ 1 - 0
tests/test-001_run-sval/files/unix/sval/input.10

@@ -0,0 +1 @@
+ abcabazwetyuiopasdfghjklxcvbnmegeeekdddabacabazwetyuiopasdfghjklxcvbnmegeeekdddabacabazwetyuiopasda

+ 0 - 0
tests/test-001_run-sval/files/win/sval/input.01


+ 1 - 0
tests/test-001_run-sval/files/win/sval/input.02

@@ -0,0 +1 @@
+

+ 1 - 0
tests/test-001_run-sval/files/win/sval/input.03

@@ -0,0 +1 @@
+a

+ 1 - 0
tests/test-001_run-sval/files/win/sval/input.04

@@ -0,0 +1 @@
+abacabazwetyuiop

+ 1 - 0
tests/test-001_run-sval/files/win/sval/input.05

@@ -0,0 +1 @@
+abacabazwetyuiopasdfghjklxcvbnmegeeekdddabacabazwetyuiopasdfghjklxcvbnmegeeekdddabacabazwetyuiopasda

+ 1 - 0
tests/test-001_run-sval/files/win/sval/input.06

@@ -0,0 +1 @@
+asbacabazwetyuiopasdfghjklxcvbnmegeeekdddabacabazwetyuiopasdfghjklxcvbnmegeeekdddabacabazwetyuiopasda

+ 1 - 0
tests/test-001_run-sval/files/win/sval/input.07

@@ -0,0 +1 @@
+asbacabazwetyuiopasdfghjklxcvbnmegeeekdddabacabazwetyuiopasdfghjklxcvbnmegeeekdddabacabazwetyuiopa.a

+ 1 - 0
tests/test-001_run-sval/files/win/sval/input.08

@@ -0,0 +1 @@
+abacabazwetyuiopasdfghjklxcvbnmegeeekdddabacabazwetyuiopasdfghjklxcvbnmegeeekdddabacabazwetyuiopasda

+ 1 - 0
tests/test-001_run-sval/files/win/sval/input.09

@@ -0,0 +1 @@
+abacabazwetyuiopasdfghjklxcvbnmegeeekdddabacabazwetyuiopasdfghjklxcvbnmegeeekdddabacabazwetyuiopasda

+ 1 - 0
tests/test-001_run-sval/files/win/sval/input.10

@@ -0,0 +1 @@
+ abcabazwetyuiopasdfghjklxcvbnmegeeekdddabacabazwetyuiopasdfghjklxcvbnmegeeekdddabacabazwetyuiopasda

+ 1 - 0
tests/test-001_run-sval/refs/r-sval-input.01/exit_code

@@ -0,0 +1 @@
+3

+ 1 - 0
tests/test-001_run-sval/refs/r-sval-input.01/stderr

@@ -0,0 +1 @@
+FAIL Unexpected end of file - token expected (stdin, line 1)

+ 0 - 0
tests/test-001_run-sval/refs/r-sval-input.01/stdout


+ 1 - 0
tests/test-001_run-sval/refs/r-sval-input.02/exit_code

@@ -0,0 +1 @@
+3

+ 1 - 0
tests/test-001_run-sval/refs/r-sval-input.02/stderr

@@ -0,0 +1 @@
+FAIL Unexpected white-space - token expected (stdin, line 1)

+ 0 - 0
tests/test-001_run-sval/refs/r-sval-input.02/stdout


+ 1 - 0
tests/test-001_run-sval/refs/r-sval-input.03/exit_code

@@ -0,0 +1 @@
+0

+ 0 - 0
tests/test-001_run-sval/refs/r-sval-input.03/stderr


+ 0 - 0
tests/test-001_run-sval/refs/r-sval-input.03/stdout


+ 1 - 0
tests/test-001_run-sval/refs/r-sval-input.04/exit_code

@@ -0,0 +1 @@
+0

+ 0 - 0
tests/test-001_run-sval/refs/r-sval-input.04/stderr


+ 0 - 0
tests/test-001_run-sval/refs/r-sval-input.04/stdout


+ 1 - 0
tests/test-001_run-sval/refs/r-sval-input.05/exit_code

@@ -0,0 +1 @@
+0

+ 0 - 0
tests/test-001_run-sval/refs/r-sval-input.05/stderr


+ 0 - 0
tests/test-001_run-sval/refs/r-sval-input.05/stdout


+ 1 - 0
tests/test-001_run-sval/refs/r-sval-input.06/exit_code

@@ -0,0 +1 @@
+3

+ 1 - 0
tests/test-001_run-sval/refs/r-sval-input.06/stderr

@@ -0,0 +1 @@
+FAIL Token parameter [name=s] equals to "asbacabazwetyuiopasdfghjklxcvb...nmegeeekdddabacabazwetyuiopasda", doesn't correspond to pattern "[a-z]{1,100}" (stdin, line 1)

+ 0 - 0
tests/test-001_run-sval/refs/r-sval-input.06/stdout


+ 1 - 0
tests/test-001_run-sval/refs/r-sval-input.07/exit_code

@@ -0,0 +1 @@
+3

+ 1 - 0
tests/test-001_run-sval/refs/r-sval-input.07/stderr

@@ -0,0 +1 @@
+FAIL Token parameter [name=s] equals to "asbacabazwetyuiopasdfghjklxcvb...bnmegeeekdddabacabazwetyuiopa.a", doesn't correspond to pattern "[a-z]{1,100}" (stdin, line 1)

+ 0 - 0
tests/test-001_run-sval/refs/r-sval-input.07/stdout


+ 1 - 0
tests/test-001_run-sval/refs/r-sval-input.08/exit_code

@@ -0,0 +1 @@
+3

+ 1 - 0
tests/test-001_run-sval/refs/r-sval-input.08/stderr

@@ -0,0 +1 @@
+FAIL Expected EOLN (stdin, line 1)

+ 0 - 0
tests/test-001_run-sval/refs/r-sval-input.08/stdout


+ 1 - 0
tests/test-001_run-sval/refs/r-sval-input.09/exit_code

@@ -0,0 +1 @@
+3

+ 1 - 0
tests/test-001_run-sval/refs/r-sval-input.09/stderr

@@ -0,0 +1 @@
+FAIL Expected EOLN (stdin, line 1)

+ 0 - 0
tests/test-001_run-sval/refs/r-sval-input.09/stdout


+ 1 - 0
tests/test-001_run-sval/refs/r-sval-input.10/exit_code

@@ -0,0 +1 @@
+3

+ 1 - 0
tests/test-001_run-sval/refs/r-sval-input.10/stderr

@@ -0,0 +1 @@
+FAIL Unexpected white-space - token expected (stdin, line 1)

+ 0 - 0
tests/test-001_run-sval/refs/r-sval-input.10/stdout


+ 15 - 0
tests/test-001_run-sval/run.sh

@@ -0,0 +1,15 @@
+#!/bin/bash
+set -eo pipefail
+
+os="unix"
+if [[ "$MACHINE" == "Windows" ]]; then
+    os="win"
+fi
+
+bash ../scripts/compile src/sval.cpp
+for i in files/"$os"/sval/*; do
+    j=$(basename $i)
+    echo "test-ref r-sval-$i "$VALGRIND" ./sval < files/$os/sval/input.$i"
+    bash ../scripts/test-ref r-sval-$j "$VALGRIND" ./sval < $i
+done
+rm -f sval sval.exe

+ 20 - 0
tests/test-001_run-sval/src/sval.cpp

@@ -0,0 +1,20 @@
+/**
+ * Validates that the input contains the only token token.
+ * This token can contain only lowercase latin letters a-z. The length should be between 1 and 100, inclusive.
+ * Also validates that file ends with EOLN and EOF.
+ */
+
+#include "testlib.h"
+
+using namespace std;
+
+int main(int argc, char* argv[])
+{
+    registerValidation(argc, argv);
+    
+    inf.readToken("[a-z]{1,100}", "s");
+    inf.readEoln();
+    inf.readEof();
+
+    return 0;
+}

+ 1 - 0
tests/test-002_run-fcmp-wcmp/files/answer.01

@@ -0,0 +1 @@
+abc 01 \%test!$

+ 1 - 0
tests/test-002_run-fcmp-wcmp/files/input.01

@@ -0,0 +1 @@
+1 2 3 4 5 6

+ 3 - 0
tests/test-002_run-fcmp-wcmp/files/output.01

@@ -0,0 +1,3 @@
+abc 01 \%test!$
+
+me

+ 1 - 0
tests/test-002_run-fcmp-wcmp/refs/r1/exit_code

@@ -0,0 +1 @@
+1

+ 1 - 0
tests/test-002_run-fcmp-wcmp/refs/r1/stderr

@@ -0,0 +1 @@
+wrong answer Participant output contains extra tokens

+ 0 - 0
tests/test-002_run-fcmp-wcmp/refs/r1/stdout


+ 1 - 0
tests/test-002_run-fcmp-wcmp/refs/r2/exit_code

@@ -0,0 +1 @@
+0

+ 1 - 0
tests/test-002_run-fcmp-wcmp/refs/r2/stderr

@@ -0,0 +1 @@
+ok 4 tokens

+ 0 - 0
tests/test-002_run-fcmp-wcmp/refs/r2/stdout


+ 1 - 0
tests/test-002_run-fcmp-wcmp/refs/r3/exit_code

@@ -0,0 +1 @@
+3

+ 1 - 0
tests/test-002_run-fcmp-wcmp/refs/r3/stderr

@@ -0,0 +1 @@
+FAIL Answer file not found: "files/answer2.01"

+ 0 - 0
tests/test-002_run-fcmp-wcmp/refs/r3/stdout


+ 1 - 0
tests/test-002_run-fcmp-wcmp/refs/r4/exit_code

@@ -0,0 +1 @@
+2

+ 1 - 0
tests/test-002_run-fcmp-wcmp/refs/r4/stderr

@@ -0,0 +1 @@
+wrong output format Extra information in the output file

+ 0 - 0
tests/test-002_run-fcmp-wcmp/refs/r4/stdout


+ 1 - 0
tests/test-002_run-fcmp-wcmp/refs/r5/exit_code

@@ -0,0 +1 @@
+0

+ 1 - 0
tests/test-002_run-fcmp-wcmp/refs/r5/stderr

@@ -0,0 +1 @@
+ok 3 lines

+ 0 - 0
tests/test-002_run-fcmp-wcmp/refs/r5/stdout


+ 1 - 0
tests/test-002_run-fcmp-wcmp/refs/r6/exit_code

@@ -0,0 +1 @@
+3

+ 1 - 0
tests/test-002_run-fcmp-wcmp/refs/r6/stderr

@@ -0,0 +1 @@
+FAIL Answer file not found: "files/answer2.01"

+ 0 - 0
tests/test-002_run-fcmp-wcmp/refs/r6/stdout


+ 14 - 0
tests/test-002_run-fcmp-wcmp/run.sh

@@ -0,0 +1,14 @@
+#!/bin/bash
+set -eo pipefail
+
+bash ../scripts/compile src/wcmp.cpp
+bash ../scripts/test-ref r1 "$VALGRIND" ./wcmp files/input.01 files/output.01 files/answer.01
+bash ../scripts/test-ref r2 "$VALGRIND" ./wcmp files/input.01 files/output.01 files/output.01
+bash ../scripts/test-ref r3 "$VALGRIND" ./wcmp files/input.01 files/output.01 files/answer2.01
+rm -f wcmp wcmp.exe
+
+bash ../scripts/compile src/fcmp.cpp
+bash ../scripts/test-ref r4 "$VALGRIND" ./fcmp files/input.01 files/output.01 files/answer.01
+bash ../scripts/test-ref r5 "$VALGRIND" ./fcmp files/input.01 files/output.01 files/output.01
+bash ../scripts/test-ref r6 "$VALGRIND" ./fcmp files/input.01 files/output.01 files/answer2.01
+rm -f fcmp fcmp.exe

+ 36 - 0
tests/test-002_run-fcmp-wcmp/src/fcmp.cpp

@@ -0,0 +1,36 @@
+#include "testlib.h"
+#include <string>
+#include <vector>
+#include <sstream>
+
+using namespace std;
+
+int main(int argc, char * argv[])
+{
+    setName("compare files as sequence of lines");
+    registerTestlibCmd(argc, argv);
+
+    std::string strAnswer;
+
+    int n = 0;
+    while (!ans.eof()) 
+    {
+        std::string j = ans.readString();
+
+        if (j == "" && ans.eof())
+          break;
+
+        strAnswer = j;
+        std::string p = ouf.readString();
+
+        n++;
+
+        if (j != p)
+            quitf(_wa, "%d%s lines differ - expected: '%s', found: '%s'", n, englishEnding(n).c_str(), compress(j).c_str(), compress(p).c_str());
+    }
+    
+    if (n == 1)
+        quitf(_ok, "single line: '%s'", compress(strAnswer).c_str());
+    
+    quitf(_ok, "%d lines", n);
+}

+ 38 - 0
tests/test-002_run-fcmp-wcmp/src/wcmp.cpp

@@ -0,0 +1,38 @@
+#include "testlib.h"
+
+using namespace std;
+
+int main(int argc, char * argv[])
+{
+    setName("compare sequences of tokens");
+    registerTestlibCmd(argc, argv);
+
+    int n = 0;
+    string j, p;
+
+    while (!ans.seekEof() && !ouf.seekEof()) 
+    {
+        n++;
+
+        ans.readWordTo(j);
+        ouf.readWordTo(p);
+        
+        if (j != p)
+            quitf(_wa, "%d%s words differ - expected: '%s', found: '%s'", n, englishEnding(n).c_str(), compress(j).c_str(), compress(p).c_str());
+    }
+
+    if (ans.seekEof() && ouf.seekEof())
+    {
+        if (n == 1)
+            quitf(_ok, "\"%s\"", compress(j).c_str());
+        else
+            quitf(_ok, "%d tokens", n);
+    }
+    else
+    {
+        if (ans.seekEof())
+            quitf(_wa, "Participant output contains extra tokens");
+        else
+            quitf(_wa, "Unexpected EOF in the participants output");
+    }
+}

+ 1 - 0
tests/test-003_run-rnd/refs/r1/exit_code

@@ -0,0 +1 @@
+0

+ 0 - 0
tests/test-003_run-rnd/refs/r1/stderr


+ 31 - 0
tests/test-003_run-rnd/refs/r1/stdout

@@ -0,0 +1,31 @@
+10
+8 10
+4 5
+2 7
+9 1
+8 7
+9 6
+10 5
+6 4
+3 2
+vutwaahqooeqoxzxwetlpecqiwgdbogiqqulttysyohwhzxzphvsfmnplizxoebzcvvfyppqbhxjksuzepuezqqzxlfmdanoeaoq
+3.0177
+71836424476711
+7548514365797
+763523
+467326
+961
+9 3 1 7 0 4 6 8 5 2
+
+
+9 2 6 10 1 4 3 5 8 7
+81 19 73 27 64 82 68 28 67 88
+
+10 6 13 5 5 11 18 4 22 6
+
+238
+844
+933
+980
+96952
+99843

+ 1 - 0
tests/test-003_run-rnd/refs/r2/exit_code

@@ -0,0 +1 @@
+0

+ 0 - 0
tests/test-003_run-rnd/refs/r2/stderr


+ 121 - 0
tests/test-003_run-rnd/refs/r2/stdout

@@ -0,0 +1,121 @@
+100
+7 33
+27 40
+97 43
+12 95
+89 36
+50 19
+34 14
+13 58
+94 78
+90 96
+91 51
+54 57
+69 86
+18 9
+20 88
+76 43
+100 85
+93 55
+35 42
+23 88
+54 44
+46 23
+17 62
+77 47
+73 25
+27 79
+79 30
+92 11
+77 61
+34 83
+21 76
+80 83
+67 40
+72 29
+8 22
+32 63
+20 75
+66 92
+55 74
+35 49
+91 21
+26 96
+10 24
+24 97
+39 33
+42 45
+70 53
+37 52
+49 86
+36 78
+85 61
+63 64
+87 5
+90 4
+5 60
+72 84
+62 100
+51 50
+74 48
+19 94
+81 98
+65 2
+1 16
+75 8
+32 6
+64 30
+29 59
+15 11
+80 93
+58 28
+87 95
+71 10
+57 31
+38 39
+71 68
+65 4
+89 9
+3 41
+18 82
+46 84
+45 3
+52 99
+16 98
+37 25
+14 38
+17 67
+69 47
+70 68
+13 48
+81 56
+59 7
+22 44
+60 15
+73 2
+6 31
+99 12
+28 53
+82 26
+41 56
+clizwkchataumicxkohcrpqnyrjyzbjvsypznpembvkkkbyzvzckcmhbjbuopfbwbkntswhwsdfzabjghjkjftogziikipdkajdm
+2.5026
+48691624799250
+6036792742366
+594248
+269230
+1156
+7 3 5 2 6 9 0 1 8 4
+
+
+3 7 5 1 2 10 6 4 8 9
+36 28 78 94 72 40 23 95 82 19
+
+5 22 4 31 3 11 4 9 7 4
+
+896
+650
+989
+860
+99644
+98400

+ 7 - 0
tests/test-003_run-rnd/run.sh

@@ -0,0 +1,7 @@
+#!/bin/bash
+set -eo pipefail
+
+bash ../scripts/compile src/gen.cpp
+bash ../scripts/test-ref r1 $VALGRIND ./gen 10 100
+bash ../scripts/test-ref r2 $VALGRIND ./gen 100 1000
+rm -f gen gen.exe

+ 104 - 0
tests/test-003_run-rnd/src/gen.cpp

@@ -0,0 +1,104 @@
+#include "testlib.h"
+
+#include <iostream>
+#include <sstream>
+#include <fstream>
+#include <iomanip>
+#include <string>
+#include <cstdlib>
+#include <cstdio>
+#include <cstring>
+#include <cmath>
+#include <ctime>
+#include <climits>
+#include <cassert>
+#include <vector>
+#include <queue>
+#include <stack>
+#include <deque>
+#include <set>
+#include <map>
+#include <bitset>
+#include <utility>
+#include <algorithm>
+
+#define forn(i, n) for (int i = 0; i < int(n); i++)
+
+using namespace std;
+
+int main(int argc, char* argv[])
+{
+    registerGen(argc, argv, 1);
+
+    int n = opt<int>(1);
+    int t = opt<int>(2);
+
+    vector<int> p(n);
+    forn(i, n)
+        if (i > 0)
+            p[i] = rnd.wnext(i, t);
+
+    println(n);
+    vector<int> perm(n);
+    forn(i, n)
+        perm[i] = i;
+    shuffle(perm.begin() + 1, perm.end());
+    vector<pair<int,int> > edges;
+
+    for (int i = 1; i < n; i++)
+        if (rnd.next(2))
+            edges.push_back(make_pair(perm[i], perm[p[i]]));
+        else
+            edges.push_back(make_pair(perm[p[i]], perm[i]));
+
+    shuffle(edges.begin(), edges.end());
+
+    for (int i = 0; i + 1 < n; i++)
+        println(edges[i].first + 1, edges[i].second + 1);
+
+    println(rnd.next("[a-z]{100}"));
+    printf("%.04f\n", rnd.next(3.1415));
+
+    {
+        long long LL = 12345678901234LL;
+        long long LR = 82345678901234LL;
+        println(rnd.next(LL, LR));
+    }
+
+    {
+        unsigned long long LL = 1345678901234ULL;
+        unsigned long long LR = 8345678901234ULL;
+        println(rnd.next(LL, LR));
+    }
+
+    {
+        unsigned long LL = 134567UL;
+        unsigned long LR = 834567UL;
+        println(rnd.next(LL, LR));
+    }
+
+    {
+        int LL = 134567;
+        int LR = 834567;
+        println(rnd.next(LL, LR));
+    }
+
+    vector<int> a;
+    forn(i, 100)
+        a.push_back(i * i);
+    println(rnd.any(a));
+    println(rnd.perm(10));
+    println(rnd.perm(0));
+    println(rnd.perm(0, 1));
+    println(rnd.perm(10, 1));
+    println(rnd.distinct(10, 15, 100));
+    println(rnd.distinct(0, 15, 100));
+    println(rnd.partition(10, 100, 3));
+    println(rnd.partition(0, 0, 10));
+    println(rnd.wnext(1000, 2));
+    println(rnd.wnext(1000LL, 2LL));
+    println(rnd.wnext(1000, 20));
+    println(rnd.wnext(1000LL, 20LL));
+    println(rnd.wnext(100000, 200));
+    println(rnd.wnext(100000LL, 200LL));
+}

+ 1 - 0
tests/test-004_use-test.h/refs/r1/exit_code

@@ -0,0 +1 @@
+0

Some files were not shown because too many files changed in this diff