def pollard_rho(n, max_iters=5, seed=1234): """Use Pollard's rho method to try to extract a factor of n. The returned factor may be a composite number. A maximum of max_iters iterations are performed; if no factor is found, None is returned. The rho algorithm is a Monte Carlo method whose outcome can be affected by changing the random seed value. References ========== Richard Crandall & Carl Pomerance (2005), "Prime Numbers: A Computational Perspective", Springer, 2nd edition, 229-231 """ prng = random.Random(seed + max_iters) for i in range(max_iters): # Alternative good nonrandom choice: a = 1 a = prng.randint(1, n-3) # Alternative good nonrandom choice: s = 2 s = prng.randint(0, n-1) U = V = s F = lambda x: (x**2 + a) % n while 1: U = F(U) V = F(F(V)) g = igcd(abs(U-V), n) if g == 1: continue if g == n: break return g return None
def is_primitive_root(a, p): """ Returns True if ``a`` is a primitive root of ``n`` ``a`` is said to be the primitive root of ``n`` if gcd(a, n) == 1 and totient(n) is the smallest positive number s.t. a**totient(n) cong 1 mod(n) **Examples** >>> from sympy.ntheory import is_primitive_root, n_order, totient >>> is_primitive_root(3, 10) True >>> is_primitive_root(9, 10) False >>> n_order(3, 10) == totient(10) True >>> n_order(9, 10) == totient(10) False """ a, p = int_tested(a, p) if igcd(a, p) != 1: raise ValueError("The two numbers should be relatively prime") if a > p: a = a % p if n_order(a, p) == totient_(p): return True else: return False
def pollard_pm1(n, B=10, seed=1234): """Use Pollard's p-1 method to try to extract a factor of n. The returned factor may be a composite number. The search is performed up to a smoothness bound B; if no factor is found, None is returned. The p-1 algorithm is a Monte Carlo method whose outcome can be affected by changing the random seed value. Example usage ============= With the default smoothness bound, this number can't be cracked: >>> pollard_pm1(21477639576571) Increasing the smoothness bound helps: >>> pollard_pm1(21477639576571, 2000) 4410317 References ========== Richard Crandall & Carl Pomerance (2005), "Prime Numbers: A Computational Perspective", Springer, 2nd edition, 236-238 """ from math import log prng = random.Random(seed + B) a = prng.randint(2, n-1) for p in sieve.primerange(2, B): e = int(log(B, p)) a = pow(a, p**e, n) g = igcd(a-1, n) if 1 < g < n: return g else: return None
def deflate(f, *G): ring = f.ring polys = [f] + list(G) J = [0]*ring.ngens for p in polys: for monom in p.monoms(): for i, m in enumerate(monom): J[i] = igcd(J[i], m) for i, b in enumerate(J): if not b: J[i] = 1 J = tuple(J) if all(b == 1 for b in J): return J, polys H = [] for p in polys: h = ring.zero for I, coeff in p.terms(): N = [ i // j for i, j in zip(I, J) ] h[tuple(N)] = coeff H.append(h) return J, H
def poly_reduce(f, g, *symbols): """Removes common content from a pair of polynomials. >>> from sympy import * >>> x = Symbol('x') >>> f = Poly(2930944*x**6 + 2198208*x**4 + 549552*x**2 + 45796, x) >>> g = Poly(17585664*x**5 + 8792832*x**3 + 1099104*x, x) >>> F, G = poly_reduce(f, g) >>> F Poly(64*x**6 + 48*x**4 + 12*x**2 + 1, x) >>> G Poly(384*x**5 + 192*x**3 + 24*x, x) """ if not isinstance(f, Poly): f = Poly(f, *symbols) elif symbols: raise SymbolsError("Redundant symbols were given") f, g = f.unify_with(g) fc = int(f.content) gc = int(g.content) cont = igcd(fc, gc) if cont != 1: f = f.div_term(cont) g = g.div_term(cont) return f, g
def _a(n, j, prec): """Compute the inner sum in the HRR formula.""" if j == 1: return fone s = fzero pi = pi_fixed(prec) for h in xrange(1, j): if igcd(h, j) != 1: continue # & with mask to compute fractional part of fixed-point number one = 1 << prec onemask = one - 1 half = one >> 1 g = 0 if j >= 3: for k in xrange(1, j): t = h*k*one//j if t > 0: frac = t & onemask else: frac = -((-t) & onemask) g += k*(frac - half) g = ((g - 2*h*n*one)*pi//j) >> prec s = mpf_add(s, mpf_cos(from_man_exp(g, -prec), prec), prec) return s
def roots_cyclotomic(f, factor=False): """Compute roots of cyclotomic polynomials. """ L, U = _inv_totient_estimate(f.degree()) for n in xrange(L, U + 1): g = cyclotomic_poly(n, f.gen, polys=True) if f == g: break else: # pragma: no cover raise RuntimeError("failed to find index of a cyclotomic polynomial") roots = [] if not factor: for k in xrange(1, n + 1): if igcd(k, n) == 1: roots.append(exp(2*k*S.Pi*I/n).expand(complex=True)) else: g = Poly(f, extension=(-1)**Rational(1, n)) for h, _ in g.factor_list()[1]: roots.append(-h.TC()) return sorted(roots, key=default_sort_key)
def is_primitive_root(a, p): """ Returns True if ``a`` is a primitive root of ``p`` ``a`` is said to be the primitive root of ``p`` if gcd(a, p) == 1 and totient(p) is the smallest positive number s.t. a**totient(p) cong 1 mod(p) Examples ======== >>> from sympy.ntheory import is_primitive_root, n_order, totient >>> is_primitive_root(3, 10) True >>> is_primitive_root(9, 10) False >>> n_order(3, 10) == totient(10) True >>> n_order(9, 10) == totient(10) False """ a, p = as_int(a), as_int(p) if igcd(a, p) != 1: raise ValueError("The two numbers should be relatively prime") if a > p: a = a % p return n_order(a, p) == totient(p)
def n_order(a, n): """Returns the order of ``a`` modulo ``n``. The order of ``a`` modulo ``n`` is the smallest integer ``k`` such that ``a**k`` leaves a remainder of 1 with ``n``. Examples ======== >>> from sympy.ntheory import n_order >>> n_order(3, 7) 6 >>> n_order(4, 7) 3 """ a, n = int_tested(a, n) if igcd(a, n) != 1: raise ValueError("The two numbers should be relatively prime") group_order = totient(n) factors = factorint(group_order) order = 1 if a > n: a = a % n for p, e in factors.iteritems(): exponent = group_order for f in xrange(0, e + 1): if (a ** (exponent)) % n != 1: order *= p ** (e - f + 1) break exponent = exponent // p return order
def roots_cyclotomic(f, factor=False): """Compute roots of cyclotomic polynomials. """ L, U = _inv_totient_estimate(f.degree()) for n in range(L, U + 1): g = cyclotomic_poly(n, f.gen, polys=True) if f == g: break else: # pragma: no cover raise RuntimeError("failed to find index of a cyclotomic polynomial") roots = [] if not factor: # get the indices in the right order so the computed # roots will be sorted h = n//2 ks = [i for i in range(1, n + 1) if igcd(i, n) == 1] ks.sort(key=lambda x: (x, -1) if x <= h else (abs(x - n), 1)) d = 2*I*pi/n for k in reversed(ks): roots.append(exp(k*d).expand(complex=True)) else: g = Poly(f, extension=root(-1, n)) for h, _ in ordered(g.factor_list()[1]): roots.append(-h.TC()) return roots
def jacobi_symbol(m, n): """ Returns the product of the legendre_symbol(m, p) for all the prime factors p of n. Returns ======= 1. 0 if m cong 0 mod(n) 2. 1 if x**2 cong m mod(n) has a solution 3. -1 otherwise Examples ======== >>> from sympy.ntheory import jacobi_symbol, legendre_symbol >>> from sympy import Mul, S >>> jacobi_symbol(45, 77) -1 >>> jacobi_symbol(60, 121) 1 The relationship between the jacobi_symbol and legendre_symbol can be demonstrated as follows: >>> L = legendre_symbol >>> S(45).factors() {3: 2, 5: 1} >>> jacobi_symbol(7, 45) == L(7, 3)**2 * L(7, 5)**1 True """ m, n = int_tested(m, n) if not n % 2: raise ValueError("n should be an odd integer") if m < 0 or m > n: m = m % n if not m: return int(n == 1) if n == 1 or m == 1: return 1 if igcd(m, n) != 1: return 0 j = 1 s = trailing(m) m = m >> s if s % 2 and n % 8 in [3, 5]: j *= -1 while m != 1: if m % 4 == 3 and n % 4 == 3: j *= -1 m, n = n % m, m s = trailing(m) m = m >> s if s % 2 and n % 8 in [3, 5]: j *= -1 return j
def n_order(a,n): """ returns the order of a modulo n Order of a modulo n is the smallest integer k such that a^k leaves a remainder of 1 with n. """ assert igcd(a,n)==1 if a>n : a=a%n for x in xrange(1,totient_(n)+1): if (a**x)%n==1: return x
def totient_(n): """returns the number of integers less than n and relatively prime to n""" if n < 1: raise ValueError("n must be a positive integer") tot = 0 for x in xrange(1, n): if igcd(x, n) == 1: tot += 1 return tot
def get_random_primitive_root(self): while True: val = random.randint(self._prime // (2 * 2), (self._prime - 1) // 2) * 2 - 1 if not (val % 3 and val % 5): continue if igcd(val, self._prime) != 1: continue if is_primitive_root(val, self._prime): return val
def is_primitive_root(a,p): """ returns True if a is a primitive root of p """ assert igcd(a,p) == 1,"The two numbers should be relatively prime" if a>p: a=a%p if n_order(a,p)==totient_(p): return True else: return False
def zzx_content(f): """Returns integer GCD of coefficients. """ cont = 0 for coeff in f: cont = igcd(cont, coeff) if cont == 1: break return cont
def is_primitive_root(a, p): """ returns True if a is a primitive root of p """ if igcd(a, p) != 1: raise ValueError("The two numbers should be relatively prime") if a > p: a = a % p if n_order(a, p) == totient_(p): return True else: return False
def legendre_symbol(a,p): """ return 1 if a is a quadratic residue of p else return -1 p should be an odd prime by definition """ assert isprime(p) and p!=2,"p should be an odd prime" assert igcd(a,p)==1,"The two numbers should be relatively prime" if a>p: a=a%p if is_quad_residue(a,p)==True: return 1 else : return -1
def _is_nthpow_residue_bign(a, n, m): """Returns True if ``x**n == a (mod m)`` has solutions for n > 2.""" # assert n > 2 # assert a > 0 and m > 0 if primitive_root(m) is None: # assert m >= 8 for prime, power in factorint(m).items(): if not _is_nthpow_residue_bign_prime_power(a, n, prime, power): return False return True f = totient(m) k = f // igcd(f, n) return pow(a, k, m) == 1
def is_quad_residue(a, p): """ returns True if a is a quadratic residue of p p should be a prime and a should be relatively prime to p """ assert isprime(p) and p != 2, "p should be an odd prime" assert igcd(a, p) == 1, "The two numbers should be relatively prime" if a > p: a = a % p rem = (a**((p - 1) // 2)) % p # a^(p-1 / 2) % p if rem == 1: return True else: return False
def __iter__(self): yield S.Zero yield S.One yield S.NegativeOne d = 2 while True: for n in range(d): if igcd(n, d) == 1: yield Rational(n, d) yield Rational(d, n) yield Rational(-n, d) yield Rational(-d, n) d += 1
def is_quad_residue(a,p): """ returns True if a is a quadratic residue of p p should be a prime and a should be relatively prime to p """ assert isprime(p) and p!=2,"p should be an odd prime" assert igcd(a,p)==1,"The two numbers should be relatively prime" if a>p: a=a%p rem=(a**((p-1)//2))%p # a^(p-1 / 2) % p if rem==1: return True else : return False
def rs_puiseux(f, p, x, prec): """ Return the puiseux series for `f(p, x, prec)` To be used when function`f` is implemented only for regular series Examples ======== >>> from sympy.polys.domains import QQ >>> from sympy.polys.rings import ring >>> from sympy.polys.ring_series import rs_puiseux, rs_exp >>> R, x = ring('x', QQ) >>> p = x**QQ(2,5) + x**QQ(2,3) + x >>> rs_puiseux(rs_exp,p, x, 1) 1/2*x**(4/5) + x**(2/3) + x**(2/5) + 1 """ index = p.ring.gens.index(x) n = 1 for k in p: power = k[index] if isinstance(power, Rational): num, den = power.as_numer_denom() n = n * den // igcd(n, den) elif power != int(power): num, den = power.numerator, power.denominator n = n * den // igcd(n, den) if n != 1: p1 = pow_xin(p, index, n) r = f(p1, x, prec * n) n1 = QQ(1, n) if isinstance(r, tuple): r = tuple([pow_xin(rx, index, n1) for rx in r]) else: r = pow_xin(r, index, n1) else: r = f(p, x, prec) return r
def rs_puiseux(f, p, x, prec): """ Return the puiseux series for `f(p, x, prec)` To be used when function`f` is implemented only for regular series Examples ======== >>> from sympy.polys.domains import QQ >>> from sympy.polys.rings import ring >>> from sympy.polys.ring_series import rs_puiseux, rs_exp >>> R, x = ring('x', QQ) >>> p = x**QQ(2,5) + x**QQ(2,3) + x >>> rs_puiseux(rs_exp,p, x, 1) 1/2*x**(4/5) + x**(2/3) + x**(2/5) + 1 """ index = p.ring.gens.index(x) n = 1 for k in p: power = k[index] if isinstance(power, Rational): num, den = power.as_numer_denom() n = n*den // igcd(n, den) elif power != int(power): num, den = power.numerator, power.denominator n = n*den // igcd(n, den) if n != 1: p1 = pow_xin(p, index, n) r = f(p1, x, prec*n) n1 = QQ(1, n) if isinstance(r, tuple): r = tuple([pow_xin(rx, index, n1) for rx in r]) else: r = pow_xin(r, index, n1) else: r = f(p, x, prec) return r
def __init__(self, p, q=None): if q is None: self.p = p self.q = 1 else: if not q: raise ZeroDivisionError('rational number') elif q < 0: p, q = -p, -q g = igcd(p, q) self.p = p // g self.q = q // g
def shor(N): """This function implements Shor's factoring algorithm on the Integer N The algorithm starts by picking a random number (a) and seeing if it is coprime with N. If it isn't, then the gcd of the two numbers is a factor and we are done. Otherwise, it begins the period_finding subroutine which finds the period of a in modulo N arithmetic. This period, if even, can be used to calculate factors by taking a**(r/2)-1 and a**(r/2)+1. These values are returned. """ a = random.randrange(N - 2) + 2 if igcd(N, a) != 1: print "got lucky with rand" return igcd(N, a) print "a= ", a print "N= ", N r = period_find(a, N) print "r= ", r if r % 2 == 1: print "r is not even, begin again" shor(N) answer = (igcd(a**(r/2) - 1, N), igcd(a**(r/2) + 1, N)) return answer
def __iter__(self): from sympy.core.numbers import igcd, Rational yield S.Zero yield S.One yield S.NegativeOne d = 2 while True: for n in range(d): if igcd(n, d) == 1: yield Rational(n, d) yield Rational(d, n) yield Rational(-n, d) yield Rational(-d, n) d += 1
def shor(N): """This function implements Shor's factoring algorithm on the Integer N The algorithm starts by picking a random number (a) and seeing if it is coprime with N. If it isn't, then the gcd of the two numbers is a factor and we are done. Otherwise, it begins the period_finding subroutine which finds the period of a in modulo N arithmetic. This period, if even, can be used to calculate factors by taking a**(r/2)-1 and a**(r/2)+1. These values are returned. """ a = random.randrange(N - 2) + 2 if igcd(N, a) != 1: print "got lucky with rand" return igcd(N, a) print "a= ", a print "N= ", N r = period_find(a, N) print "r= ", r if r % 2 == 1: print "r is not even, begin again" shor(N) answer = (igcd(a**(r / 2) - 1, N), igcd(a**(r / 2) + 1, N)) return answer
def __init__(self, p, q=None): if q is None: self.p = p self.q = 1 else: if not q: raise ZeroDivisionError('rational number') elif q < 0: p, q = -p, -q g = igcd(p, q) self.p = p//g self.q = q//g
def rs_puiseux2(f, p, q, x, prec): """ Return the puiseux series for `f(p, q, x, prec)` To be used when function `f` is implemented only for regular series """ index = p.ring.gens.index(x) n = 1 for k in p: power = k[index] if isinstance(power, Rational): num, den = power.as_numer_denom() n = n*den // igcd(n, den) elif power != int(power): num, den = power.numerator, power.denominator n = n*den // igcd(n, den) if n != 1: p1 = pow_xin(p, index, n) r = f(p1, q, x, prec*n) n1 = QQ(1, n) r = pow_xin(r, index, n1) else: r = f(p, q, x, prec) return r
def rs_puiseux2(f, p, q, x, prec): """ Return the puiseux series for `f(p, q, x, prec)` To be used when function `f` is implemented only for regular series """ index = p.ring.gens.index(x) n = 1 for k in p: power = k[index] if isinstance(power, Rational): num, den = power.as_numer_denom() n = n * den // igcd(n, den) elif power != int(power): num, den = power.numerator, power.denominator n = n * den // igcd(n, den) if n != 1: p1 = pow_xin(p, index, n) r = f(p1, q, x, prec * n) n1 = QQ(1, n) r = pow_xin(r, index, n1) else: r = f(p, q, x, prec) return r
def Shor(N): """ Algoritmo de Shor. """ print("N= ", N) while (True): # Armazena um número aleatório menor que N x = random.randrange(N - 2) + 2 # x = 2 # teste print("x= ", x) # Se não é coprimo com N if igcd(N, x) != 1: print("nao eh coprimo! MDC(N, x) != 1!") return igcd(N, x) # O resultado da fatoração é apenas o MDC de N e x # Encontra o período r print("calculando o periodo...") r = find_r(x, N) print("r= ", r) # Se r não é par if r % 2 == 1: print("r nao eh par, reiniciando...") else: break # Como r é par, podemos calcular os fatores answer = (igcd(x**(r / 2) - 1, N), igcd(x**(r / 2) + 1, N)) return answer
def legendre_symbol(a, p): """ return 1 if a is a quadratic residue of p else return -1 p should be an odd prime by definition """ if not isprime(p) or p == 2: raise ValueError("p should be an odd prime") if igcd(a, p) != 1: raise ValueError("The two numbers should be relatively prime") if a > p: a = a % p if is_quad_residue(a, p): return 1 else: return -1
def is_nthpow_residue(a, n, m): """ Returns True if ``x**n == a (mod m)`` has solutions. References ========== P. Hackman "Elementary Number Theory" (2009), page 76 """ if n == 1: return True if n == 2: return is_quad_residue(a, m) f = totient(m) k = f // igcd(f, n) return pow(a, k, m) == 1
def is_nthpow_residue(a, n, m): """ Returns True if ``x**n == a (mod m)`` has solutions. References ========== P. Hackman "Elementary Number Theory" (2009), page 76 """ if( primitive_root(m) == None ): raise NotImplementedError("Not implemented for m which do not have any primitive root") if n == 1: return True if n == 2: return is_quad_residue(a, m) f = totient(m) k = f // igcd(f, n) return pow(a, k, m) == 1
def pollard_rho(n, retries=5, max_steps=None, seed=1234): """Use Pollard's rho method to try to extract a nontrivial factor of ``n``. The returned factor may be a composite number. If no factor is found, ``None`` is returned. The algorithm may need to take thousands of steps before it finds a factor or reports failure. If ``max_steps`` is specified, the iteration is cancelled with a failure after the specified number of steps. On failure, the algorithm will self-restart (with different parameters) up to ``retries`` number of times. The rho algorithm is a Monte Carlo method whose outcome can be affected by changing the random seed value. References ========== - Richard Crandall & Carl Pomerance (2005), "Prime Numbers: A Computational Perspective", Springer, 2nd edition, 229-231 """ prng = random.Random(seed + retries) for i in range(retries): # Alternative good nonrandom choice: a = 1 a = prng.randint(1, n - 3) # Alternative good nonrandom choice: s = 2 s = prng.randint(0, n - 1) U = V = s F = lambda x: (x**2 + a) % n j = 0 while 1: if max_steps and (j > max_steps): break j += 1 U = F(U) V = F(F(V)) g = igcd(abs(U - V), n) if g == 1: continue if g == n: break return int(g) return None
def pollard_rho(n, retries=5, max_steps=None, seed=1234): """Use Pollard's rho method to try to extract a nontrivial factor of ``n``. The returned factor may be a composite number. If no factor is found, ``None`` is returned. The algorithm may need to take thousands of steps before it finds a factor or reports failure. If ``max_steps`` is specified, the iteration is cancelled with a failure after the specified number of steps. On failure, the algorithm will self-restart (with different parameters) up to ``retries`` number of times. The rho algorithm is a Monte Carlo method whose outcome can be affected by changing the random seed value. References ========== - Richard Crandall & Carl Pomerance (2005), "Prime Numbers: A Computational Perspective", Springer, 2nd edition, 229-231 """ prng = random.Random(seed + retries) for i in range(retries): # Alternative good nonrandom choice: a = 1 a = prng.randint(1, n-3) # Alternative good nonrandom choice: s = 2 s = prng.randint(0, n-1) U = V = s F = lambda x: (x**2 + a) % n j = 0 while 1: if max_steps and (j > max_steps): break j += 1 U = F(U) V = F(F(V)) g = igcd(abs(U-V), n) if g == 1: continue if g == n: break return int(g) return None
def totient_(n): """Returns the number of integers less than n and relatively prime to n Examples ======== >>> from sympy.ntheory import totient_ >>> totient_(6) 2 >>> totient_(67) 66 """ n = int_tested(n) if n < 1: raise ValueError("n must be a positive integer") tot = 0 for x in xrange(1, n): if igcd(x, n) == 1: tot += 1 return tot
def n_order(a, n): """Returns the order of ``a`` modulo ``n``. The order of ``a`` modulo ``n`` is the smallest integer ``k`` such that ``a**k`` leaves a remainder of 1 with ``n``. Examples ======== >>> from sympy.ntheory import n_order >>> n_order(3, 7) 6 >>> n_order(4, 7) 3 """ from collections import defaultdict a, n = as_int(a), as_int(n) if igcd(a, n) != 1: raise ValueError("The two numbers should be relatively prime") factors = defaultdict(int) f = factorint(n) for px, kx in f.items(): if kx > 1: factors[px] += kx - 1 fpx = factorint(px - 1) for py, ky in fpx.items(): factors[py] += ky group_order = 1 for px, kx in factors.items(): group_order *= px ** kx order = 1 if a > n: a = a % n for p, e in factors.items(): exponent = group_order for f in range(e + 1): if pow(a, exponent, n) != 1: order *= p ** (e - f + 1) break exponent = exponent // p return order
def _lucas_extrastrong_params(n): """Calculates the "extra strong" parameters (D, P, Q) for n. References ========== .. [1] OEIS A217719: Extra Strong Lucas Pseudoprimes https://oeis.org/A217719 .. [1] https://en.wikipedia.org/wiki/Lucas_pseudoprime """ from sympy.ntheory.residue_ntheory import jacobi_symbol P, Q, D = 3, 1, 5 while True: g = igcd(D, n) if g > 1 and g != n: return (0, 0, 0) if jacobi_symbol(D, n) == -1: break P += 1 D = P * P - 4 return _int_tuple(D, P, Q)
def n_order(a, n): """ returns the order of a modulo n Order of a modulo n is the smallest integer k such that a^k leaves a remainder of 1 with n. """ if igcd(a, n) != 1: raise ValueError("The two numbers should be relatively prime") group_order = totient_(n) factors = factorint(group_order) order = 1 if a > n: a = a % n for p, e in factors.iteritems(): exponent = group_order for f in xrange(0, e + 1): if (a ** (exponent)) % n != 1: order *= p ** (e - f + 1) break exponent = exponent // p return order
def n_order(a, n): """ returns the order of a modulo n Order of a modulo n is the smallest integer k such that a^k leaves a remainder of 1 with n. """ if igcd(a, n) != 1: raise ValueError("The two numbers should be relatively prime") group_order = totient_(n) factors = factorint(group_order) order = 1 if a > n: a = a % n for p, e in factors.iteritems(): exponent = group_order for f in xrange(0, e + 1): if (a**(exponent)) % n != 1: order *= p**(e - f + 1) break exponent = exponent // p return order
def pollard_pm1(n, B=10, seed=1234): """ Use Pollard's p-1 method to try to extract a nontrivial factor of ``n``. The returned factor may be a composite number. If no factor is found, ``None`` is returned. The search is performed up to a smoothness bound ``B``. Choosing a larger B increases the likelihood of finding a large factor. The p-1 algorithm is a Monte Carlo method whose outcome can be affected by changing the random seed value. Example usage ============= With the default smoothness bound, this number can't be cracked: >>> from sympy.ntheory import pollard_pm1 >>> pollard_pm1(21477639576571) Increasing the smoothness bound helps: >>> pollard_pm1(21477639576571, B=2000) 4410317 References ========== - Richard Crandall & Carl Pomerance (2005), "Prime Numbers: A Computational Perspective", Springer, 2nd edition, 236-238 """ prng = random.Random(seed + B) a = prng.randint(2, n - 1) for p in sieve.primerange(2, B): e = int(math.log(B, p)) a = pow(a, p**e, n) g = igcd(a - 1, n) if 1 < g < n: return int(g) else: return None
def _find_factor(dependent_rows, mark, gauss_matrix, index, smooth_relations, N): """Finds proper factor of N. Here, transform the dependent rows as a combination of independent rows of the gauss_matrix to form the desired relation of the form ``X**2 = Y**2 modN``. After obtaining the desired relation we obtain a proper factor of N by `gcd(X - Y, N)`. Parameters: =========== dependent_rows : denoted dependent rows in the reduced matrix form mark : boolean array to denoted dependent and independent rows gauss_matrix : Reduced form of the smooth relations matrix index : denoted the index of the dependent_rows smooth_relations : Smooth relations vectors matrix N : Number to be factored """ from sympy import integer_nthroot idx_in_smooth = dependent_rows[index][1] independent_u = [smooth_relations[idx_in_smooth][0]] independent_v = [smooth_relations[idx_in_smooth][1]] dept_row = dependent_rows[index][0] for idx, val in enumerate(dept_row): if val == 1: for row in range(len(gauss_matrix)): if gauss_matrix[row][idx] == 1 and mark[row] == True: independent_u.append(smooth_relations[row][0]) independent_v.append(smooth_relations[row][1]) break u = 1 v = 1 for i in independent_u: u *= i for i in independent_v: v *= i #assert u**2 % N == v % N v = integer_nthroot(v, 2)[0] return igcd(u - v, N)
def _lucas_selfridge_params(n): """Calculates the Selfridge parameters (D, P, Q) for n. This is method A from page 1401 of Baillie and Wagstaff. References ========== .. [1] "Lucas Pseudoprimes", Baillie and Wagstaff, 1980. http://mpqs.free.fr/LucasPseudoprimes.pdf """ from sympy.ntheory.residue_ntheory import jacobi_symbol D = 5 while True: g = igcd(abs(D), n) if g > 1 and g != n: return (0, 0, 0) if jacobi_symbol(D, n) == -1: break if D > 0: D = -D - 2 else: D = -D + 2 return _int_tuple(D, 1, (1 - D) / 4)
def test_rsa_multipower_exhanstive(): from sympy.core.numbers import igcd primes = [5, 5, 7] e = 7 args = primes + [e] puk = rsa_public_key(*args, multipower=True) prk = rsa_private_key(*args, multipower=True) n = puk[0] for msg in range(n): if igcd(msg, n) != 1: continue encrypted = encipher_rsa(msg, puk) decrypted = decipher_rsa(encrypted, prk) try: assert decrypted == msg except AssertionError: raise AssertionError( "The RSA is not correctly decrypted " \ "(Original : {}, Encrypted : {}, Decrypted : {})" \ .format(msg, encrypted, decrypted) )
def _a(n, j, prec): """Compute the inner sum in the HRR formula.""" if j == 1: return fone s = fzero pi = pi_fixed(prec) for h in xrange(1, j): if igcd(h,j) != 1: continue # & with mask to compute fractional part of fixed-point number one = 1 << prec onemask = one - 1 half = one >> 1 g = 0 if j >= 3: for k in xrange(1, j): t = h*k*one//j if t > 0: frac = t & onemask else: frac = -((-t) & onemask) g += k*(frac - half) g = ((g - 2*h*n*one)*pi//j) >> prec s = mpf_add(s, mpf_cos(from_man_exp(g, -prec), prec), prec) return s
def is_quad_residue(a, p): """ returns True if a is a quadratic residue of p p should be a prime and a should be relatively prime to p """ if not isprime(p) or p == 2: raise ValueError("p should be an odd prime") if igcd(a, p) != 1: raise ValueError("The two numbers should be relatively prime") if a > p: a = a % p def square_and_multiply(a, n, p): if n == 0: return 1 elif n == 1: return a elif n % 2 == 1: return ((square_and_multiply(a, n // 2, p)**2) * a) % p else: return (square_and_multiply(a, n // 2, p)**2) % p return (square_and_multiply(a, (p - 1) // 2, p) % p) == 1
def test_igcd(): assert igcd(0, 0) == 0 assert igcd(0, 1) == 1 assert igcd(1, 0) == 1 assert igcd(0, 7) == 7 assert igcd(7, 0) == 7 assert igcd(7, 1) == 1 assert igcd(1, 7) == 1 assert igcd(-1, 0) == 1 assert igcd(0, -1) == 1 assert igcd(-1, -1) == 1 assert igcd(-1, 7) == 1 assert igcd(7, -1) == 1 assert igcd(8, 2) == 2 assert igcd(4, 8) == 4 assert igcd(8, 16) == 8 assert igcd(7, -3) == 1 assert igcd(-7, 3) == 1 assert igcd(-7, -3) == 1 assert igcd(*[10, 20, 30]) == 10 raises(ValueError, lambda: igcd(45.1, 30)) raises(ValueError, lambda: igcd(45, 30.1))
def pollard_pm1(n, B=10, a=2, retries=0, seed=1234): """ Use Pollard's p-1 method to try to extract a nontrivial factor of ``n``. Either a divisor (perhaps composite) or ``None`` is returned. The value of ``a`` is the base that is used in the test gcd(a**M - 1, n). The default is 2. If ``retries`` > 0 then if no factor is found after the first attempt, a new ``a`` will be generated randomly (using the ``seed``) and the process repeated. Note: the value of M is lcm(1..B) = reduce(ilcm, range(2, B + 1)). A search is made for factors next to even numbers having a power smoothness less than ``B``. Choosing a larger B increases the likelihood of finding a larger factor but takes longer. Whether a factor of n is found or not depends on ``a`` and the power smoothness of the even mumber just less than the factor p (hence the name p - 1). Although some discussion of what constitutes a good ``a`` some descriptions are hard to interpret. At the modular.math site referenced below it is stated that if gcd(a**M - 1, n) = N then a**M % q**r is 1 for every prime power divisor of N. But consider the following: >>> from sympy.ntheory.factor_ import smoothness_p, pollard_pm1 >>> n=257*1009 >>> smoothness_p(n) (-1, [(257, (1, 2, 256)), (1009, (1, 7, 16))]) So we should (and can) find a root with B=16: >>> pollard_pm1(n, B=16, a=3) 1009 If we attempt to increase B to 256 we find that it doesn't work: >>> pollard_pm1(n, B=256) >>> But if the value of ``a`` is changed we find that only multiples of 257 work, e.g.: >>> pollard_pm1(n, B=256, a=257) 1009 Checking different ``a`` values shows that all the ones that didn't work had a gcd value not equal to ``n`` but equal to one of the factors: >>> from sympy.core.numbers import ilcm, igcd >>> from sympy import factorint, Pow >>> M = 1 >>> for i in range(2, 256): ... M = ilcm(M, i) ... >>> set([igcd(pow(a, M, n) - 1, n) for a in range(2, 256) if ... igcd(pow(a, M, n) - 1, n) != n]) set([1009]) But does aM % d for every divisor of n give 1? >>> aM = pow(255, M, n) >>> [(d, aM%Pow(*d.args)) for d in factorint(n, visual=True).args] [(257**1, 1), (1009**1, 1)] No, only one of them. So perhaps the principle is that a root will be found for a given value of B provided that: 1) the power smoothness of the p - 1 value next to the root does not exceed B 2) a**M % p != 1 for any of the divisors of n. By trying more than one ``a`` it is possible that one of them will yield a factor. Examples ======== With the default smoothness bound, this number can't be cracked: >>> from sympy.ntheory import pollard_pm1, primefactors >>> pollard_pm1(21477639576571) Increasing the smoothness bound helps: >>> pollard_pm1(21477639576571, B=2000) 4410317 Looking at the smoothness of the factors of this number we find: >>> from sympy.utilities import flatten >>> from sympy.ntheory.factor_ import smoothness_p, factorint >>> print(smoothness_p(21477639576571, visual=1)) p**i=4410317**1 has p-1 B=1787, B-pow=1787 p**i=4869863**1 has p-1 B=2434931, B-pow=2434931 The B and B-pow are the same for the p - 1 factorizations of the divisors because those factorizations had a very large prime factor: >>> factorint(4410317 - 1) {2: 2, 617: 1, 1787: 1} >>> factorint(4869863-1) {2: 1, 2434931: 1} Note that until B reaches the B-pow value of 1787, the number is not cracked; >>> pollard_pm1(21477639576571, B=1786) >>> pollard_pm1(21477639576571, B=1787) 4410317 The B value has to do with the factors of the number next to the divisor, not the divisors themselves. A worst case scenario is that the number next to the factor p has a large prime divisisor or is a perfect power. If these conditions apply then the power-smoothness will be about p/2 or p. The more realistic is that there will be a large prime factor next to p requiring a B value on the order of p/2. Although primes may have been searched for up to this level, the p/2 is a factor of p - 1, something that we don't know. The modular.math reference below states that 15% of numbers in the range of 10**15 to 15**15 + 10**4 are 10**6 power smooth so a B of 10**6 will fail 85% of the time in that range. From 10**8 to 10**8 + 10**3 the percentages are nearly reversed...but in that range the simple trial division is quite fast. References ========== - Richard Crandall & Carl Pomerance (2005), "Prime Numbers: A Computational Perspective", Springer, 2nd edition, 236-238 - http://modular.math.washington.edu/edu/2007/spring/ent/ent-html/node81.html - http://www.cs.toronto.edu/~yuvalf/Factorization.pdf """ n = int(n) if n < 4 or B < 3: raise ValueError('pollard_pm1 should receive n > 3 and B > 2') prng = random.Random(seed + B) # computing a**lcm(1,2,3,..B) % n for B > 2 # it looks weird, but it's right: primes run [2, B] # and the answer's not right until the loop is done. for i in range(retries + 1): aM = a for p in sieve.primerange(2, B + 1): e = int(math.log(B, p)) aM = pow(aM, pow(p, e), n) g = igcd(aM - 1, n) if 1 < g < n: return int(g) # get a new a: # since the exponent, lcm(1..B), is even, if we allow 'a' to be 'n-1' # then (n - 1)**even % n will be 1 which will give a g of 0 and 1 will # give a zero, too, so we set the range as [2, n-2]. Some references # say 'a' should be coprime to n, but either will detect factors. a = prng.randint(2, n - 2)
def pollard_rho(n, s=2, a=1, retries=5, seed=1234, max_steps=None, F=None): r""" Use Pollard's rho method to try to extract a nontrivial factor of ``n``. The returned factor may be a composite number. If no factor is found, ``None`` is returned. The algorithm generates pseudo-random values of x with a generator function, replacing x with F(x). If F is not supplied then the function x**2 + ``a`` is used. The first value supplied to F(x) is ``s``. Upon failure (if ``retries`` is > 0) a new ``a`` and ``s`` will be supplied; the ``a`` will be ignored if F was supplied. The sequence of numbers generated by such functions generally have a a lead-up to some number and then loop around back to that number and begin to repeat the sequence, e.g. 1, 2, 3, 4, 5, 3, 4, 5 -- this leader and loop look a bit like the Greek letter rho, and thus the name, 'rho'. For a given function, very different leader-loop values can be obtained so it is a good idea to allow for retries: >>> from sympy.ntheory.generate import cycle_length >>> n = 16843009 >>> F = lambda x:(2048*pow(x, 2, n) + 32767) % n >>> for s in range(5): ... print('loop length = %4i; leader length = %3i' % next(cycle_length(F, s))) ... loop length = 2489; leader length = 42 loop length = 78; leader length = 120 loop length = 1482; leader length = 99 loop length = 1482; leader length = 285 loop length = 1482; leader length = 100 Here is an explicit example where there is a two element leadup to a sequence of 3 numbers (11, 14, 4) that then repeat: >>> x=2 >>> for i in range(9): ... x=(x**2+12)%17 ... print(x) ... 16 13 11 14 4 11 14 4 11 >>> next(cycle_length(lambda x: (x**2+12)%17, 2)) (3, 2) >>> list(cycle_length(lambda x: (x**2+12)%17, 2, values=True)) [16, 13, 11, 14, 4] Instead of checking the differences of all generated values for a gcd with n, only the kth and 2*kth numbers are checked, e.g. 1st and 2nd, 2nd and 4th, 3rd and 6th until it has been detected that the loop has been traversed. Loops may be many thousands of steps long before rho finds a factor or reports failure. If ``max_steps`` is specified, the iteration is cancelled with a failure after the specified number of steps. Examples ======== >>> from sympy import pollard_rho >>> n=16843009 >>> F=lambda x:(2048*pow(x,2,n) + 32767) % n >>> pollard_rho(n, F=F) 257 Use the default setting with a bad value of ``a`` and no retries: >>> pollard_rho(n, a=n-2, retries=0) If retries is > 0 then perhaps the problem will correct itself when new values are generated for a: >>> pollard_rho(n, a=n-2, retries=1) 257 References ========== - Richard Crandall & Carl Pomerance (2005), "Prime Numbers: A Computational Perspective", Springer, 2nd edition, 229-231 """ n = int(n) if n < 5: raise ValueError('pollard_rho should receive n > 4') prng = random.Random(seed + retries) V = s for i in range(retries + 1): U = V if not F: F = lambda x: (pow(x, 2, n) + a) % n j = 0 while 1: if max_steps and (j > max_steps): break j += 1 U = F(U) V = F(F(V)) # V is 2x further along than U g = igcd(U - V, n) if g == 1: continue if g == n: break return int(g) V = prng.randint(0, n - 1) a = prng.randint(1, n - 3) # for x**2 + a, a%n should not be 0 or -2 F = None return None
def perfect_power(n, candidates=None, big=True, factor=True): """ Return ``(b, e)`` such that ``n`` == ``b**e`` if ``n`` is a perfect power; otherwise return ``False``. By default, the base is recursively decomposed and the exponents collected so the largest possible ``e`` is sought. If ``big=False`` then the smallest possible ``e`` (thus prime) will be chosen. If ``candidates`` for exponents are given, they are assumed to be sorted and the first one that is larger than the computed maximum will signal failure for the routine. If ``factor=True`` then simultaneous factorization of n is attempted since finding a factor indicates the only possible root for n. This is True by default since only a few small factors will be tested in the course of searching for the perfect power. Examples ======== >>> from sympy import perfect_power >>> perfect_power(16) (2, 4) >>> perfect_power(16, big = False) (4, 2) """ n = int(n) if n < 3: return False logn = math.log(n, 2) max_possible = int(logn) + 2 # only check values less than this not_square = n % 10 in [2, 3, 7, 8] # squares cannot end in 2, 3, 7, 8 if not candidates: candidates = primerange(2 + not_square, max_possible) afactor = 2 + n % 2 for e in candidates: if e < 3: if e == 1 or e == 2 and not_square: continue if e > max_possible: return False # see if there is a factor present if factor: if n % afactor == 0: # find what the potential power is if afactor == 2: e = trailing(n) else: e = multiplicity(afactor, n) # if it's a trivial power we are done if e == 1: return False # maybe the bth root of n is exact r, exact = integer_nthroot(n, e) if not exact: # then remove this factor and check to see if # any of e's factors are a common exponent; if # not then it's not a perfect power n //= afactor**e m = perfect_power(n, candidates=primefactors(e), big=big) if m is False: return False else: r, m = m # adjust the two exponents so the bases can # be combined g = igcd(m, e) if g == 1: return False m //= g e //= g r, e = r**m * afactor**e, g if not big: e0 = primefactors(e) if len(e0) > 1 or e0[0] != e: e0 = e0[0] r, e = r**(e // e0), e0 return r, e else: # get the next factor ready for the next pass through the loop afactor = nextprime(afactor) # Weed out downright impossible candidates if logn / e < 40: b = 2.0**(logn / e) if abs(int(b + 0.5) - b) > 0.01: continue # now see if the plausible e makes a perfect power r, exact = integer_nthroot(n, e) if exact: if big: m = perfect_power(r, big=big, factor=factor) if m is not False: r, e = m[0], e * m[1] return int(r), e else: return False
def test_igcd(): assert igcd(0, 0) == 0 assert igcd(0, 1) == 1 assert igcd(1, 0) == 1 assert igcd(0, 7) == 7 assert igcd(7, 0) == 7 assert igcd(7, 1) == 1 assert igcd(1, 7) == 1 assert igcd(-1, 0) == 1 assert igcd(0, -1) == 1 assert igcd(-1, -1) == 1 assert igcd(-1, 7) == 1 assert igcd(7, -1) == 1 assert igcd(8, 2) == 2 assert igcd(4, 8) == 4 assert igcd(8, 16) == 8 assert igcd(7, -3) == 1 assert igcd(-7, 3) == 1 assert igcd(-7, -3) == 1
def primitive_root(p): """ Returns the smallest primitive root or None Parameters ========== p : positive integer Examples ======== >>> from sympy.ntheory.residue_ntheory import primitive_root >>> primitive_root(19) 2 References ========== .. [1] W. Stein "Elementary Number Theory" (2011), page 44 .. [2] P. Hackman "Elementary Number Theory" (2009), Chapter C """ p = as_int(p) if p < 1: raise ValueError('p is required to be positive') if p <= 2: return 1 f = factorint(p) if len(f) > 2: return None if len(f) == 2: if 2 not in f or f[2] > 1: return None # case p = 2*p1**k, p1 prime for p1, e1 in f.items(): if p1 != 2: break i = 1 while i < p: i += 2 if i % p1 == 0: continue if is_primitive_root(i, p): return i else: if 2 in f: if p == 4: return 3 return None p1, n = list(f.items())[0] if n > 1: # see Ref [2], page 81 g = primitive_root(p1) if is_primitive_root(g, p1**2): return g else: for i in range(2, g + p1 + 1): if igcd(i, p) == 1 and is_primitive_root(i, p): return i return next(_primitive_root_prime_iter(p))
def jacobi_symbol(m, n): r""" Returns the Jacobi symbol `(m / n)`. For any integer ``m`` and any positive odd integer ``n`` the Jacobi symbol is defined as the product of the Legendre symbols corresponding to the prime factors of ``n``: .. math :: \genfrac(){}{}{m}{n} = \genfrac(){}{}{m}{p^{1}}^{\alpha_1} \genfrac(){}{}{m}{p^{2}}^{\alpha_2} ... \genfrac(){}{}{m}{p^{k}}^{\alpha_k} \text{ where } n = p_1^{\alpha_1} p_2^{\alpha_2} ... p_k^{\alpha_k} Like the Legendre symbol, if the Jacobi symbol `\genfrac(){}{}{m}{n} = -1` then ``m`` is a quadratic nonresidue modulo ``n``. But, unlike the Legendre symbol, if the Jacobi symbol `\genfrac(){}{}{m}{n} = 1` then ``m`` may or may not be a quadratic residue modulo ``n``. Parameters ========== m : integer n : odd positive integer Examples ======== >>> from sympy.ntheory import jacobi_symbol, legendre_symbol >>> from sympy import Mul, S >>> jacobi_symbol(45, 77) -1 >>> jacobi_symbol(60, 121) 1 The relationship between the ``jacobi_symbol`` and ``legendre_symbol`` can be demonstrated as follows: >>> L = legendre_symbol >>> S(45).factors() {3: 2, 5: 1} >>> jacobi_symbol(7, 45) == L(7, 3)**2 * L(7, 5)**1 True See Also ======== is_quad_residue, legendre_symbol """ m, n = as_int(m), as_int(n) if n < 0 or not n % 2: raise ValueError("n should be an odd positive integer") if m < 0 or m > n: m = m % n if not m: return int(n == 1) if n == 1 or m == 1: return 1 if igcd(m, n) != 1: return 0 j = 1 if m < 0: m = -m if n % 4 == 3: j = -j while m != 0: while m % 2 == 0 and m > 0: m >>= 1 if n % 8 in [3, 5]: j = -j m, n = n, m if m % 4 == 3 and n % 4 == 3: j = -j m %= n if n != 1: j = 0 return j