import logging
from logging import debug, info, warning, error, critical, exception

def sweep(mkResult, start, increment, afterPeak = 2, maxRuns = 20, maxInput = None):
    """Perform a linear sweep starting at START and incrementing by
    INCREMENT until AFTERPEAK points after the peak result.  mkResult
    must return an object with a getResult method.  This will yield
    pairs of test input and result object."""

    results = {}
    n = start
    stopAfterPeak = afterPeak

    while True:
        # I would *love* to use Python's co-routine support for this,
        # but they left out syntax for *using* a co-routine!
        info("*" * 79)
        result = mkResult()
        assert hasattr(result, "getResult")
        yield n, result
        lastResult = result.getResult()
        results[n] = lastResult
        info("*" * 79)

        info("Run %d", len(results))
        info("  Input: %s", n)
        info("  Result: %s", lastResult)
        maxResult = max(results.values())
        info("  Max result: %s", maxResult)

        if lastResult == maxResult:
            stopAfterPeak = len(results) + afterPeak
            info("  Still at peak -- resetting stopAfterPeak to %d", stopAfterPeak)
        else:
            info("  Past peak -- will stop at %d", stopAfterPeak)
        if len(results) >= maxRuns:
            info("  Stopping -- reached maxRuns %d", maxRuns)
            break
        if len(results) >= stopAfterPeak:
            info("  Stopping -- reached stopAfterPeak %d", stopAfterPeak)
            break
        if maxInput != None and n + increment > maxInput:
            info("  Stopping -- reached maxInput %s", maxInput)
            break

        n += increment


class TestResult(object):
    def setResult(self, v):
        self.result = v

    def getResult(self):
        return self.result

def test(strategy):
    for v, result in strategy:
        result.setResult(25 - (v - 5) ** 2)

if __name__ == "__main__":
    logging.getLogger().setLevel(logging.INFO)
    test(sweep(TestResult, 0, 1))
