// checker.cpp — scoring for "Ябълки" (apples)
// Usage: ./checker <input> <answer> <contestant_code>
//   (argument order matches the judge: input, jury answer, contestant submission.)
//
// The contestant submits a Python program that must output a+b WITHOUT using the
// +/- operators or any built-in summation. We enforce this by parsing the
// submission's AST (a substring blacklist is trivially bypassable, e.g.
// eval assembled from bytes, or `import operator; operator.add`) and, only if it
// is clean, executing it on the test input and comparing the output.
//
// The checker writes NO files (the sandbox is unreliable with filesystem writes):
// the validator is passed inline with `python3 -c`, reading the submission (a
// read, not a write), and the test input is piped in with printf.
//
// Note: no static check can catch every re-encoding of addition in a
// Turing-complete language (e.g. len([*[0]*a, *[0]*b])); this enforces the
// literal rule (+, -, built-in sum) and closes the dynamic-eval escape hatches.
//
// The validator parses the submission as RAW BYTES, never as decoded text. That
// is load-bearing: PEP 263 coding declarations are applied by CPython only when
// compiling bytes, so ast.parse() on a str silently ignores them and can see a
// completely different program from the one that will run. See validatorSrc.
//
// Prints a single 0 or 1 on stdout; diagnostics on stderr.

#include <iostream>
#include <fstream>
#include <string>
#include <array>
#include <memory>
#include <cctype>
#include <cstdio>
#include <cstdlib>
using namespace std;

static string trim(const string &s) {
    size_t a = 0, b = s.size();
    while (a < b && isspace((unsigned char)s[a])) a++;
    while (b > a && isspace((unsigned char)s[b - 1])) b--;
    return s.substr(a, b - a);
}

static string runCapture(const string &cmd) {
    array<char, 4096> buffer;
    string result;
    unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
    if (!pipe) return "";
    while (fgets(buffer.data(), (int)buffer.size(), pipe.get()) != nullptr)
        result += buffer.data();
    return result;
}

static string pythonInterp() {
    if (system("command -v python3 >/dev/null 2>&1") == 0) return "python3";
    return "python";
}

// Wraps s as one shell word. A single quote inside single quotes has to be
// spliced in from outside them, hence the '\'' dance. Without this, an
// apostrophe anywhere in the validator source silently ends the quoted word and
// the shell concatenates the pieces, so python receives mangled code, prints
// nothing, and EVERY submission scores 0.
static string shellQuote(const string &s) {
    string out = "'";
    for (char c : s) {
        if (c == '\'') out += "'\\''";
        else out += c;
    }
    out += "'";
    return out;
}

// Runs on the submission as DATA (ast.parse), never executes it.
// Prints "VALID" or "BANNED <reason>".
static const char *validatorSrc = R"PY(
import ast, sys, re

def fail(reason):
    sys.exit(0)

try:
    raw = open(sys.argv[1], "rb").read()
except OSError:
    fail("cannot read submission")

# PEP 263 lets line 1 or 2 choose the codec CPython decodes the file with, so
# the bytes on disk can compile to something other than what they look like.
# The escape this closes:
#
#     # coding: unicode_escape
#     a=int(input())
#     b=int(input())
#     #\x0a print(a+b)
#
# Read as utf-8 that last line is a comment; decoded as unicode_escape the
# \x0a becomes a real newline and the print runs, using the banned + operator.
#
# Two independent defences. First, refuse any declaration that is not an
# ASCII-transparent utf-8 alias.
for line in raw.split(b"\n")[:2]:
    mo = re.match(rb"^[ \t\f]*#.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+)", line)
    if mo:
        enc = mo.group(1).lower().replace(b"_", b"-")
        if enc not in (b"utf-8", b"utf8", b"ascii", b"us-ascii"):
            fail("uses source encoding declaration " + mo.group(1).decode("ascii", "replace"))

# Second, hand ast the raw bytes. ast.parse() on bytes applies the same PEP 263
# decoding the interpreter does, so the tree below is exactly the program that
# would execute. Never change this back to reading decoded text.
try:
    tree = ast.parse(raw)
except (SyntaxError, ValueError):
    fail("not valid Python")

BANNED_NAMES = {"sum", "eval", "exec", "compile", "__import__",
                "getattr", "setattr", "delattr", "vars", "globals",
                "locals", "open"}
dunder = re.compile(r"^__.*__$")

for node in ast.walk(tree):
    if isinstance(node, ast.BinOp) and isinstance(node.op, (ast.Add, ast.Sub)):
        fail("uses + or - operator")
    if isinstance(node, ast.AugAssign) and isinstance(node.op, (ast.Add, ast.Sub)):
        fail("uses += or -= operator")
    if isinstance(node, ast.UnaryOp) and isinstance(node.op, (ast.UAdd, ast.USub)):
        fail("uses unary + or - operator")
    if isinstance(node, (ast.Import, ast.ImportFrom)):
        fail("uses import")
    if isinstance(node, ast.Name):
        if dunder.match(node.id):
            fail("uses dunder name " + node.id)
        if node.id in BANNED_NAMES:
            fail("references " + node.id)
    if isinstance(node, ast.Attribute) and dunder.match(node.attr):
        fail("accesses dunder attribute " + node.attr)

print("VALID")
)PY";

int main(int argc, char *argv[]) {
    if (argc < 4) {
        cout << 0 << endl;
        cerr << "Usage: checker <input> <answer> <code>" << endl;
        return 0;
    }

    string codefile = argv[3];

    ifstream in(argv[1]);
    unsigned long long a, b;
    if (!(in >> a >> b)) {
        cout << 0 << endl;
        cerr << "Bad input file" << endl;
        return 0;
    }

    ifstream ansIn(argv[2]);
    unsigned long long ans;
    if (!(ansIn >> ans)) {
        cout << 0 << endl;
        cerr << "Bad answer file" << endl;
        return 0;
    }
    if (a + b != ans)
        cerr << "Jury warning: answer file wrong" << endl;

    string py = pythonInterp();

    // 1) AST validation, inline (no files written). Reads the submission for parsing only.
    string verdict = trim(runCapture(py + " -c " + shellQuote(validatorSrc) +
                                     " " + shellQuote(codefile) + " 2>/dev/null"));
    if (verdict.rfind("VALID", 0) != 0) {
        cout << 0 << endl;
        return 0;
    }

    // 2) execute on the test input, piped in, with a timeout; then compare.
    string cmd = "printf '%s\\n%s\\n' " + to_string(a) + " " + to_string(b) +
                 " | timeout 1 " + py + " " + shellQuote(codefile) + " 2>/dev/null";
    string output = trim(runCapture(cmd));

    cout << (output == to_string(ans)) << endl;
    return 0;
}
