#!/usr/bin/env python3
"""
Checker for the Hlohovec Space Program.

Usage: python3 checker.py input reference_output contestants_output maxscore
"""

from math import hypot, isfinite
from statistics import median
from sys import argv, stderr

EXPONENT = 1.5      # cost of one step is max(|a| - af, 0) ** EXPONENT
SLACK = 1.01        # we forgive being this much worse than the reference
EPS = 0.1           # additive term, so that OPT = 0 cases stay gradable
CAP = 10.0          # badness is clamped here
VERBOSE = False     # print extra stuff?

def verbose(msg):
    if VERBOSE:
        print(msg, file=stderr)
    else:
        pass

def fail(msg):
    """Internal problem, not the contestant's."""
    print(msg)
    exit(2)

def wrong_answer(msg):
    print(f"Wrong answer: {msg}")
    exit(1)

class Tokens:
    def __init__(self, path, what, contestant=False):
        self.bad = wrong_answer if contestant else fail
        try:
            with open(path) as f:
                self.tok = f.read().split()
        except OSError:
            fail(f"failed to open the {what} ({path})")
        except UnicodeDecodeError:
            self.bad(f"failed to parse the {what} "
                     f"(probably not a valid text file)")
        self.i = 0
        self.what = what

    def left(self):
        return len(self.tok) - self.i

    def ints(self, n):
        vals = self.floats(n)
        return None if vals is None else [int(v) for v in vals]

    def floats(self, n):
        out = []
        for _ in range(n):
            if self.i >= len(self.tok):
                return None
            t = self.tok[self.i]
            self.i += 1
            try:
                v = float(t)
            except ValueError:
                self.bad(f"failed to parse {t!r} as a number in the {self.what}")
            if not isfinite(v):
                self.bad(f"{t!r} is not a finite number in the {self.what}")
            out.append(v)
        return out

def flight_cost(t, af, p0, v0, pt, vt1, mid):
    pos = [p0] + mid + [pt]
    vel = [v0] + [(pos[j][0] - pos[j - 1][0], pos[j][1] - pos[j - 1][1])
                  for j in range(1, t + 1)] + [vt1]
    total = 0.0
    for i in range(t + 1):
        ax = vel[i + 1][0] - vel[i][0]
        ay = vel[i + 1][1] - vel[i][1]
        total += max(hypot(ax, ay) - af, 0.0) ** EXPONENT
    return total

def read_flight(src, t):
    vals = src.floats(2 * (t - 1))
    if vals is None: return None
    return [(vals[2 * i], vals[2 * i + 1]) for i in range(t - 1)]

def main():
    if len(argv) != 5:
        fail(f"correct usage: python3 {argv[0]} input reference_output contestants_output maxscore")

    try:
        maxscore = float(argv[4])
    except ValueError:
        fail(f"failed to parse the maxscore ({argv[4]!r})")

    fin = Tokens(argv[1], "input")
    fref = Tokens(argv[2], "reference output")
    fout = Tokens(argv[3], "contestant's output", contestant=True)

    T = fin.ints(1)
    if T is None: fail("failed to read T from the input")
    T = T[0]

    badnesses = []
    verdicts = []

    for tc in range(T):
        head = fin.floats(2)
        body = fin.floats(8)
        if head is None or body is None: fail(f"failed to read test {tc} from the input")
        t, af = int(head[0]), head[1]
        p0, v0 = (body[0], body[1]), (body[2], body[3])
        pt, vt1 = (body[4], body[5]), (body[6], body[7])

        ref = read_flight(fref, t)
        if ref is None: fail(f"INTERNAL ERROR: reference output ended inside test {tc}")
        opt = flight_cost(t, af, p0, v0, pt, vt1, ref)

        available = fout.left()
        mine = read_flight(fout, t)
        if mine is None: wrong_answer(f"too few tokens: output ended inside test {tc} of {T}")

        cost = flight_cost(t, af, p0, v0, pt, vt1, mine)
        badness = max(1.0, (cost + EPS) / (SLACK * opt + EPS))
        badnesses.append(badness)
        verdicts.append(f"{min(badness, CAP):.3f}")
        verbose(f"test {tc}: t={t} af={af:g} cost={cost:.9f} ref={opt:.9f} badness={badness:.4f}")
        if cost < opt - 1e-6 * max(abs(opt), 1.0):
            verbose(f"test {tc}: NOTE beats the reference by {opt - cost:.3g} -- the reference may not be optimal")

    if fout.left():
        wrong_answer(f"too many tokens: {fout.left()} left over after all {T} test cases")

    a = median(badnesses)
    b = max(badnesses)
    ap, bp = min(a, CAP), min(b, CAP)
    score = maxscore * (2.0 ** -ap + 2.0 ** -bp)
    worst = badnesses.index(b)

    verbose(f"median badness {a:.4f}, worst badness {b:.4f} on test {worst}")
    print(f"{score:.2f}")
    print(f"OK (a' = {ap:.4f}, b' = {bp:.4f})")

if __name__ == "__main__":
    main()
