from lib_misc import is_palindrome


def euler_004():
    r = 0
    for a in range(999, 99, -1):
        for b in range(a, r // a, -1):
            if is_palindrome(a * b) and a * b > r:
                r = a * b
    return r


def euler_004_original():
    """ The solution that I came up with originally. """
    r = 0
    for a in range(999, 99, -1):
        if a * a < r:
            break
        for b in range(a, 99, -1):
            c = a * b
            if c > r and is_palindrome(c):
                r = c
    return r


def euler_004_forum():
    """
    A solution from etatsui in the project Euler forum.
    It is impressive to see how much fast it is relying on
    mathematical tricks:
        11(9091a + 910b + 100c) = mn;
    Let:
        11 * 10 < m < 11 * 90
    """
    for a in range(9, 0, -1):
        for b in range(9, -1, -1):
            for c in range(9, -1, -1):
                num = 9091 * a + 910 * b + 100 * c
                for m in range(90, 9, -1):
                    if num % m == 0:
                        if num / m > 999:
                            break
                        else:
                            result = num * 11
                            return result


assert(euler_004() == 906609)
print("e004.py: {}".format(euler_004()))


def time_tests():
    from timeit import timeit
    assert(euler_004() == 906609)
    assert(euler_004_original() == 906609)
    assert(euler_004_forum() == 906609)

    print(timeit(lambda: euler_004(), number=100))
    print(timeit(lambda: euler_004_original(), number=100))
    print(timeit(lambda: euler_004_forum(), number=100))


# time_tests()
# 0.5044660240000667
# 0.7896412069985672
# 0.04794573199978913
