Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I am trying to implement quadratic sieve factorization algorithm, but got stuck at finding modular squares with Tonelli-Shanks algorithm.
For example, if n = 87463, output of Tonelli-Shanks should be:

p = 2 -> x = 1  
p = 3 -> x = 1, 2  
p = 13 -> x = 5, 8  
p = 17 -> x = 7, 10  
p = 19 -> x = 5, 14  
p = 29 -> x = 12, 17  

But I get sometimes correct answers, and sometimes absolute rubbish:

tonelli_shanks(87463, 2)  
(87463, -87461)  
tonelli_shanks(87463, 3)  
(87463, -87460)  
tonelli_shanks(87463, 13)  
(8L, 5L)  
tonelli_shanks(87463, 17)  
(11, 6)  
tonelli_shanks(87463, 19)  
(5L, 14L)  
tonelli_shanks(87463, 29)  
(12L, 17L)   

I am following Tonelli-Algorithm description on Wikipedia ( http://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm ) but still cannot figure out the spot in tonelli_shanks function where I messed up. Could someone help find the problem? Here is the code I've written so far:

from math import *

def bound(num):
    o1 = 0.22
    return int(exp((0.5 + o1) * (log(num) * log(log(num))) ** 0.5))

def atkin_sieve(bound):
    primes = [2, 3, 5]
    sieve = [False] * (bound + 1)
    factor = int(sqrt(bound)) + 1
    for i in range(1, factor):
        for j in range(1, factor):
            n = 4 * i ** 2 + j ** 2
            if n <= bound and (n % 12 == 1 or n % 12 == 5):
                sieve[n] = not sieve[n]
            n = 3 * i ** 2 + j ** 2
            if n <= bound and n % 12 == 7:
                sieve[n] = not sieve[n]
            if i > j:
                n = 3 * i ** 2 - j ** 2
                if n <= bound and n % 12 == 11:
                    sieve[n] = not sieve[n]
    for i in range(5, factor):
        if sieve[i]:
            for j in range(i ** 2, bound, i ** 2):
                sieve[j] = False
    for i in range(7, bound):
        if sieve[i]: primes.append(i)
    return primes

def binary_jacobi(a, n):
    t = 1
    while a != 0:
        while a % 2 == 0:
            a >>= 1
            if n % 8 == 3 or n % 8 == 5: t = -t
        if a < n:
            a, n = n, a
            if a % 4 == 3 and n % 4 == 3: t = -t
        a = (a - n) >> 1
        if n % 8 == 3 or n % 8 == 5: t = -t
    if n == 1: return t
    else: return 0


def factor_base(primes, n):
    return [p for p in primes if binary_jacobi(n, p) == 1]

def stein_gcd(u, v):
    g = 1
    while u % 2 == 0 and v % 2 == 0:
        u >>= 1
        v >>= 1
        g <<= 1
    while u != 0:
        if u % 2 == 0: u >>= 1
        elif v % 2 == 0: v >>= 1
        else:
            t = abs((u - v) >> 2)
            if u >= v: u = t

            else: v = t
    return g * v

def modpower(a, k, n):
    """compute a ** k % n"""
    if k == 0: return 1
    if k == 1: return a % n
    q, r = divmod(k ,2)
    if r == 1:
        return modpower(a * a % n, q, n) * a % n
    else:
        return modpower(a * a % n, q, n)

def tonelli_shanks(n, p):
    h = p >> 1
    Q = p - 1
    S = 0
    while not Q & 1:
        Q >>= 1
        S += 1
    if S == 1:
        R = modpower(n, p + 1 >> 2, p)
        return R, p - R
    z = next(a for a in atkin_sieve(p) if binary_jacobi(p, a) == -1)
    c = modpower(z, Q, p)
    R = modpower(n, Q + 1 >> 1, p)
    if p - R > h: R -= p    
    t = modpower(n, Q, p)
    if p - t > h: t -= p
    M = S
    while t % p != 1:
        i = 0
        while t % p != 1:
            t **= 2
            i += 1
        b = modpower(c, 2 ** (M - i - 1), p)    
        bb = b * b % p
        if p - bb > h: bb -= p                    
        R = (R * b) % p        
        t = (t * bb) % p
        if p - t > h: t -= p        
        c = (bb) % p
        if p - c > h: c -= p        
        M = i        
    return R, p - R

Update 0: Apparently there an error in modpower function, instead of

if k == 0: return 1
if k == 1: return a

There should be:

if k == 0: return 1
if k == 1: return a % n

There is also an error in stein_gcd method but it is not used yet, dont pay attention to it.
After modpower fix tonelli_shanks almost works, but for

tonelli_shanks(87463, 17)

it still produces incorrect result - (11, 6) instead of (7, 10)

Update 1: I found and error, which was in the place where I couldn't even think I might make a mistake:

        i = 0
        while t % p != 1:
            t **= 2
            i += 1

Here I search for the lowest i, s.t. t ^ 2 ^ i mod p = 1. But while searching for i, I modify t, which I should not. So correct version is with

        i = 0
        temp = t
        while temp % p != 1:
            temp **= 2
            i += 1

DSM, thank you very much for pointing out other mistakes. J.F. Sebastian thanks for advice.

share|improve this question

migrated from stackoverflow.com Aug 23 '12 at 12:06

This question came from our site for professional and enthusiast programmers.

12  
don't use from math import *; always specify names explicitly unless in a REPL. Builtin pow(x,y,z) is equivalent to (x**y) % z. There is fractions.gcd() function. –  J.F. Sebastian Aug 12 '12 at 15:56
5  
eli.thegreenplace.net/2009/03/07/… –  200 OK Aug 12 '12 at 15:58
5  
stein_gcd(3,9) == 1, which doesn't seem right, and modpower(2, 1, 2) == 2, which doesn't seem right either. You might want to test your utility functions. –  DSM Aug 12 '12 at 16:03
1  
Shouldn't your update 1 be while temp % p != 1 instead of while t % p != 1? Otherwise you will have an infinite loop if t % p != 1. –  EarlCrapstone Aug 22 '12 at 19:28
3  
If this is resolved, someone (perhaps @YpsilonIV) should answer it and accept it so that it gets off the unanswered questions page. –  David Robinson Aug 22 '12 at 19:47
show 4 more comments

1 Answer

up vote 6 down vote accepted

Here's my version of the modular square root; it uses basically the same algorithm as yours, but in a somewhat different form. Mine is derived from Algorithm 2.3.8 of the book Prime Numbers by Richard Crandall and Carl Pomerance. I assume p is an odd prime and (a/p) == 1. This function returns one root x such that x^2 == a (mod p); the other root is a-x (mod p). Note that Python provides modular exponentiation natively: pow(b, e, m) is equivalent to b^e (mod m). You can see a sample run, using the jacobi function that you provided, at http://ideone.com/r7BAX.

def mod_sqrt(a, p):
    a = a % p

    if p % 8 == 3 or p % 8 == 7:
        return pow(a, (p+1)/4, p)

    elif p % 8 == 5:
        x = pow(a, (p+3)/8, p)
        c = (x*x) % p
        if a == c:
            return x
        return (x * pow(2, (p-1)/4, p)) % p

    else:

        # find a quadratic non-residue d
        d = 2
        while jacobi(d, p) > -1:
            d += 1

        # set p-1 = 2^s * t with t odd
        t = p - 1
        s = 0
        while t % 2 == 0:
            t /= 2
            s += 1

        at = pow(a, t, p)
        dt = pow(d, t, p)

        m = 0
        for i in xrange(0, s):
            if pow(at * pow(dt, m), pow(2, s-1-i), p) == (p-1):
                m = m + pow(2, i)

        return (pow(a, (t+1)/2) * pow(dt, m/2)) % p
share|improve this answer
    
p % 8 == 3 or p % 8 == 7 can be replaced by p % 4 == 3 –  Phong Mar 3 at 0:34
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.