from subprocess import Popen, PIPE
from random import randint, choice

class WrongOutput (Exception): pass

class Tester:
    def __init__ (self, file, generator, /, solver = None, checker = None):
        self.file = file
        self.generator = generator
        self.solver = solver
        self.checker = checker
        self._total = 0
        self._wrong = set()

    def __iter__ (self):
        for inp in self.generator():
            p = Popen(self.file, stdin=PIPE, stdout=PIPE, shell=True)
            res = p.communicate(inp.encode())[0].decode()
            self._total += 1
            
            if self.solver:
                right = self.solver(inp.strip()) == res.strip()
            else:
                right = self.checker(inp, res)
            if not right:
                self._wrong.add((inp, res))
                yield inp, res

    def run (self):
        for _ in self:
            pass
        return self

    def stats (self):
        print(f"{len(self._wrong)} / {self._total} wrong tests")

    def show (self):
        print(f"{len(self._wrong)} wrong tests"+(":" if self._wrong else ""))
        for i, (inp, out) in enumerate(self._wrong):
            print(f"== {i+1}:", inp, sep="\n")
            if self.solver:
                print("<< exp:", self.solver(inp).strip(), sep="\n")
            print(">> out:", out.strip(), "", sep="\n")

    def __repr__ (self):
        return f"Tester({self.file})"

def repeat (times = -1):
    def decorator(func):
        if times == -1:
            def wrapper (*args):
                while True:
                    yield func(*args)
        else:
            def wrapper (*args):
                yield from (func(*args) for i in range(times))
        return wrapper
    return decorator
