Browse Source

Compile checker on user side to be OS compatible.

Marcelo Fornet 6 years ago
parent
commit
6772d80801

+ 2 - 2
notes/similar-projects.md

@@ -3,5 +3,5 @@
 * [CHelper](https://github.com/EgorKulikov/idea-chelper/)
 * [JHelper](https://github.com/AlexeyDmitriev/JHelper)
 * [Caide](https://github.com/slycelote/caide)
-* [Ineffable](Search)
-* [cgy4ever]
+* [Ineffable](https://codeforces.com/blog/entry/19083)
+* [Hightail](https://codeforces.com/blog/entry/13141)

+ 45 - 2
src/core.ts

@@ -108,6 +108,48 @@ function createFolder(path: string){
     }
 }
 
+function globalAtticPath(){
+    let path: string | undefined = vscode.workspace.getConfiguration('acmx.configuration', null).get('solutionPath');
+
+    return join(path!, ATTIC);
+}
+
+/**
+ * Create default environment that let acmX run properly
+ */
+export function initAcmX(){
+    // Create global attic.
+    let globalAttic = globalAtticPath();
+    createFolder(globalAttic);
+
+    // Create checker folder
+    let checkerFolder = join(globalAttic, 'checkers');
+    createFolder(checkerFolder);
+
+    // Copy testlibe
+    let testlib = 'testlib.h';
+    if (!existsSync(join(checkerFolder, testlib))){
+        copyFileSync(join(SRC, 'static', 'checkers', testlib),
+                     join(checkerFolder, testlib));
+    }
+
+    // Create wcmp checker
+    let checkerName = 'wcmp.cpp';
+    if (!existsSync(join(checkerFolder, checkerName))){
+        copyFileSync(join(SRC, 'static', 'checkers', checkerName),
+                     join(checkerFolder, checkerName));
+    }
+
+    // Compile checker
+    let compiledName = 'wcmp.exe';
+    if (!existsSync(join(checkerFolder, compiledName))){
+        let checkerPath = join(checkerFolder, checkerName);
+        let compiledPath = join(checkerFolder, compiledName);
+
+        child_process.spawnSync("g++", ["-std=c++11", `${checkerPath}`, "-o", `${compiledPath}`]);
+    }
+}
+
 export function newArena(path: string){
     createFolder(path);
 
@@ -124,7 +166,6 @@ export function newArena(path: string){
     }
 
     copyFileSync(templatePath!, join(path, solFile()));
-    copyFileSync(join(SRC, 'static', 'checker'), join(path, ATTIC, 'checker'));
 }
 
 export function removeExtension(name: string){
@@ -279,7 +320,9 @@ export function timedRun(path: string, tcName: string, timeout: number){
     writeSync(currentFd, xresult.stdout);
     closeSync(currentFd);
 
-    let checker_result = child_process.spawnSync(join(path, ATTIC, 'checker'), [tcInput, tcCurrent, tcOutput]);
+    let checker = join(globalAtticPath(), 'checkers', 'wcmp.exe');
+
+    let checker_result = child_process.spawnSync(checker, [tcInput, tcCurrent, tcOutput]);
 
     if (checker_result.status !== 0){
         return new TestcaseResult(Veredict.WA);

+ 3 - 1
src/extension.ts

@@ -3,7 +3,7 @@ import * as vscode from 'vscode';
 import { existsSync, writeFileSync, readdirSync } from 'fs';
 import { join, extname } from 'path';
 import { SITES } from './conn';
-import { newContestFromId, testSolution, veredictName, stressSolution, upgradeArena, newProblemFromId, removeExtension, solFile } from './core';
+import { newContestFromId, testSolution, veredictName, stressSolution, upgradeArena, newProblemFromId, removeExtension, solFile, initAcmX } from './core';
 import { Veredict, SiteDescription } from './types';
 import { currentProblem, compileCode, ATTIC } from './core';
 import { getSite } from "./conn";
@@ -272,6 +272,8 @@ async function debugTest(){
 // this method is called when your extension is activated
 // your extension is activated the very first time the command is executed
 export function activate(context: vscode.ExtensionContext) {
+    initAcmX();
+
     let addProblemCommand = vscode.commands.registerCommand('acmx.addProblem', addProblem);
     let addContestCommand = vscode.commands.registerCommand('acmx.addContest', addContest);
     let runSolutionCommand = vscode.commands.registerCommand('acmx.runSolution', runSolution);

BIN
src/static/checker


+ 19 - 0
src/static/checkers/acmp.cpp

@@ -0,0 +1,19 @@
+#include "testlib.h"
+#include <stdio.h>
+#include <math.h>
+
+const double EPS = 1.5E-6;
+
+int main(int argc, char * argv[])
+{
+    setName("compare two doubles, maximal absolute error = %.10f", EPS);
+    registerTestlibCmd(argc, argv);
+    
+    double ja = ans.readDouble();
+    double pa = ouf.readDouble();
+
+    if (fabs(ja - pa) > EPS + 1E-15)
+        quitf(_wa, "expected %.10f, found %.10f", ja, pa);
+    
+    quitf(_ok, "answer is %.10f", ja);
+}

+ 95 - 0
src/static/checkers/caseicmp.cpp

@@ -0,0 +1,95 @@
+/**
+ * Checker to compare output and answer in the form:
+ *
+ * Case 1: <number>
+ * Case 2: <number>
+ * ...
+ * Case n: <number>
+ *
+ */
+
+#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>
+
+using namespace std;
+
+#define forn(i, n) for (int i = 0; i < int(n); i++)
+
+vector<long long> readStream(InStream& in, TResult pe)
+{
+    vector<long long> result;
+
+    for (int testCase = 1; !in.seekEof(); testCase++)
+    {
+        string caseStr = in.readToken();
+        if (caseStr != "Case")
+            quitf(pe, "Expected 'Case' but found '%s' [test case %d]", compress(caseStr).c_str(), testCase);
+
+        string numExpStr;
+        stringstream ss;
+        ss << testCase;
+        ss >> numExpStr;
+        numExpStr += ":";
+        string numStr = in.readToken();
+        if (numExpStr != numStr)
+            quitf(pe, "Expected '%s' but found '%s' [test case %d]", compress(numExpStr).c_str(), compress(numStr).c_str(), testCase);
+
+        result.push_back(in.readLong());
+    }
+
+    return result;
+}
+
+int main(int argc, char* argv[])
+{
+    setName("Single int64 checker with testcase-support");
+    registerTestlibCmd(argc, argv);
+
+    vector<long long> ja = readStream(ans, _fail);
+    vector<long long> pa = readStream(ouf, _pe);
+
+    forn(i, min(ja.size(), pa.size()))
+        if (ja[i] != pa[i])
+            quitf(_wa, "Expected %s found %s [test case %d]", vtos(ja[i]).c_str(), vtos(pa[i]).c_str(), i + 1);
+
+    if (ja.size() != pa.size())
+        quitf(_pe, "Expected %u test case(s) but found %u", (unsigned int)(ja.size()), (unsigned int)(pa.size()));
+
+    string message = format("%u case(s):", (unsigned int)(ja.size()));
+    if (ja.size() <= 5)
+    {
+        forn(i, ja.size())
+            message += " " + vtos(ja[i]);
+    }
+    else
+    {
+        forn(i, 3)
+            message += " " + vtos(ja[i]);
+        message += " ...";
+        forn(i, 2)
+            message += " " + vtos(ja[ja.size() - 2 + i]);
+    }
+
+    quitf(_ok, "%s", message.c_str());
+}

+ 124 - 0
src/static/checkers/casencmp.cpp

@@ -0,0 +1,124 @@
+/**
+ * Checker to compare output and answer in the form:
+ *
+ * Case 1: <number> <number> <number> ... <number>
+ * Case 2: <number> <number> <number> ... <number>
+ * ...
+ * Case n: <number> <number> <number> ... <number>
+ *
+ */
+
+#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>
+
+using namespace std;
+
+#define forn(i, n) for (int i = 0; i < int(n); i++)
+
+string token;
+
+vector<long long> readStreamCase(InStream& in, TResult pe, int testCase, bool& prereadCase)
+{
+    if (!prereadCase)
+    {
+        string caseStr = in.readToken();
+        if (caseStr != "Case")
+            quitf(pe, "Expected 'Case' but found '%s' [test case %d]", compress(caseStr).c_str(), testCase);
+    }
+
+    string numExpStr;
+    stringstream ss;
+    ss << testCase;
+    ss >> numExpStr;
+    numExpStr += ":";
+    string numStr = in.readToken();
+    if (numExpStr != numStr)
+        quitf(pe, "Expected '%s' but found '%s' [test case %d]", compress(numExpStr).c_str(), compress(numStr).c_str(), testCase);
+
+    vector<long long> result;
+    while (!in.seekEof())
+    {
+        in.readTokenTo(token);
+        if (token == "Case")
+        {
+            prereadCase = true;
+            break;
+        }
+
+        result.push_back(stringToLongLong(in, token.c_str()));
+    }
+
+    return result;
+}
+
+string longLongsToString(const vector<long long>& a)
+{
+    if (a.empty())
+        return "\"\" [size=0]";
+    
+    string elems;
+    if (a.size() <= 5)
+    {
+        forn(i, a.size())
+            elems += vtos(a[i]) + " ";
+    }
+    else
+    {
+        forn(i, 3)
+            elems += vtos(a[i]) + " ";
+        elems += "... ";
+        forn(i, 2)
+            elems += vtos(a[a.size() - 2 + i]) + " ";
+    }
+
+    return format("\"%s\" [size=%u]", trim(elems).c_str(), (unsigned int)(a.size()));
+}
+
+int main(int argc, char* argv[])
+{
+    setName("Many int64s checker with testcase-support");
+    registerTestlibCmd(argc, argv);
+
+    int testCase = 0;
+
+    bool ansPrereadCase = false;
+    bool oufPrereadCase = false;
+
+    while (!ans.seekEof())
+    {
+        testCase++;
+
+        vector<long long> ja = readStreamCase(ans, _fail, testCase, ansPrereadCase);
+        vector<long long> pa = readStreamCase(ouf, _pe, testCase, oufPrereadCase);
+
+        if (ja != pa)
+        {
+            string js = longLongsToString(ja);
+            string ps = longLongsToString(pa);
+            quitf(_wa, "Sequences differ: jury has %s, but participant has %s [test case %d]", js.c_str(), ps.c_str(), testCase);
+        }
+    }
+
+    quitf(_ok, "%d test cases(s)", testCase);
+}

+ 113 - 0
src/static/checkers/casewcmp.cpp

@@ -0,0 +1,113 @@
+/**
+ * Checker to compare output and answer in the form:
+ *
+ * Case 1: <token> <token> ... <token>
+ * Case 2: <token> <token> ... <token>
+ * ...
+ * Case n: <token> <token> ... <token>
+ *
+ */
+
+#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>
+
+using namespace std;
+
+#define forn(i, n) for (int i = 0; i < int(n); i++)
+
+string token;
+
+vector<string> readStreamCase(InStream& in, TResult pe, int testCase, bool& prereadCase)
+{
+    if (!prereadCase)
+    {
+        string caseStr = in.readToken();
+        if (caseStr != "Case")
+            quitf(pe, "Expected 'Case' but found '%s' [test case %d]", compress(caseStr).c_str(), testCase);
+    }
+
+    string numExpStr;
+    stringstream ss;
+    ss << testCase;
+    ss >> numExpStr;
+    numExpStr += ":";
+    string numStr = in.readToken();
+    if (numExpStr != numStr)
+        quitf(pe, "Expected '%s' but found '%s' [test case %d]", compress(numExpStr).c_str(), compress(numStr).c_str(), testCase);
+
+    vector<string> result;
+    while (!in.seekEof())
+    {
+        in.readTokenTo(token);
+        if (token == "Case")
+        {
+            prereadCase = true;
+            break;
+        }
+
+        result.push_back(token);
+    }
+
+    return result;
+}
+
+string stringsToString(const vector<string>& a)
+{
+    if (a.empty())
+        return "\"\" [size=0]";
+    
+    string elems;
+    forn(i, a.size())
+        elems += a[i] + " ";
+
+    return format("\"%s\" [size=%u]", compress(trim(elems)).c_str(), (unsigned int)(a.size()));
+}
+
+int main(int argc, char* argv[])
+{
+    setName("Tokens checker with testcase-support");
+    registerTestlibCmd(argc, argv);
+
+    int testCase = 0;
+
+    bool ansPrereadCase = false;
+    bool oufPrereadCase = false;
+
+    while (!ans.seekEof())
+    {
+        testCase++;
+
+        vector<string> ja = readStreamCase(ans, _fail, testCase, ansPrereadCase);
+        vector<string> pa = readStreamCase(ouf, _pe, testCase, oufPrereadCase);
+
+        if (ja != pa)
+        {
+            string js = stringsToString(ja);
+            string ps = stringsToString(pa);
+            quitf(_wa, "Sequences differ: jury has %s, but participant has %s [test case %d]", js.c_str(), ps.c_str(), testCase);
+        }
+    }
+
+    quitf(_ok, "%d test cases(s)", testCase);
+}

+ 19 - 0
src/static/checkers/dcmp.cpp

@@ -0,0 +1,19 @@
+#include "testlib.h"
+#include <stdio.h>
+#include <math.h>
+
+const double EPS = 1E-6;
+
+int main(int argc, char * argv[])
+{
+    setName("compare two doubles, maximal absolute or relative error = %.10f", EPS);
+    registerTestlibCmd(argc, argv);
+    
+    double ja = ans.readDouble();
+    double pa = ouf.readDouble();
+    
+    if (!doubleCompare(ja, pa, EPS))
+        quitf(_wa, "expected %.10f, found %.10f", ja, pa);
+    
+    quitf(_ok, "answer is %.10f", ja);
+}

+ 36 - 0
src/static/checkers/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);
+}

+ 35 - 0
src/static/checkers/hcmp.cpp

@@ -0,0 +1,35 @@
+#include "testlib.h"
+
+#include <string>
+
+using namespace std;
+
+pattern pnum("0|-?[1-9][0-9]*");
+
+bool isNumeric(const string& p)
+{
+    return pnum.matches(p);
+}
+
+int main(int argc, char * argv[])
+{
+    setName("compare two signed huge integers");
+    registerTestlibCmd(argc, argv);
+    
+    string ja = ans.readWord();
+    string pa = ouf.readWord();
+
+    if (!isNumeric(ja))
+        quitf(_fail, "%s is not a valid integer", compress(ja).c_str());
+
+    if (!ans.seekEof())
+        quitf(_fail, "expected exactly one token in the answer file");
+    
+    if (!isNumeric(pa))
+        quitf(_pe, "%s is not a valid integer", compress(pa).c_str());
+
+    if (ja != pa)
+        quitf(_wa, "expected '%s', found '%s'", compress(ja).c_str(), compress(pa).c_str());
+    
+    quitf(_ok, "answer is '%s'", compress(ja).c_str());
+}

+ 16 - 0
src/static/checkers/icmp.cpp

@@ -0,0 +1,16 @@
+#include "testlib.h"
+#include <stdio.h>
+
+int main(int argc, char * argv[])
+{
+    setName("compare two signed int%d's", 8 * int(sizeof(int)));
+    registerTestlibCmd(argc, argv);
+    
+    int ja = ans.readInt();
+    int pa = ouf.readInt();
+    
+    if (ja != pa)
+        quitf(_wa, "expected %d, found %d", ja, pa);
+    
+    quitf(_ok, "answer is %d", ja);
+}

+ 54 - 0
src/static/checkers/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);
+}

+ 57 - 0
src/static/checkers/ncmp.cpp

@@ -0,0 +1,57 @@
+#include "testlib.h"
+#include <sstream>
+
+using namespace std;
+
+int main(int argc, char * argv[])
+{
+    setName("compare ordered sequences of signed int%d numbers", 8 * int(sizeof(long long)));
+
+    registerTestlibCmd(argc, argv);
+
+    int n = 0;
+    string firstElems;
+
+    while (!ans.seekEof() && !ouf.seekEof())
+    {
+        n++;
+        long long j = ans.readLong();
+        long long p = ouf.readLong();
+        if (j != p)
+            quitf(_wa, "%d%s numbers differ - expected: '%s', found: '%s'", n, englishEnding(n).c_str(), vtos(j).c_str(), vtos(p).c_str());
+        else
+            if (n <= 5)
+            {
+                if (firstElems.length() > 0)
+                    firstElems += " ";
+                firstElems += vtos(j);
+            }
+    }
+
+    int extraInAnsCount = 0;
+
+    while (!ans.seekEof())
+    {
+        ans.readLong();
+        extraInAnsCount++;
+    }
+    
+    int extraInOufCount = 0;
+
+    while (!ouf.seekEof())
+    {
+        ouf.readLong();
+        extraInOufCount++;
+    }
+
+    if (extraInAnsCount > 0)
+        quitf(_wa, "Answer contains longer sequence [length = %d], but output contains %d elements", n + extraInAnsCount, n);
+    
+    if (extraInOufCount > 0)
+        quitf(_wa, "Output contains longer sequence [length = %d], but answer contains %d elements", n + extraInOufCount, n);
+    
+    if (n <= 5)
+        quitf(_ok, "%d number(s): \"%s\"", n, compress(firstElems).c_str());
+    else
+        quitf(_ok, "%d numbers", n);
+}

+ 14 - 0
src/static/checkers/pointscmp.cpp

@@ -0,0 +1,14 @@
+#include "testlib.h"
+
+using namespace std;
+
+int main(int argc, char * argv[])
+{
+    setName("example of scored checker");
+    registerTestlibCmd(argc, argv);
+
+    double ja = ans.readDouble();
+    double pa = ouf.readDouble();
+
+    quitp(fabs(ja - pa), "ja=%.4f pa=%.4f", ja, pa);
+}

+ 19 - 0
src/static/checkers/rcmp.cpp

@@ -0,0 +1,19 @@
+#include "testlib.h"
+#include <stdio.h>
+#include <math.h>
+
+const double EPS = 1.5E-6;
+
+int main(int argc, char * argv[])
+{
+    setName("compare two doubles, maximal absolute error = %.10f", EPS);
+    registerTestlibCmd(argc, argv);
+    
+    double ja = ans.readDouble();
+    double pa = ouf.readDouble();
+
+    if (fabs(ja - pa) > EPS + 1E-15)
+        quitf(_wa, "expected %.10f, found %.10f", ja, pa);
+    
+    quitf(_ok, "answer is %.10f", ja);
+}

+ 32 - 0
src/static/checkers/rcmp4.cpp

@@ -0,0 +1,32 @@
+#include "testlib.h"
+#include <cmath>
+
+using namespace std;
+
+const double EPS = 1E-4;
+
+int main(int argc, char * argv[])
+{
+    setName("compare two sequences of doubles, max absolute or relative error = %.5f", EPS);
+    registerTestlibCmd(argc, argv);
+
+    int n = 0;
+    double j = 0, p = 0;
+
+    while (!ans.seekEof()) 
+    {
+        n++;
+        j = ans.readDouble();
+        p = ouf.readDouble();
+        if (!doubleCompare(j, p, EPS))
+        {
+            quitf(_wa, "%d%s numbers differ - expected: '%.5f', found: '%.5f', error = '%.5f'",
+                n, englishEnding(n).c_str(), j, p, doubleDelta(j, p));
+        }
+    }
+
+    if (n == 1)
+        quitf(_ok, "found '%.5f', expected '%.5f', error '%.5f'", p, j, doubleDelta(j, p));
+    
+    quitf(_ok, "%d numbers", n);
+}

+ 32 - 0
src/static/checkers/rcmp6.cpp

@@ -0,0 +1,32 @@
+#include "testlib.h"
+#include <cmath>
+
+using namespace std;
+
+const double EPS = 1E-6;
+
+int main(int argc, char * argv[])
+{
+    setName("compare two sequences of doubles, max absolute or relative  error = %.7f", EPS);
+    registerTestlibCmd(argc, argv);
+
+    int n = 0;
+    double j = 0, p = 0;
+
+    while (!ans.seekEof()) 
+    {
+        n++;
+        j = ans.readDouble();
+        p = ouf.readDouble();
+        if (!doubleCompare(j, p, EPS))
+        {
+            quitf(_wa, "%d%s numbers differ - expected: '%.7f', found: '%.7f', error = '%.7f'",
+                n, englishEnding(n).c_str(), j, p, doubleDelta(j, p));
+        }
+    }
+
+    if (n == 1)
+        quitf(_ok, "found '%.7f', expected '%.7f', error '%.7f'", p, j, doubleDelta(j, p));
+
+    quitf(_ok, "%d numbers", n);
+}

+ 32 - 0
src/static/checkers/rcmp9.cpp

@@ -0,0 +1,32 @@
+#include "testlib.h"
+#include <cmath>
+
+using namespace std;
+
+const double EPS = 1E-9;
+
+int main(int argc, char * argv[])
+{
+    setName("compare two sequences of doubles, max absolute or relative error = %.10f", EPS);
+    registerTestlibCmd(argc, argv);
+
+    int n = 0;
+    double j = 0, p = 0;
+
+    while (!ans.seekEof()) 
+    {
+        n++;
+        j = ans.readDouble();
+        p = ouf.readDouble();
+        if (!doubleCompare(j, p, EPS))
+        {
+            quitf(_wa, "%d%s numbers differ - expected: '%.10f', found: '%.10f', error = '%.10f'",
+                n, englishEnding(n).c_str(), j, p, doubleDelta(j, p));
+        }
+    }
+
+    if (n == 1)
+        quitf(_ok, "found '%.10f', expected '%.10f', error '%.10f'", p, j, doubleDelta(j, p));
+
+    quitf(_ok, "%d numbers", n);
+}

+ 24 - 0
src/static/checkers/rncmp.cpp

@@ -0,0 +1,24 @@
+#include "testlib.h"
+#include <cmath>
+
+using namespace std;
+
+const double EPS = 1.5E-5;
+
+int main(int argc, char * argv[])
+{
+    setName("compare two sequences of doubles, maximal absolute error = %.10f", EPS);
+    registerTestlibCmd(argc, argv);
+
+    int n = 0;
+    while (!ans.seekEof()) 
+    {
+        n++;
+        double j = ans.readDouble();
+        double p = ouf.readDouble();
+        if (fabs(j - p) > EPS + 1E-15)
+            quitf(_wa, "%d%s numbers differ - expected: '%.10f', found: '%.10f'", n, englishEnding(n).c_str(), j, p);
+    }
+
+    quitf(_ok, "%d numbers", n);
+}

File diff suppressed because it is too large
+ 4681 - 0
src/static/checkers/testlib.h


+ 52 - 0
src/static/checkers/uncmp.cpp

@@ -0,0 +1,52 @@
+#include "testlib.h"
+#include <vector>
+
+using namespace std;
+
+int main(int argc, char * argv[])
+{
+    setName("compare unordered sequences of signed int%lu numbers", 8 * sizeof(long long));
+
+    registerTestlibCmd(argc, argv);
+
+    vector<long long> ja, pa;
+
+    while (!ans.seekEof())
+        ja.push_back(ans.readLong());
+
+    while (!ouf.seekEof())
+        pa.push_back(ouf.readLong());
+
+    if (ja.size() != pa.size())
+        quitf(_wa, "Expected %u elements, but %u found", (unsigned int)(ja.size()), (unsigned int)(pa.size()));
+
+    sort(ja.begin(), ja.end());
+    sort(pa.begin(), pa.end());
+
+    if (ja != pa)
+        quitf(_wa, "Expected sequence and output are different (as unordered sequences) [size=%u]", (unsigned int)(ja.size()));
+
+    string message;
+    
+    if (ja.size() != 1)
+        if (ja.empty())
+            message = "empty sequence";
+        else
+            message = vtos(ja.size()) + " numbers (in increasing order):";
+    else
+        message = vtos(ja.size()) + " number:";
+    
+    if (ja.size() <= 5)
+        for (int i = 0; i < min(int(ja.size()), 5); i++)
+            message += " " + vtos(ja[i]);
+    else
+    {
+        for (int i = 0; i < 2; i++)
+            message += " " + vtos(ja[i]);
+        message += " ...";
+        for (int i = 0; i < 2; i++)
+            message += " " + vtos(ja[ja.size() - 2 + i]);
+    }
+        
+    quitf(_ok, "%s", message.c_str());
+}

+ 38 - 0
src/static/checkers/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");
+    }
+}

+ 27 - 0
src/static/checkers/yesno.cpp

@@ -0,0 +1,27 @@
+#include "testlib.h"
+#include <string>
+
+using namespace std;
+
+const string YES = "YES";
+const string NO = "NO";
+
+int main(int argc, char * argv[])
+{
+    setName("%s", (YES + " or " + NO + " (case insensetive)").c_str());
+    registerTestlibCmd(argc, argv);
+
+    std::string ja = upperCase(ans.readWord());
+    std::string pa = upperCase(ouf.readWord());
+
+    if (ja != YES && ja != NO)
+        quitf(_fail, "%s or %s expected in answer, but %s found", YES.c_str(), NO.c_str(), compress(ja).c_str());
+
+    if (pa != YES && pa != NO)
+        quitf(_pe, "%s or %s expected, but %s found", YES.c_str(), NO.c_str(), compress(pa).c_str());
+
+    if (ja != pa)
+        quitf(_wa, "expected %s, found %s", compress(ja).c_str(), compress(pa).c_str());
+
+    quitf(_ok, "answer is %s", ja.c_str());
+}

src/static/sol.cpp → src/static/template.cpp