import time

def legendre_symbol(a, p):
    """
    Legendre symbol
    Define if a is a quadratic residue modulo odd prime
    http://en.wikipedia.org/wiki/Legendre_symbol
    """
    ls = pow(a, (p - 1)/2, p)
    if ls == p - 1:
        return -1
    return ls

def prime_mod_sqrt(a, p):
    """
    Square root modulo prime number
    Solve the equation
        x^2 = a mod p
    and return list of x solution
    http://en.wikipedia.org/wiki/Tonelli-Shanks_algorithm
    """
    a %= p

    # Simple case
    if a == 0:
        return [0]
    if p == 2:
        return [a]

    # Check solution existence on odd prime
    if legendre_symbol(a, p) != 1:
        return []

    # Simple case
    if p % 4 == 3:
        x = pow(a, (p + 1)/4, p)
        return [x, p-x]

    # Factor p-1 on the form q * 2^s (with Q odd)
    q, s = p - 1, 0
    while q % 2 == 0:
        s += 1
        q //= 2

    # Select a z which is a quadratic non resudue modulo p
    z = 1
    while legendre_symbol(z, p) != -1:
        z += 1
    c = pow(z, q, p)

    # Search for a solution
    x = pow(a, (q + 1)/2, p)
    t = pow(a, q, p)
    m = s
    while t != 1:
        # Find the lowest i such that t^(2^i) = 1
        i, e = 0, 2
        for i in xrange(1, m):
            if pow(t, e, p) == 1:
                break
            e *= 2

        # Update next value to iterate
        b = pow(c, 2**(m - i - 1), p)
        x = (x * b) % p
        t = (t * b * b) % p
        c = (b * b) % p
        m = i

    return [x, p-x]

def inv(a, n):
    if a == 0:
        return 0
    lm, hm = 1, 0
    low, high = a % n, n
    while low > 1:
        r = high//low
        nm, new = hm-lm*r, high-low*r
        lm, low, hm, high = nm, new, lm, low
    return lm % n

# Pre-compute (i) a list of primes
# (ii) a list of -1 legendre bases for each prime
# (iii) the inverse for each base
LENPRIMES = 1000
primes = []
r = 2**31 - 1
for i in range(LENPRIMES):
    r += 2
    while pow(2, r, r) != 2: r += 2
    primes.append(r)
bases = [None] * LENPRIMES
invbases = [None] * LENPRIMES
for i in range(LENPRIMES):
    b = 2
    while legendre_symbol(b, primes[i]) == 1:
        b += 1
    bases[i] = b
    invbases[i] = inv(b, primes[i])

# Compute the PoW
def forward(val, rounds=10**6):
    t1 = time.time()
    for i in range(rounds):
        # Select a prime
        p = primes[i % LENPRIMES]
        # Make sure the value we're working on is a
        # quadratic residue. If it's not, do a spooky
        # transform (ie. multiply by a known
        # non-residue) to make sure that it is
        if legendre_symbol(val, p) != 1:
            val = (val * invbases[i % LENPRIMES]) % p
            mul_by_base = 1
        else:
            mul_by_base = 0
        # Take advantage of the fact that two square
        # roots exist to hide whether or not the spooky
        # transform was done in the result so that we
        # can invert it when verifying
        val = sorted(prime_mod_sqrt(val, p))[mul_by_base]
    print time.time() - t1
    return val

def backward(val, rounds=10**6):
    t1 = time.time()
    for i in range(rounds-1, -1, -1):
        # Select a prime
        p = primes[i % LENPRIMES]
        # Extract the info about whether or not the
        # spooky transform was done
        mul_by_base = val * 2 > p
        # Square the value (ie. invert the square root)
        val = pow(val, 2, p)
        # Undo the spooky transform if needed
        if mul_by_base:
            val = (val * bases[i % LENPRIMES]) % p
    print time.time() - t1
    return val