Checkers

Which library. A new checker is written with eolymp.h, as on this page. If the problemalready has a PROGRAM checker, read it first with get_checker: one that includestestlib.h or calls registerTestlibCmd is changed in testlib — followWrite a checker with testlib.h — and so is a new one therequester explicitly asked to be written with testlib. For an interactive problem the checkerfollows its interactor: see Write an interactor. The rule, andwhy, is in eolymp.h reference.

1. What a checker is

A program that decides what a contestant's output is worth on one test: accepted, a wronganswer, a partial score, or a jury error — the problem is broken, not the contestant. It seesthree files:

stream

is

trust

c.input

the test input

trusted — the validator already vetted it

c.output

the contestant's output

hostile — arbitrary bytes, assume nothing

c.jury

the jury's answer

verify it too — see §4

Write a PROGRAM checker only when correctness genuinely needs logic — several validanswers, a constructed object to verify, or partial credit. Reach for a built-in first:

  • LINES — byte-for-byte line comparison. Trailing spaces and tabs on each line, andempty lines at the end of the file, are ignored; everything else must match exactly.

  • TOKENS — compares word by word or number by number. Numbers are compared with arelative tolerance; words case-insensitively unless case_sensitive is set. Itfails with a system failure on a single token over 64 KB.

  • QUERY_RESULTS — for per-query output.

A statement promising "any case is accepted" is only true under TOKENS withcase_sensitive off, or a checker that folds case.


2. The skeleton

#include <eolymp.h>

int main(int argc, char** argv) {
    eo::checker c(argc, argv);
    long long expected = c.jury.read_long(eo::any, "sum");
    long long found = c.output.read_long(eo::any, "sum");
    if (found != expected) eo::wrong("the sum is {}, not {}", expected, found);
    eo::accept("the sum is {}", found);
}
  • eo::checker c(argc, argv) finds the three files, reads the test's cost and takes over thelog.

  • A read that fails on c.output is a wrong answer; on c.jury or c.input it is a juryerror. You never choose — the stream decides.

  • After a verdict that accepts or scores, anything but whitespace left in the output is awrong answer, "extra output after the answer". c.output.trailing(eo::ignore) allows itwhen the task does.

  • Every path must end in a verdict. Returning from main without one is a jury error.

That shape is enough when the answer is one value. For anything composite, use §4.

Runtime: store it as cpp:20-gnu14, type PROGRAM, with no files entry — theheader is in the judge's runtime. The judge gives it 10 s of wall time.


3. Verdicts and scores

call

exit

result

eo::accept("…", args)

0

ACCEPTED, the test's full cost

eo::wrong("…", args)

1

WRONG_ANSWER

eo::score(f, "…", args)

7

PARTIALLY_CORRECT, f × cost points; f = 1 is an accept

eo::points(p, "…", args)

as eo::score

as eo::score(p / cost)

eo::jury_error("…", args)

3

the problem is broken — the run fails as a system error

c.output.wrong("…", args)

1

a wrong answer blamed on that stream (see §4)

Messages use {} placeholders. Name the position, the expected value and the found value —eo::wrong("position {}: expected {}, found {}", i, want, got) — because the message is theonly thing a human sees when a verdict is disputed.

jury_error is not a contestant verdict

It means the problem is broken: a checker bug, an impossible jury answer, a contestant whofound something better than the jury. Never use it for anything a contestant can trigger, andnever return a wrong answer when the jury's answer is impossible — that hides a brokenproblem behind contestant failures.

A scored zero and a wrong answer are different runs

eo::score(0) is PARTIALLY_CORRECT with 0 points: a well-formed answer whose formula came outat nothing. eo::wrong is WRONG_ANSWER: the output is not a valid answer at all. They pay thesame and read differently, so choose by what happened, not by the points.


4. Read the jury's answer and the contestant's with one function

The single most important structural rule for a non-trivial checker: write one functionthat reads an answer, verifies it, and returns its value, and run it on both answers.

#include <eolymp.h>

int main(int argc, char** argv) {
    eo::checker c(argc, argv);
    int n = c.input.read_int(1, 100000, "n");
    auto chosen = [n](eo::answer_stream& a) {
        int k = a.read_int(0, n, "k");
        std::vector<int> items = a.read_ints(k, 1, n, "item");
        if (!eo::all_distinct(items)) a.wrong("an item is chosen twice");
        return k;
    };
    c.answers(eo::many);
    auto [by_the_jury, found] = c.read_both(chosen);
    c.optimum(by_the_jury, found, eo::maximize);
}

c.read_both(reader) runs the reader on c.jury first, then on c.output, and returnsboth results. Inside it, a failed read, a.wrong(…) and a bare eo::wrong(…) are all blamedon the stream being read: a jury error during the first call, a wrong answer during the second.So the three rules that matter hold by construction — the jury is read first, the jury's answeris verified too, and a broken jury answer is a jury error rather than every contestant's wronganswer.

c.optimum(by_the_jury, found, eo::minimize) (or eo::maximize) then ends the program:

Outcome

Verdict

equal

accept

the contestant is worse

wrong answer the answer is 17; the optimum is 12

the contestant is better

a jury error: the contestant's 9 beats the jury's 12

For optimisation problems always check both that the answer is feasible and that itsvalue matches — the reader verifies the first, optimum the second.

c.answers(eo::many) declares that several answers are correct. It is a declaration, not acheck: the library then warns (EO212) if the checker only compares with the jury's answer.

YES / NO with a certificate

#include <eolymp.h>

int main(int argc, char** argv) {
    eo::checker c(argc, argv);
    int n = c.input.read_int(1, 100000, "n");
    c.yes_no([n](eo::answer_stream& a) {
        std::vector<int> p = a.read_ints(n, 1, n, "p");
        if (!eo::is_permutation(p)) a.wrong("p is not a permutation");
    });
}

yes_no compares the first word of each side, ignoring case, checks the certificate after aYES on both sides, and makes a contestant's valid YES against the jury's NO a juryerror. c.yes_no(certificate, "POSSIBLE", "IMPOSSIBLE") takes other words.


5. Never trust c.output

A contestant who prints 2147483647 where a count is expected makes an unbounded checkersize a vector to match, or loop for ever — and a crashing or hanging checker is a systemerror, not a wrong answer. Bound every read from the output by something derived from theinput:

int k = c.output.read_int(0, n, "k");
int at = c.output.read_int(1, n, "position");

A bounded read rejects the value on the spot, as a wrong answer that names it:output.txt, line 1, k: 5000 is above 100. Reading a number from c.output or c.jurywithout bounds compiles with warning EO103.

All three streams are lenient about whitespace — any spaces, tabs and line breaksseparate two values — because a contestant's formatting is not the task. read_line is theone read that looks at lines; at_eof() and at_eoln() look ahead.


6. Problems with many valid answers

Most checkers of such problems never need the jury's answer: "output any valid colouring","any shortest path" — the checker recomputes validity from the input. Write the verifier as areader anyway, so the day the problem gains an optimality criterion you add read_both ratherthan restructure. If only the contestant's answer is read, say so to the library:

c.jury.skip_rest("the answer file holds one valid colouring; any valid one is accepted");

Without it, an answer file left unread gets warning EO203 — usually a sign the checker and theanswer files disagree about the format.


7. Partial scoring

eo::score(eo::ratio(good, total), "{} of {} pairs are correct", good, total);

eo::score takes a fraction of the test — never points, never a percentage. It readsTEST_COST itself: on a test worth 40, eo::score(0.5) pays 20; on one worth 7 it pays 3.5.Subtask weights stay in the testset configuration, and the checker never hard-codes 100.

  • eo::ratio(a, b) is exact, so eo::ratio(n, n) is exactly 1 — an accept, not a hairbelow it.

  • Rounding. When the statement rounds the score, round the same way, or a documentedfull score can be unreachable: eo::score(q, eo::round_to(0), "D = {}", d).

  • When scoring depends on the subtask, key it off c.group() — the testset index —never off the cost: two subtasks can be worth the same.

The testset decides whether a partial score pays(Design a testset):

  • A partially scored subtask is scored WORST (the minimum), with the subtask's full valueon every test.

  • ALL pays nothing for a partial score — only accepted runs count.

  • A partial score on a test worth 0 is recorded ACCEPTED, because its points equal itscost. So a testset that puts its whole value on the first test and 0 on the rest pays infull for a partially correct solution. Never pair a scoring checker with that layout.

Verify the scoring on the judge, with an attached solution that should earn a partial score:a checker is not proved correct by one subtask paying out.


8. Floating-point answers

#include <eolymp.h>

int main(int argc, char** argv) {
    eo::checker c(argc, argv);
    double expected = c.jury.read_real(eo::any, "area");
    double found = c.output.read_real(-1e18, 1e18, "area");
    if (!eo::close_enough(expected, found, 1e-6))
        eo::wrong("the area is {}, not {}", expected, found);
    eo::accept();
}

eo::close_enough(expected, found, eps) accepts an absolute or relative error withineps, which is what statements mean; a hand-rolled fabs(a - b) < eps fails at largemagnitudes. For a whole output of numbers, c.reals(1e-6) compares token by token and endsthe program. The output's real numbers may carry an exponent, since many languages print smallvalues that way; c.output.reals(eo::plain) refuses it when the statement fixes the format.


9. Ready-made comparisons

Each gives the verdict and ends the program:

Call

Accepts when

c.tokens()

exactly the answer's tokens, in order; letter case matters

c.lines()

the answer's lines, ignoring trailing spaces and tabs

c.reals(eps)

token by token, numbers within eps absolute or relative

c.yes_no(certificate)

§4

Use the built-in TOKENS / LINES types when they suffice; these exist for a checker thatalso does something else, and c.tokens() has no 64 KB token limit.


10. Debug output is safe

The library writes the verdict line first, whatever the checker printed before it, sostd::cout, printf and eo::log("n = {}", n) cannot break the judge's parse of the score.The log is kept per run.


11. Review checklist

BLOCKER breaks judging · MAJOR hides defects · MINOR style.

Structure

  • [ ] BLOCKER eo::checker c(argc, argv), #include <eolymp.h> first, runtime cpp:20-gnu14, type PROGRAM

  • [ ] MAJOR a built-in type (TOKENS / LINES / QUERY_RESULTS) would not have sufficed

  • [ ] BLOCKER every path ends in a verdict

  • [ ] BLOCKER composite answer ⇒ one reader run on both answers through c.read_both

  • [ ] MAJOR trailing output policy deliberate (trailing(eo::ignore) only when the task allows it)

Trusting the contestant

  • [ ] BLOCKER every read from c.output producing a count, index or length is bounded by the input

  • [ ] BLOCKER no container sized, and no array indexed, from an unchecked value

  • [ ] MAJOR every read names its value; no warnings left unexplained (the codes)

Verdict semantics

  • [ ] BLOCKER eo::jury_error only for jury-side impossibilities

  • [ ] BLOCKER a contestant beating the jury is a jury error (c.optimum does this)

  • [ ] MAJOR feasibility and optimality both checked, for optimisation problems

  • [ ] MAJOR messages name position, expected and found

Scoring (if partial)

  • [ ] BLOCKER eo::score with a fraction, eo::ratio for "a out of b" — no hard-coded points

  • [ ] BLOCKER the scored testsets are WORST with the value on every test, not ALL, and not the value on test 1 only

  • [ ] MAJOR full marks, partial and zero all reachable, and verified on the judge