Spaces:
Paused
Paused
| from __future__ import annotations | |
| from sympy.external.gmpy import (gcd, lcm, invert, sqrt, jacobi, | |
| bit_scan1, remove) | |
| from sympy.polys import Poly | |
| from sympy.polys.domains import ZZ | |
| from sympy.polys.galoistools import gf_crt1, gf_crt2, linear_congruence, gf_csolve | |
| from .primetest import isprime | |
| from .generate import primerange | |
| from .factor_ import factorint, _perfect_power | |
| from .modular import crt | |
| from sympy.utilities.decorator import deprecated | |
| from sympy.utilities.memoization import recurrence_memo | |
| from sympy.utilities.misc import as_int | |
| from sympy.utilities.iterables import iproduct | |
| from sympy.core.random import _randint, randint | |
| from itertools import product | |
| def n_order(a, n): | |
| r""" Returns the order of ``a`` modulo ``n``. | |
| Explanation | |
| =========== | |
| The order of ``a`` modulo ``n`` is the smallest integer | |
| ``k`` such that `a^k` leaves a remainder of 1 with ``n``. | |
| Parameters | |
| ========== | |
| a : integer | |
| n : integer, n > 1. a and n should be relatively prime | |
| Returns | |
| ======= | |
| int : the order of ``a`` modulo ``n`` | |
| Raises | |
| ====== | |
| ValueError | |
| If `n \le 1` or `\gcd(a, n) \neq 1`. | |
| If ``a`` or ``n`` is not an integer. | |
| Examples | |
| ======== | |
| >>> from sympy.ntheory import n_order | |
| >>> n_order(3, 7) | |
| 6 | |
| >>> n_order(4, 7) | |
| 3 | |
| See Also | |
| ======== | |
| is_primitive_root | |
| We say that ``a`` is a primitive root of ``n`` | |
| when the order of ``a`` modulo ``n`` equals ``totient(n)`` | |
| """ | |
| a, n = as_int(a), as_int(n) | |
| if n <= 1: | |
| raise ValueError("n should be an integer greater than 1") | |
| a = a % n | |
| # Trivial | |
| if a == 1: | |
| return 1 | |
| if gcd(a, n) != 1: | |
| raise ValueError("The two numbers should be relatively prime") | |
| a_order = 1 | |
| for p, e in factorint(n).items(): | |
| pe = p**e | |
| pe_order = (p - 1) * p**(e - 1) | |
| factors = factorint(p - 1) | |
| if e > 1: | |
| factors[p] = e - 1 | |
| order = 1 | |
| for px, ex in factors.items(): | |
| x = pow(a, pe_order // px**ex, pe) | |
| while x != 1: | |
| x = pow(x, px, pe) | |
| order *= px | |
| a_order = lcm(a_order, order) | |
| return int(a_order) | |
| def _primitive_root_prime_iter(p): | |
| r""" Generates the primitive roots for a prime ``p``. | |
| Explanation | |
| =========== | |
| The primitive roots generated are not necessarily sorted. | |
| However, the first one is the smallest primitive root. | |
| Find the element whose order is ``p-1`` from the smaller one. | |
| If we can find the first primitive root ``g``, we can use the following theorem. | |
| .. math :: | |
| \operatorname{ord}(g^k) = \frac{\operatorname{ord}(g)}{\gcd(\operatorname{ord}(g), k)} | |
| From the assumption that `\operatorname{ord}(g)=p-1`, | |
| it is a necessary and sufficient condition for | |
| `\operatorname{ord}(g^k)=p-1` that `\gcd(p-1, k)=1`. | |
| Parameters | |
| ========== | |
| p : odd prime | |
| Yields | |
| ====== | |
| int | |
| the primitive roots of ``p`` | |
| Examples | |
| ======== | |
| >>> from sympy.ntheory.residue_ntheory import _primitive_root_prime_iter | |
| >>> sorted(_primitive_root_prime_iter(19)) | |
| [2, 3, 10, 13, 14, 15] | |
| References | |
| ========== | |
| .. [1] W. Stein "Elementary Number Theory" (2011), page 44 | |
| """ | |
| if p == 3: | |
| yield 2 | |
| return | |
| # Let p = +-1 (mod 4a). Legendre symbol (a/p) = 1, so `a` is not the primitive root. | |
| # Corollary : If p = +-1 (mod 8), then 2 is not the primitive root of p. | |
| g_min = 3 if p % 8 in [1, 7] else 2 | |
| if p < 41: | |
| # small case | |
| g = 5 if p == 23 else g_min | |
| else: | |
| v = [(p - 1) // i for i in factorint(p - 1).keys()] | |
| for g in range(g_min, p): | |
| if all(pow(g, pw, p) != 1 for pw in v): | |
| break | |
| yield g | |
| # g**k is the primitive root of p iff gcd(p - 1, k) = 1 | |
| for k in range(3, p, 2): | |
| if gcd(p - 1, k) == 1: | |
| yield pow(g, k, p) | |
| def _primitive_root_prime_power_iter(p, e): | |
| r""" Generates the primitive roots of `p^e`. | |
| Explanation | |
| =========== | |
| Let ``g`` be the primitive root of ``p``. | |
| If `g^{p-1} \not\equiv 1 \pmod{p^2}`, then ``g`` is primitive root of `p^e`. | |
| Thus, if we find a primitive root ``g`` of ``p``, | |
| then `g, g+p, g+2p, \ldots, g+(p-1)p` are primitive roots of `p^2` except one. | |
| That one satisfies `\hat{g}^{p-1} \equiv 1 \pmod{p^2}`. | |
| If ``h`` is the primitive root of `p^2`, | |
| then `h, h+p^2, h+2p^2, \ldots, h+(p^{e-2}-1)p^e` are primitive roots of `p^e`. | |
| Parameters | |
| ========== | |
| p : odd prime | |
| e : positive integer | |
| Yields | |
| ====== | |
| int | |
| the primitive roots of `p^e` | |
| Examples | |
| ======== | |
| >>> from sympy.ntheory.residue_ntheory import _primitive_root_prime_power_iter | |
| >>> sorted(_primitive_root_prime_power_iter(5, 2)) | |
| [2, 3, 8, 12, 13, 17, 22, 23] | |
| """ | |
| if e == 1: | |
| yield from _primitive_root_prime_iter(p) | |
| else: | |
| p2 = p**2 | |
| for g in _primitive_root_prime_iter(p): | |
| t = (g - pow(g, 2 - p, p2)) % p2 | |
| for k in range(0, p2, p): | |
| if k != t: | |
| yield from (g + k + m for m in range(0, p**e, p2)) | |
| def _primitive_root_prime_power2_iter(p, e): | |
| r""" Generates the primitive roots of `2p^e`. | |
| Explanation | |
| =========== | |
| If ``g`` is the primitive root of ``p**e``, | |
| then the odd one of ``g`` and ``g+p**e`` is the primitive root of ``2*p**e``. | |
| Parameters | |
| ========== | |
| p : odd prime | |
| e : positive integer | |
| Yields | |
| ====== | |
| int | |
| the primitive roots of `2p^e` | |
| Examples | |
| ======== | |
| >>> from sympy.ntheory.residue_ntheory import _primitive_root_prime_power2_iter | |
| >>> sorted(_primitive_root_prime_power2_iter(5, 2)) | |
| [3, 13, 17, 23, 27, 33, 37, 47] | |
| """ | |
| for g in _primitive_root_prime_power_iter(p, e): | |
| if g % 2 == 1: | |
| yield g | |
| else: | |
| yield g + p**e | |
| def primitive_root(p, smallest=True): | |
| r""" Returns a primitive root of ``p`` or None. | |
| Explanation | |
| =========== | |
| For the definition of primitive root, | |
| see the explanation of ``is_primitive_root``. | |
| The primitive root of ``p`` exist only for | |
| `p = 2, 4, q^e, 2q^e` (``q`` is an odd prime). | |
| Now, if we know the primitive root of ``q``, | |
| we can calculate the primitive root of `q^e`, | |
| and if we know the primitive root of `q^e`, | |
| we can calculate the primitive root of `2q^e`. | |
| When there is no need to find the smallest primitive root, | |
| this property can be used to obtain a fast primitive root. | |
| On the other hand, when we want the smallest primitive root, | |
| we naively determine whether it is a primitive root or not. | |
| Parameters | |
| ========== | |
| p : integer, p > 1 | |
| smallest : if True the smallest primitive root is returned or None | |
| Returns | |
| ======= | |
| int | None : | |
| If the primitive root exists, return the primitive root of ``p``. | |
| If not, return None. | |
| Raises | |
| ====== | |
| ValueError | |
| If `p \le 1` or ``p`` is not an integer. | |
| Examples | |
| ======== | |
| >>> from sympy.ntheory.residue_ntheory import primitive_root | |
| >>> primitive_root(19) | |
| 2 | |
| >>> primitive_root(21) is None | |
| True | |
| >>> primitive_root(50, smallest=False) | |
| 27 | |
| See Also | |
| ======== | |
| is_primitive_root | |
| 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 should be an integer greater than 1") | |
| if p <= 4: | |
| return p - 1 | |
| p_even = p % 2 == 0 | |
| if not p_even: | |
| q = p # p is odd | |
| elif p % 4: | |
| q = p//2 # p had 1 factor of 2 | |
| else: | |
| return None # p had more than one factor of 2 | |
| if isprime(q): | |
| e = 1 | |
| else: | |
| m = _perfect_power(q, 3) | |
| if not m: | |
| return None | |
| q, e = m | |
| if not isprime(q): | |
| return None | |
| if not smallest: | |
| if p_even: | |
| return next(_primitive_root_prime_power2_iter(q, e)) | |
| return next(_primitive_root_prime_power_iter(q, e)) | |
| if p_even: | |
| for i in range(3, p, 2): | |
| if i % q and is_primitive_root(i, p): | |
| return i | |
| g = next(_primitive_root_prime_iter(q)) | |
| if e == 1 or pow(g, q - 1, q**2) != 1: | |
| return g | |
| for i in range(g + 1, p): | |
| if i % q and is_primitive_root(i, p): | |
| return i | |
| def is_primitive_root(a, p): | |
| r""" Returns True if ``a`` is a primitive root of ``p``. | |
| Explanation | |
| =========== | |
| ``a`` is said to be the primitive root of ``p`` if `\gcd(a, p) = 1` and | |
| `\phi(p)` is the smallest positive number s.t. | |
| `a^{\phi(p)} \equiv 1 \pmod{p}`. | |
| where `\phi(p)` is Euler's totient function. | |
| The primitive root of ``p`` exist only for | |
| `p = 2, 4, q^e, 2q^e` (``q`` is an odd prime). | |
| Hence, if it is not such a ``p``, it returns False. | |
| To determine the primitive root, we need to know | |
| the prime factorization of ``q-1``. | |
| The hardness of the determination depends on this complexity. | |
| Parameters | |
| ========== | |
| a : integer | |
| p : integer, ``p`` > 1. ``a`` and ``p`` should be relatively prime | |
| Returns | |
| ======= | |
| bool : If True, ``a`` is the primitive root of ``p``. | |
| Raises | |
| ====== | |
| ValueError | |
| If `p \le 1` or `\gcd(a, p) \neq 1`. | |
| If ``a`` or ``p`` is not an integer. | |
| Examples | |
| ======== | |
| >>> from sympy.functions.combinatorial.numbers import totient | |
| >>> from sympy.ntheory import is_primitive_root, n_order | |
| >>> 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 | |
| See Also | |
| ======== | |
| primitive_root | |
| """ | |
| a, p = as_int(a), as_int(p) | |
| if p <= 1: | |
| raise ValueError("p should be an integer greater than 1") | |
| a = a % p | |
| if gcd(a, p) != 1: | |
| raise ValueError("The two numbers should be relatively prime") | |
| # Primitive root of p exist only for | |
| # p = 2, 4, q**e, 2*q**e (q is odd prime) | |
| if p <= 4: | |
| # The primitive root is only p-1. | |
| return a == p - 1 | |
| if p % 2: | |
| q = p # p is odd | |
| elif p % 4: | |
| q = p//2 # p had 1 factor of 2 | |
| else: | |
| return False # p had more than one factor of 2 | |
| if isprime(q): | |
| group_order = q - 1 | |
| factors = factorint(q - 1).keys() | |
| else: | |
| m = _perfect_power(q, 3) | |
| if not m: | |
| return False | |
| q, e = m | |
| if not isprime(q): | |
| return False | |
| group_order = q**(e - 1)*(q - 1) | |
| factors = set(factorint(q - 1).keys()) | |
| factors.add(q) | |
| return all(pow(a, group_order // prime, p) != 1 for prime in factors) | |
| def _sqrt_mod_tonelli_shanks(a, p): | |
| """ | |
| Returns the square root in the case of ``p`` prime with ``p == 1 (mod 8)`` | |
| Assume that the root exists. | |
| Parameters | |
| ========== | |
| a : int | |
| p : int | |
| prime number. should be ``p % 8 == 1`` | |
| Returns | |
| ======= | |
| int : Generally, there are two roots, but only one is returned. | |
| Which one is returned is random. | |
| Examples | |
| ======== | |
| >>> from sympy.ntheory.residue_ntheory import _sqrt_mod_tonelli_shanks | |
| >>> _sqrt_mod_tonelli_shanks(2, 17) in [6, 11] | |
| True | |
| References | |
| ========== | |
| .. [1] Carl Pomerance, Richard Crandall, Prime Numbers: A Computational Perspective, | |
| 2nd Edition (2005), page 101, ISBN:978-0387252827 | |
| """ | |
| s = bit_scan1(p - 1) | |
| t = p >> s | |
| # find a non-quadratic residue | |
| if p % 12 == 5: | |
| # Legendre symbol (3/p) == -1 if p % 12 in [5, 7] | |
| d = 3 | |
| elif p % 5 in [2, 3]: | |
| # Legendre symbol (5/p) == -1 if p % 5 in [2, 3] | |
| d = 5 | |
| else: | |
| while 1: | |
| d = randint(6, p - 1) | |
| if jacobi(d, p) == -1: | |
| break | |
| #assert legendre_symbol(d, p) == -1 | |
| A = pow(a, t, p) | |
| D = pow(d, t, p) | |
| m = 0 | |
| for i in range(s): | |
| adm = A*pow(D, m, p) % p | |
| adm = pow(adm, 2**(s - 1 - i), p) | |
| if adm % p == p - 1: | |
| m += 2**i | |
| #assert A*pow(D, m, p) % p == 1 | |
| x = pow(a, (t + 1)//2, p)*pow(D, m//2, p) % p | |
| return x | |
| def sqrt_mod(a, p, all_roots=False): | |
| """ | |
| Find a root of ``x**2 = a mod p``. | |
| Parameters | |
| ========== | |
| a : integer | |
| p : positive integer | |
| all_roots : if True the list of roots is returned or None | |
| Notes | |
| ===== | |
| If there is no root it is returned None; else the returned root | |
| is less or equal to ``p // 2``; in general is not the smallest one. | |
| It is returned ``p // 2`` only if it is the only root. | |
| Use ``all_roots`` only when it is expected that all the roots fit | |
| in memory; otherwise use ``sqrt_mod_iter``. | |
| Examples | |
| ======== | |
| >>> from sympy.ntheory import sqrt_mod | |
| >>> sqrt_mod(11, 43) | |
| 21 | |
| >>> sqrt_mod(17, 32, True) | |
| [7, 9, 23, 25] | |
| """ | |
| if all_roots: | |
| return sorted(sqrt_mod_iter(a, p)) | |
| p = abs(as_int(p)) | |
| halfp = p // 2 | |
| x = None | |
| for r in sqrt_mod_iter(a, p): | |
| if r < halfp: | |
| return r | |
| elif r > halfp: | |
| return p - r | |
| else: | |
| x = r | |
| return x | |
| def sqrt_mod_iter(a, p, domain=int): | |
| """ | |
| Iterate over solutions to ``x**2 = a mod p``. | |
| Parameters | |
| ========== | |
| a : integer | |
| p : positive integer | |
| domain : integer domain, ``int``, ``ZZ`` or ``Integer`` | |
| Examples | |
| ======== | |
| >>> from sympy.ntheory.residue_ntheory import sqrt_mod_iter | |
| >>> list(sqrt_mod_iter(11, 43)) | |
| [21, 22] | |
| See Also | |
| ======== | |
| sqrt_mod : Same functionality, but you want a sorted list or only one solution. | |
| """ | |
| a, p = as_int(a), abs(as_int(p)) | |
| v = [] | |
| pv = [] | |
| _product = product | |
| for px, ex in factorint(p).items(): | |
| if a % px: | |
| # `len(rx)` is at most 4 | |
| rx = _sqrt_mod_prime_power(a, px, ex) | |
| else: | |
| # `len(list(rx))` can be assumed to be large. | |
| # The `itertools.product` is disadvantageous in terms of memory usage. | |
| # It is also inferior to iproduct in speed if not all Cartesian products are needed. | |
| rx = _sqrt_mod1(a, px, ex) | |
| _product = iproduct | |
| if not rx: | |
| return | |
| v.append(rx) | |
| pv.append(px**ex) | |
| if len(v) == 1: | |
| yield from map(domain, v[0]) | |
| else: | |
| mm, e, s = gf_crt1(pv, ZZ) | |
| for vx in _product(*v): | |
| yield domain(gf_crt2(vx, pv, mm, e, s, ZZ)) | |
| def _sqrt_mod_prime_power(a, p, k): | |
| """ | |
| Find the solutions to ``x**2 = a mod p**k`` when ``a % p != 0``. | |
| If no solution exists, return ``None``. | |
| Solutions are returned in an ascending list. | |
| Parameters | |
| ========== | |
| a : integer | |
| p : prime number | |
| k : positive integer | |
| Examples | |
| ======== | |
| >>> from sympy.ntheory.residue_ntheory import _sqrt_mod_prime_power | |
| >>> _sqrt_mod_prime_power(11, 43, 1) | |
| [21, 22] | |
| References | |
| ========== | |
| .. [1] P. Hackman "Elementary Number Theory" (2009), page 160 | |
| .. [2] http://www.numbertheory.org/php/squareroot.html | |
| .. [3] [Gathen99]_ | |
| """ | |
| pk = p**k | |
| a = a % pk | |
| if p == 2: | |
| # see Ref.[2] | |
| if a % 8 != 1: | |
| return None | |
| # Trivial | |
| if k <= 3: | |
| return list(range(1, pk, 2)) | |
| r = 1 | |
| # r is one of the solutions to x**2 - a = 0 (mod 2**3). | |
| # Hensel lift them to solutions of x**2 - a = 0 (mod 2**k) | |
| # if r**2 - a = 0 mod 2**nx but not mod 2**(nx+1) | |
| # then r + 2**(nx - 1) is a root mod 2**(nx+1) | |
| for nx in range(3, k): | |
| if ((r**2 - a) >> nx) % 2: | |
| r += 1 << (nx - 1) | |
| # r is a solution of x**2 - a = 0 (mod 2**k), and | |
| # there exist other solutions -r, r+h, -(r+h), and these are all solutions. | |
| h = 1 << (k - 1) | |
| return sorted([r, pk - r, (r + h) % pk, -(r + h) % pk]) | |
| # If the Legendre symbol (a/p) is not 1, no solution exists. | |
| if jacobi(a, p) != 1: | |
| return None | |
| if p % 4 == 3: | |
| res = pow(a, (p + 1) // 4, p) | |
| elif p % 8 == 5: | |
| res = pow(a, (p + 3) // 8, p) | |
| if pow(res, 2, p) != a % p: | |
| res = res * pow(2, (p - 1) // 4, p) % p | |
| else: | |
| res = _sqrt_mod_tonelli_shanks(a, p) | |
| if k > 1: | |
| # Hensel lifting with Newton iteration, see Ref.[3] chapter 9 | |
| # with f(x) = x**2 - a; one has f'(a) != 0 (mod p) for p != 2 | |
| px = p | |
| for _ in range(k.bit_length() - 1): | |
| px = px**2 | |
| frinv = invert(2*res, px) | |
| res = (res - (res**2 - a)*frinv) % px | |
| if k & (k - 1): # If k is not a power of 2 | |
| frinv = invert(2*res, pk) | |
| res = (res - (res**2 - a)*frinv) % pk | |
| return sorted([res, pk - res]) | |
| def _sqrt_mod1(a, p, n): | |
| """ | |
| Find solution to ``x**2 == a mod p**n`` when ``a % p == 0``. | |
| If no solution exists, return ``None``. | |
| Parameters | |
| ========== | |
| a : integer | |
| p : prime number, p must divide a | |
| n : positive integer | |
| References | |
| ========== | |
| .. [1] http://www.numbertheory.org/php/squareroot.html | |
| """ | |
| pn = p**n | |
| a = a % pn | |
| if a == 0: | |
| # case gcd(a, p**k) = p**n | |
| return range(0, pn, p**((n + 1) // 2)) | |
| # case gcd(a, p**k) = p**r, r < n | |
| a, r = remove(a, p) | |
| if r % 2 == 1: | |
| return None | |
| res = _sqrt_mod_prime_power(a, p, n - r) | |
| if res is None: | |
| return None | |
| m = r // 2 | |
| return (x for rx in res for x in range(rx*p**m, pn, p**(n - m))) | |
| def is_quad_residue(a, p): | |
| """ | |
| Returns True if ``a`` (mod ``p``) is in the set of squares mod ``p``, | |
| i.e a % p in set([i**2 % p for i in range(p)]). | |
| Parameters | |
| ========== | |
| a : integer | |
| p : positive integer | |
| Returns | |
| ======= | |
| bool : If True, ``x**2 == a (mod p)`` has solution. | |
| Raises | |
| ====== | |
| ValueError | |
| If ``a``, ``p`` is not integer. | |
| If ``p`` is not positive. | |
| Examples | |
| ======== | |
| >>> from sympy.ntheory import is_quad_residue | |
| >>> is_quad_residue(21, 100) | |
| True | |
| Indeed, ``pow(39, 2, 100)`` would be 21. | |
| >>> is_quad_residue(21, 120) | |
| False | |
| That is, for any integer ``x``, ``pow(x, 2, 120)`` is not 21. | |
| If ``p`` is an odd | |
| prime, an iterative method is used to make the determination: | |
| >>> from sympy.ntheory import is_quad_residue | |
| >>> sorted(set([i**2 % 7 for i in range(7)])) | |
| [0, 1, 2, 4] | |
| >>> [j for j in range(7) if is_quad_residue(j, 7)] | |
| [0, 1, 2, 4] | |
| See Also | |
| ======== | |
| legendre_symbol, jacobi_symbol, sqrt_mod | |
| """ | |
| a, p = as_int(a), as_int(p) | |
| if p < 1: | |
| raise ValueError('p must be > 0') | |
| a %= p | |
| if a < 2 or p < 3: | |
| return True | |
| # Since we want to compute the Jacobi symbol, | |
| # we separate p into the odd part and the rest. | |
| t = bit_scan1(p) | |
| if t: | |
| # The existence of a solution to a power of 2 is determined | |
| # using the logic of `p==2` in `_sqrt_mod_prime_power` and `_sqrt_mod1`. | |
| a_ = a % (1 << t) | |
| if a_: | |
| r = bit_scan1(a_) | |
| if r % 2 or (a_ >> r) & 6: | |
| return False | |
| p >>= t | |
| a %= p | |
| if a < 2 or p < 3: | |
| return True | |
| # If Jacobi symbol is -1 or p is prime, can be determined by Jacobi symbol only | |
| j = jacobi(a, p) | |
| if j == -1 or isprime(p): | |
| return j == 1 | |
| # Checks if `x**2 = a (mod p)` has a solution | |
| for px, ex in factorint(p).items(): | |
| if a % px: | |
| if jacobi(a, px) != 1: | |
| return False | |
| else: | |
| a_ = a % px**ex | |
| if a_ == 0: | |
| continue | |
| a_, r = remove(a_, px) | |
| if r % 2 or jacobi(a_, px) != 1: | |
| return False | |
| return True | |
| def is_nthpow_residue(a, n, m): | |
| """ | |
| Returns True if ``x**n == a (mod m)`` has solutions. | |
| References | |
| ========== | |
| .. [1] P. Hackman "Elementary Number Theory" (2009), page 76 | |
| """ | |
| a = a % m | |
| a, n, m = as_int(a), as_int(n), as_int(m) | |
| if m <= 0: | |
| raise ValueError('m must be > 0') | |
| if n < 0: | |
| raise ValueError('n must be >= 0') | |
| if n == 0: | |
| if m == 1: | |
| return False | |
| return a == 1 | |
| if a == 0: | |
| return True | |
| if n == 1: | |
| return True | |
| if n == 2: | |
| return is_quad_residue(a, m) | |
| return all(_is_nthpow_residue_bign_prime_power(a, n, p, e) | |
| for p, e in factorint(m).items()) | |
| def _is_nthpow_residue_bign_prime_power(a, n, p, k): | |
| r""" | |
| Returns True if `x^n = a \pmod{p^k}` has solutions for `n > 2`. | |
| Parameters | |
| ========== | |
| a : positive integer | |
| n : integer, n > 2 | |
| p : prime number | |
| k : positive integer | |
| """ | |
| while a % p == 0: | |
| a %= pow(p, k) | |
| if not a: | |
| return True | |
| a, mu = remove(a, p) | |
| if mu % n: | |
| return False | |
| k -= mu | |
| if p != 2: | |
| f = p**(k - 1)*(p - 1) # f = totient(p**k) | |
| return pow(a, f // gcd(f, n), pow(p, k)) == 1 | |
| if n & 1: | |
| return True | |
| c = min(bit_scan1(n) + 2, k) | |
| return a % pow(2, c) == 1 | |
| def _nthroot_mod1(s, q, p, all_roots): | |
| """ | |
| Root of ``x**q = s mod p``, ``p`` prime and ``q`` divides ``p - 1``. | |
| Assume that the root exists. | |
| Parameters | |
| ========== | |
| s : integer | |
| q : integer, n > 2. ``q`` divides ``p - 1``. | |
| p : prime number | |
| all_roots : if False returns the smallest root, else the list of roots | |
| Returns | |
| ======= | |
| list[int] | int : | |
| Root of ``x**q = s mod p``. If ``all_roots == True``, | |
| returned ascending list. otherwise, returned an int. | |
| Examples | |
| ======== | |
| >>> from sympy.ntheory.residue_ntheory import _nthroot_mod1 | |
| >>> _nthroot_mod1(5, 3, 13, False) | |
| 7 | |
| >>> _nthroot_mod1(13, 4, 17, True) | |
| [3, 5, 12, 14] | |
| References | |
| ========== | |
| .. [1] A. M. Johnston, A Generalized qth Root Algorithm, | |
| ACM-SIAM Symposium on Discrete Algorithms (1999), pp. 929-930 | |
| """ | |
| g = next(_primitive_root_prime_iter(p)) | |
| r = s | |
| for qx, ex in factorint(q).items(): | |
| f = (p - 1) // qx**ex | |
| while f % qx == 0: | |
| f //= qx | |
| z = f*invert(-f, qx) | |
| x = (1 + z) // qx | |
| t = discrete_log(p, pow(r, f, p), pow(g, f*qx, p)) | |
| for _ in range(ex): | |
| # assert t == discrete_log(p, pow(r, f, p), pow(g, f*qx, p)) | |
| r = pow(r, x, p)*pow(g, -z*t % (p - 1), p) % p | |
| t //= qx | |
| res = [r] | |
| h = pow(g, (p - 1) // q, p) | |
| #assert pow(h, q, p) == 1 | |
| hx = r | |
| for _ in range(q - 1): | |
| hx = (hx*h) % p | |
| res.append(hx) | |
| if all_roots: | |
| res.sort() | |
| return res | |
| return min(res) | |
| def _nthroot_mod_prime_power(a, n, p, k): | |
| """ Root of ``x**n = a mod p**k``. | |
| Parameters | |
| ========== | |
| a : integer | |
| n : integer, n > 2 | |
| p : prime number | |
| k : positive integer | |
| Returns | |
| ======= | |
| list[int] : | |
| Ascending list of roots of ``x**n = a mod p**k``. | |
| If no solution exists, return ``[]``. | |
| """ | |
| if not _is_nthpow_residue_bign_prime_power(a, n, p, k): | |
| return [] | |
| a_mod_p = a % p | |
| if a_mod_p == 0: | |
| base_roots = [0] | |
| elif (p - 1) % n == 0: | |
| base_roots = _nthroot_mod1(a_mod_p, n, p, all_roots=True) | |
| else: | |
| # The roots of ``x**n - a = 0 (mod p)`` are roots of | |
| # ``gcd(x**n - a, x**(p - 1) - 1) = 0 (mod p)`` | |
| pa = n | |
| pb = p - 1 | |
| b = 1 | |
| if pa < pb: | |
| a_mod_p, pa, b, pb = b, pb, a_mod_p, pa | |
| # gcd(x**pa - a, x**pb - b) = gcd(x**pb - b, x**pc - c) | |
| # where pc = pa % pb; c = b**-q * a mod p | |
| while pb: | |
| q, pc = divmod(pa, pb) | |
| c = pow(b, -q, p) * a_mod_p % p | |
| pa, pb = pb, pc | |
| a_mod_p, b = b, c | |
| if pa == 1: | |
| base_roots = [a_mod_p] | |
| elif pa == 2: | |
| base_roots = sqrt_mod(a_mod_p, p, all_roots=True) | |
| else: | |
| base_roots = _nthroot_mod1(a_mod_p, pa, p, all_roots=True) | |
| if k == 1: | |
| return base_roots | |
| a %= p**k | |
| tot_roots = set() | |
| for root in base_roots: | |
| diff = pow(root, n - 1, p)*n % p | |
| new_base = p | |
| if diff != 0: | |
| m_inv = invert(diff, p) | |
| for _ in range(k - 1): | |
| new_base *= p | |
| tmp = pow(root, n, new_base) - a | |
| tmp *= m_inv | |
| root = (root - tmp) % new_base | |
| tot_roots.add(root) | |
| else: | |
| roots_in_base = {root} | |
| for _ in range(k - 1): | |
| new_base *= p | |
| new_roots = set() | |
| for k_ in roots_in_base: | |
| if pow(k_, n, new_base) != a % new_base: | |
| continue | |
| while k_ not in new_roots: | |
| new_roots.add(k_) | |
| k_ = (k_ + (new_base // p)) % new_base | |
| roots_in_base = new_roots | |
| tot_roots = tot_roots | roots_in_base | |
| return sorted(tot_roots) | |
| def nthroot_mod(a, n, p, all_roots=False): | |
| """ | |
| Find the solutions to ``x**n = a mod p``. | |
| Parameters | |
| ========== | |
| a : integer | |
| n : positive integer | |
| p : positive integer | |
| all_roots : if False returns the smallest root, else the list of roots | |
| Returns | |
| ======= | |
| list[int] | int | None : | |
| solutions to ``x**n = a mod p``. | |
| The table of the output type is: | |
| ========== ========== ========== | |
| all_roots has roots Returns | |
| ========== ========== ========== | |
| True Yes list[int] | |
| True No [] | |
| False Yes int | |
| False No None | |
| ========== ========== ========== | |
| Raises | |
| ====== | |
| ValueError | |
| If ``a``, ``n`` or ``p`` is not integer. | |
| If ``n`` or ``p`` is not positive. | |
| Examples | |
| ======== | |
| >>> from sympy.ntheory.residue_ntheory import nthroot_mod | |
| >>> nthroot_mod(11, 4, 19) | |
| 8 | |
| >>> nthroot_mod(11, 4, 19, True) | |
| [8, 11] | |
| >>> nthroot_mod(68, 3, 109) | |
| 23 | |
| References | |
| ========== | |
| .. [1] P. Hackman "Elementary Number Theory" (2009), page 76 | |
| """ | |
| a = a % p | |
| a, n, p = as_int(a), as_int(n), as_int(p) | |
| if n < 1: | |
| raise ValueError("n should be positive") | |
| if p < 1: | |
| raise ValueError("p should be positive") | |
| if n == 1: | |
| return [a] if all_roots else a | |
| if n == 2: | |
| return sqrt_mod(a, p, all_roots) | |
| base = [] | |
| prime_power = [] | |
| for q, e in factorint(p).items(): | |
| tot_roots = _nthroot_mod_prime_power(a, n, q, e) | |
| if not tot_roots: | |
| return [] if all_roots else None | |
| prime_power.append(q**e) | |
| base.append(sorted(tot_roots)) | |
| P, E, S = gf_crt1(prime_power, ZZ) | |
| ret = sorted(map(int, {gf_crt2(c, prime_power, P, E, S, ZZ) | |
| for c in product(*base)})) | |
| if all_roots: | |
| return ret | |
| if ret: | |
| return ret[0] | |
| def quadratic_residues(p) -> list[int]: | |
| """ | |
| Returns the list of quadratic residues. | |
| Examples | |
| ======== | |
| >>> from sympy.ntheory.residue_ntheory import quadratic_residues | |
| >>> quadratic_residues(7) | |
| [0, 1, 2, 4] | |
| """ | |
| p = as_int(p) | |
| r = {pow(i, 2, p) for i in range(p // 2 + 1)} | |
| return sorted(r) | |
| def legendre_symbol(a, p): | |
| r""" | |
| Returns the Legendre symbol `(a / p)`. | |
| .. deprecated:: 1.13 | |
| The ``legendre_symbol`` function is deprecated. Use :class:`sympy.functions.combinatorial.numbers.legendre_symbol` | |
| instead. See its documentation for more information. See | |
| :ref:`deprecated-ntheory-symbolic-functions` for details. | |
| For an integer ``a`` and an odd prime ``p``, the Legendre symbol is | |
| defined as | |
| .. math :: | |
| \genfrac(){}{}{a}{p} = \begin{cases} | |
| 0 & \text{if } p \text{ divides } a\\ | |
| 1 & \text{if } a \text{ is a quadratic residue modulo } p\\ | |
| -1 & \text{if } a \text{ is a quadratic nonresidue modulo } p | |
| \end{cases} | |
| Parameters | |
| ========== | |
| a : integer | |
| p : odd prime | |
| Examples | |
| ======== | |
| >>> from sympy.functions.combinatorial.numbers import legendre_symbol | |
| >>> [legendre_symbol(i, 7) for i in range(7)] | |
| [0, 1, 1, -1, 1, -1, -1] | |
| >>> sorted(set([i**2 % 7 for i in range(7)])) | |
| [0, 1, 2, 4] | |
| See Also | |
| ======== | |
| is_quad_residue, jacobi_symbol | |
| """ | |
| from sympy.functions.combinatorial.numbers import legendre_symbol as _legendre_symbol | |
| return _legendre_symbol(a, p) | |
| def jacobi_symbol(m, n): | |
| r""" | |
| Returns the Jacobi symbol `(m / n)`. | |
| .. deprecated:: 1.13 | |
| The ``jacobi_symbol`` function is deprecated. Use :class:`sympy.functions.combinatorial.numbers.jacobi_symbol` | |
| instead. See its documentation for more information. See | |
| :ref:`deprecated-ntheory-symbolic-functions` for details. | |
| 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.functions.combinatorial.numbers import jacobi_symbol, legendre_symbol | |
| >>> from sympy import 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 | |
| """ | |
| from sympy.functions.combinatorial.numbers import jacobi_symbol as _jacobi_symbol | |
| return _jacobi_symbol(m, n) | |
| def mobius(n): | |
| """ | |
| Mobius function maps natural number to {-1, 0, 1} | |
| .. deprecated:: 1.13 | |
| The ``mobius`` function is deprecated. Use :class:`sympy.functions.combinatorial.numbers.mobius` | |
| instead. See its documentation for more information. See | |
| :ref:`deprecated-ntheory-symbolic-functions` for details. | |
| It is defined as follows: | |
| 1) `1` if `n = 1`. | |
| 2) `0` if `n` has a squared prime factor. | |
| 3) `(-1)^k` if `n` is a square-free positive integer with `k` | |
| number of prime factors. | |
| It is an important multiplicative function in number theory | |
| and combinatorics. It has applications in mathematical series, | |
| algebraic number theory and also physics (Fermion operator has very | |
| concrete realization with Mobius Function model). | |
| Parameters | |
| ========== | |
| n : positive integer | |
| Examples | |
| ======== | |
| >>> from sympy.functions.combinatorial.numbers import mobius | |
| >>> mobius(13*7) | |
| 1 | |
| >>> mobius(1) | |
| 1 | |
| >>> mobius(13*7*5) | |
| -1 | |
| >>> mobius(13**2) | |
| 0 | |
| References | |
| ========== | |
| .. [1] https://en.wikipedia.org/wiki/M%C3%B6bius_function | |
| .. [2] Thomas Koshy "Elementary Number Theory with Applications" | |
| """ | |
| from sympy.functions.combinatorial.numbers import mobius as _mobius | |
| return _mobius(n) | |
| def _discrete_log_trial_mul(n, a, b, order=None): | |
| """ | |
| Trial multiplication algorithm for computing the discrete logarithm of | |
| ``a`` to the base ``b`` modulo ``n``. | |
| The algorithm finds the discrete logarithm using exhaustive search. This | |
| naive method is used as fallback algorithm of ``discrete_log`` when the | |
| group order is very small. | |
| Examples | |
| ======== | |
| >>> from sympy.ntheory.residue_ntheory import _discrete_log_trial_mul | |
| >>> _discrete_log_trial_mul(41, 15, 7) | |
| 3 | |
| See Also | |
| ======== | |
| discrete_log | |
| References | |
| ========== | |
| .. [1] "Handbook of applied cryptography", Menezes, A. J., Van, O. P. C., & | |
| Vanstone, S. A. (1997). | |
| """ | |
| a %= n | |
| b %= n | |
| if order is None: | |
| order = n | |
| x = 1 | |
| for i in range(order): | |
| if x == a: | |
| return i | |
| x = x * b % n | |
| raise ValueError("Log does not exist") | |
| def _discrete_log_shanks_steps(n, a, b, order=None): | |
| """ | |
| Baby-step giant-step algorithm for computing the discrete logarithm of | |
| ``a`` to the base ``b`` modulo ``n``. | |
| The algorithm is a time-memory trade-off of the method of exhaustive | |
| search. It uses `O(sqrt(m))` memory, where `m` is the group order. | |
| Examples | |
| ======== | |
| >>> from sympy.ntheory.residue_ntheory import _discrete_log_shanks_steps | |
| >>> _discrete_log_shanks_steps(41, 15, 7) | |
| 3 | |
| See Also | |
| ======== | |
| discrete_log | |
| References | |
| ========== | |
| .. [1] "Handbook of applied cryptography", Menezes, A. J., Van, O. P. C., & | |
| Vanstone, S. A. (1997). | |
| """ | |
| a %= n | |
| b %= n | |
| if order is None: | |
| order = n_order(b, n) | |
| m = sqrt(order) + 1 | |
| T = {} | |
| x = 1 | |
| for i in range(m): | |
| T[x] = i | |
| x = x * b % n | |
| z = pow(b, -m, n) | |
| x = a | |
| for i in range(m): | |
| if x in T: | |
| return i * m + T[x] | |
| x = x * z % n | |
| raise ValueError("Log does not exist") | |
| def _discrete_log_pollard_rho(n, a, b, order=None, retries=10, rseed=None): | |
| """ | |
| Pollard's Rho algorithm for computing the discrete logarithm of ``a`` to | |
| the base ``b`` modulo ``n``. | |
| It is a randomized algorithm with the same expected running time as | |
| ``_discrete_log_shanks_steps``, but requires a negligible amount of memory. | |
| Examples | |
| ======== | |
| >>> from sympy.ntheory.residue_ntheory import _discrete_log_pollard_rho | |
| >>> _discrete_log_pollard_rho(227, 3**7, 3) | |
| 7 | |
| See Also | |
| ======== | |
| discrete_log | |
| References | |
| ========== | |
| .. [1] "Handbook of applied cryptography", Menezes, A. J., Van, O. P. C., & | |
| Vanstone, S. A. (1997). | |
| """ | |
| a %= n | |
| b %= n | |
| if order is None: | |
| order = n_order(b, n) | |
| randint = _randint(rseed) | |
| for i in range(retries): | |
| aa = randint(1, order - 1) | |
| ba = randint(1, order - 1) | |
| xa = pow(b, aa, n) * pow(a, ba, n) % n | |
| c = xa % 3 | |
| if c == 0: | |
| xb = a * xa % n | |
| ab = aa | |
| bb = (ba + 1) % order | |
| elif c == 1: | |
| xb = xa * xa % n | |
| ab = (aa + aa) % order | |
| bb = (ba + ba) % order | |
| else: | |
| xb = b * xa % n | |
| ab = (aa + 1) % order | |
| bb = ba | |
| for j in range(order): | |
| c = xa % 3 | |
| if c == 0: | |
| xa = a * xa % n | |
| ba = (ba + 1) % order | |
| elif c == 1: | |
| xa = xa * xa % n | |
| aa = (aa + aa) % order | |
| ba = (ba + ba) % order | |
| else: | |
| xa = b * xa % n | |
| aa = (aa + 1) % order | |
| c = xb % 3 | |
| if c == 0: | |
| xb = a * xb % n | |
| bb = (bb + 1) % order | |
| elif c == 1: | |
| xb = xb * xb % n | |
| ab = (ab + ab) % order | |
| bb = (bb + bb) % order | |
| else: | |
| xb = b * xb % n | |
| ab = (ab + 1) % order | |
| c = xb % 3 | |
| if c == 0: | |
| xb = a * xb % n | |
| bb = (bb + 1) % order | |
| elif c == 1: | |
| xb = xb * xb % n | |
| ab = (ab + ab) % order | |
| bb = (bb + bb) % order | |
| else: | |
| xb = b * xb % n | |
| ab = (ab + 1) % order | |
| if xa == xb: | |
| r = (ba - bb) % order | |
| try: | |
| e = invert(r, order) * (ab - aa) % order | |
| if (pow(b, e, n) - a) % n == 0: | |
| return e | |
| except ZeroDivisionError: | |
| pass | |
| break | |
| raise ValueError("Pollard's Rho failed to find logarithm") | |
| def _discrete_log_is_smooth(n: int, factorbase: list): | |
| """Try to factor n with respect to a given factorbase. | |
| Upon success a list of exponents with repect to the factorbase is returned. | |
| Otherwise None.""" | |
| factors = [0]*len(factorbase) | |
| for i, p in enumerate(factorbase): | |
| while n % p == 0: # divide by p as many times as possible | |
| factors[i] += 1 | |
| n = n // p | |
| if n != 1: | |
| return None # the number factors if at the end nothing is left | |
| return factors | |
| def _discrete_log_index_calculus(n, a, b, order, rseed=None): | |
| """ | |
| Index Calculus algorithm for computing the discrete logarithm of ``a`` to | |
| the base ``b`` modulo ``n``. | |
| The group order must be given and prime. It is not suitable for small orders | |
| and the algorithm might fail to find a solution in such situations. | |
| Examples | |
| ======== | |
| >>> from sympy.ntheory.residue_ntheory import _discrete_log_index_calculus | |
| >>> _discrete_log_index_calculus(24570203447, 23859756228, 2, 12285101723) | |
| 4519867240 | |
| See Also | |
| ======== | |
| discrete_log | |
| References | |
| ========== | |
| .. [1] "Handbook of applied cryptography", Menezes, A. J., Van, O. P. C., & | |
| Vanstone, S. A. (1997). | |
| """ | |
| randint = _randint(rseed) | |
| from math import sqrt, exp, log | |
| a %= n | |
| b %= n | |
| # assert isprime(order), "The order of the base must be prime." | |
| # First choose a heuristic the bound B for the factorbase. | |
| # We have added an extra term to the asymptotic value which | |
| # is closer to the theoretical optimum for n up to 2^70. | |
| B = int(exp(0.5 * sqrt( log(n) * log(log(n)) )*( 1 + 1/log(log(n)) ))) | |
| max = 5 * B * B # expected number of trys to find a relation | |
| factorbase = list(primerange(B)) # compute the factorbase | |
| lf = len(factorbase) # length of the factorbase | |
| ordermo = order-1 | |
| abx = a | |
| for x in range(order): | |
| if abx == 1: | |
| return (order - x) % order | |
| relationa = _discrete_log_is_smooth(abx, factorbase) | |
| if relationa: | |
| relationa = [r % order for r in relationa] + [x] | |
| break | |
| abx = abx * b % n # abx = a*pow(b, x, n) % n | |
| else: | |
| raise ValueError("Index Calculus failed") | |
| relations = [None] * lf | |
| k = 1 # number of relations found | |
| kk = 0 | |
| while k < 3 * lf and kk < max: # find relations for all primes in our factor base | |
| x = randint(1,ordermo) | |
| relation = _discrete_log_is_smooth(pow(b,x,n), factorbase) | |
| if relation is None: | |
| kk += 1 | |
| continue | |
| k += 1 | |
| kk = 0 | |
| relation += [ x ] | |
| index = lf # determine the index of the first nonzero entry | |
| for i in range(lf): | |
| ri = relation[i] % order | |
| if ri> 0 and relations[i] is not None: # make this entry zero if we can | |
| for j in range(lf+1): | |
| relation[j] = (relation[j] - ri*relations[i][j]) % order | |
| else: | |
| relation[i] = ri | |
| if relation[i] > 0 and index == lf: # is this the index of the first nonzero entry? | |
| index = i | |
| if index == lf or relations[index] is not None: # the relation contains no new information | |
| continue | |
| # the relation contains new information | |
| rinv = pow(relation[index],-1,order) # normalize the first nonzero entry | |
| for j in range(index,lf+1): | |
| relation[j] = rinv * relation[j] % order | |
| relations[index] = relation | |
| for i in range(lf): # subtract the new relation from the one for a | |
| if relationa[i] > 0 and relations[i] is not None: | |
| rbi = relationa[i] | |
| for j in range(lf+1): | |
| relationa[j] = (relationa[j] - rbi*relations[i][j]) % order | |
| if relationa[i] > 0: # the index of the first nonzero entry | |
| break # we do not need to reduce further at this point | |
| else: # all unkowns are gone | |
| #print(f"Success after {k} relations out of {lf}") | |
| x = (order -relationa[lf]) % order | |
| if pow(b,x,n) == a: | |
| return x | |
| raise ValueError("Index Calculus failed") | |
| raise ValueError("Index Calculus failed") | |
| def _discrete_log_pohlig_hellman(n, a, b, order=None, order_factors=None): | |
| """ | |
| Pohlig-Hellman algorithm for computing the discrete logarithm of ``a`` to | |
| the base ``b`` modulo ``n``. | |
| In order to compute the discrete logarithm, the algorithm takes advantage | |
| of the factorization of the group order. It is more efficient when the | |
| group order factors into many small primes. | |
| Examples | |
| ======== | |
| >>> from sympy.ntheory.residue_ntheory import _discrete_log_pohlig_hellman | |
| >>> _discrete_log_pohlig_hellman(251, 210, 71) | |
| 197 | |
| See Also | |
| ======== | |
| discrete_log | |
| References | |
| ========== | |
| .. [1] "Handbook of applied cryptography", Menezes, A. J., Van, O. P. C., & | |
| Vanstone, S. A. (1997). | |
| """ | |
| from .modular import crt | |
| a %= n | |
| b %= n | |
| if order is None: | |
| order = n_order(b, n) | |
| if order_factors is None: | |
| order_factors = factorint(order) | |
| l = [0] * len(order_factors) | |
| for i, (pi, ri) in enumerate(order_factors.items()): | |
| for j in range(ri): | |
| aj = pow(a * pow(b, -l[i], n), order // pi**(j + 1), n) | |
| bj = pow(b, order // pi, n) | |
| cj = discrete_log(n, aj, bj, pi, True) | |
| l[i] += cj * pi**j | |
| d, _ = crt([pi**ri for pi, ri in order_factors.items()], l) | |
| return d | |
| def discrete_log(n, a, b, order=None, prime_order=None): | |
| """ | |
| Compute the discrete logarithm of ``a`` to the base ``b`` modulo ``n``. | |
| This is a recursive function to reduce the discrete logarithm problem in | |
| cyclic groups of composite order to the problem in cyclic groups of prime | |
| order. | |
| It employs different algorithms depending on the problem (subgroup order | |
| size, prime order or not): | |
| * Trial multiplication | |
| * Baby-step giant-step | |
| * Pollard's Rho | |
| * Index Calculus | |
| * Pohlig-Hellman | |
| Examples | |
| ======== | |
| >>> from sympy.ntheory import discrete_log | |
| >>> discrete_log(41, 15, 7) | |
| 3 | |
| References | |
| ========== | |
| .. [1] https://mathworld.wolfram.com/DiscreteLogarithm.html | |
| .. [2] "Handbook of applied cryptography", Menezes, A. J., Van, O. P. C., & | |
| Vanstone, S. A. (1997). | |
| """ | |
| from math import sqrt, log | |
| n, a, b = as_int(n), as_int(a), as_int(b) | |
| if order is None: | |
| # Compute the order and its factoring in one pass | |
| # order = totient(n), factors = factorint(order) | |
| factors = {} | |
| for px, kx in factorint(n).items(): | |
| if kx > 1: | |
| if px in factors: | |
| factors[px] += kx - 1 | |
| else: | |
| factors[px] = kx - 1 | |
| for py, ky in factorint(px - 1).items(): | |
| if py in factors: | |
| factors[py] += ky | |
| else: | |
| factors[py] = ky | |
| order = 1 | |
| for px, kx in factors.items(): | |
| order *= px**kx | |
| # Now the `order` is the order of the group and factors = factorint(order) | |
| # The order of `b` divides the order of the group. | |
| order_factors = {} | |
| for p, e in factors.items(): | |
| i = 0 | |
| for _ in range(e): | |
| if pow(b, order // p, n) == 1: | |
| order //= p | |
| i += 1 | |
| else: | |
| break | |
| if i < e: | |
| order_factors[p] = e - i | |
| if prime_order is None: | |
| prime_order = isprime(order) | |
| if order < 1000: | |
| return _discrete_log_trial_mul(n, a, b, order) | |
| elif prime_order: | |
| # Shanks and Pollard rho are O(sqrt(order)) while index calculus is O(exp(2*sqrt(log(n)log(log(n))))) | |
| # we compare the expected running times to determine the algorithmus which is expected to be faster | |
| if 4*sqrt(log(n)*log(log(n))) < log(order) - 10: # the number 10 was determined experimental | |
| return _discrete_log_index_calculus(n, a, b, order) | |
| elif order < 1000000000000: | |
| # Shanks seems typically faster, but uses O(sqrt(order)) memory | |
| return _discrete_log_shanks_steps(n, a, b, order) | |
| return _discrete_log_pollard_rho(n, a, b, order) | |
| return _discrete_log_pohlig_hellman(n, a, b, order, order_factors) | |
| def quadratic_congruence(a, b, c, n): | |
| r""" | |
| Find the solutions to `a x^2 + b x + c \equiv 0 \pmod{n}`. | |
| Parameters | |
| ========== | |
| a : int | |
| b : int | |
| c : int | |
| n : int | |
| A positive integer. | |
| Returns | |
| ======= | |
| list[int] : | |
| A sorted list of solutions. If no solution exists, ``[]``. | |
| Examples | |
| ======== | |
| >>> from sympy.ntheory.residue_ntheory import quadratic_congruence | |
| >>> quadratic_congruence(2, 5, 3, 7) # 2x^2 + 5x + 3 = 0 (mod 7) | |
| [2, 6] | |
| >>> quadratic_congruence(8, 6, 4, 15) # No solution | |
| [] | |
| See Also | |
| ======== | |
| polynomial_congruence : Solve the polynomial congruence | |
| """ | |
| a = as_int(a) | |
| b = as_int(b) | |
| c = as_int(c) | |
| n = as_int(n) | |
| if n <= 1: | |
| raise ValueError("n should be an integer greater than 1") | |
| a %= n | |
| b %= n | |
| c %= n | |
| if a == 0: | |
| return linear_congruence(b, -c, n) | |
| if n == 2: | |
| # assert a == 1 | |
| roots = [] | |
| if c == 0: | |
| roots.append(0) | |
| if (b + c) % 2: | |
| roots.append(1) | |
| return roots | |
| if gcd(2*a, n) == 1: | |
| inv_a = invert(a, n) | |
| b *= inv_a | |
| c *= inv_a | |
| if b % 2: | |
| b += n | |
| b >>= 1 | |
| return sorted((i - b) % n for i in sqrt_mod_iter(b**2 - c, n)) | |
| res = set() | |
| for i in sqrt_mod_iter(b**2 - 4*a*c, 4*a*n): | |
| res.update(j % n for j in linear_congruence(2*a, i - b, 4*a*n)) | |
| return sorted(res) | |
| def _valid_expr(expr): | |
| """ | |
| return coefficients of expr if it is a univariate polynomial | |
| with integer coefficients else raise a ValueError. | |
| """ | |
| if not expr.is_polynomial(): | |
| raise ValueError("The expression should be a polynomial") | |
| polynomial = Poly(expr) | |
| if not polynomial.is_univariate: | |
| raise ValueError("The expression should be univariate") | |
| if not polynomial.domain == ZZ: | |
| raise ValueError("The expression should should have integer coefficients") | |
| return polynomial.all_coeffs() | |
| def polynomial_congruence(expr, m): | |
| """ | |
| Find the solutions to a polynomial congruence equation modulo m. | |
| Parameters | |
| ========== | |
| expr : integer coefficient polynomial | |
| m : positive integer | |
| Examples | |
| ======== | |
| >>> from sympy.ntheory import polynomial_congruence | |
| >>> from sympy.abc import x | |
| >>> expr = x**6 - 2*x**5 -35 | |
| >>> polynomial_congruence(expr, 6125) | |
| [3257] | |
| See Also | |
| ======== | |
| sympy.polys.galoistools.gf_csolve : low level solving routine used by this routine | |
| """ | |
| coefficients = _valid_expr(expr) | |
| coefficients = [num % m for num in coefficients] | |
| rank = len(coefficients) | |
| if rank == 3: | |
| return quadratic_congruence(*coefficients, m) | |
| if rank == 2: | |
| return quadratic_congruence(0, *coefficients, m) | |
| if coefficients[0] == 1 and 1 + coefficients[-1] == sum(coefficients): | |
| return nthroot_mod(-coefficients[-1], rank - 1, m, True) | |
| return gf_csolve(coefficients, m) | |
| def binomial_mod(n, m, k): | |
| """Compute ``binomial(n, m) % k``. | |
| Explanation | |
| =========== | |
| Returns ``binomial(n, m) % k`` using a generalization of Lucas' | |
| Theorem for prime powers given by Granville [1]_, in conjunction with | |
| the Chinese Remainder Theorem. The residue for each prime power | |
| is calculated in time O(log^2(n) + q^4*log(n)log(p) + q^4*p*log^3(p)). | |
| Parameters | |
| ========== | |
| n : an integer | |
| m : an integer | |
| k : a positive integer | |
| Examples | |
| ======== | |
| >>> from sympy.ntheory.residue_ntheory import binomial_mod | |
| >>> binomial_mod(10, 2, 6) # binomial(10, 2) = 45 | |
| 3 | |
| >>> binomial_mod(17, 9, 10) # binomial(17, 9) = 24310 | |
| 0 | |
| References | |
| ========== | |
| .. [1] Binomial coefficients modulo prime powers, Andrew Granville, | |
| Available: https://web.archive.org/web/20170202003812/http://www.dms.umontreal.ca/~andrew/PDF/BinCoeff.pdf | |
| """ | |
| if k < 1: raise ValueError('k is required to be positive') | |
| # We decompose q into a product of prime powers and apply | |
| # the generalization of Lucas' Theorem given by Granville | |
| # to obtain binomial(n, k) mod p^e, and then use the Chinese | |
| # Remainder Theorem to obtain the result mod q | |
| if n < 0 or m < 0 or m > n: return 0 | |
| factorisation = factorint(k) | |
| residues = [_binomial_mod_prime_power(n, m, p, e) for p, e in factorisation.items()] | |
| return crt([p**pw for p, pw in factorisation.items()], residues, check=False)[0] | |
| def _binomial_mod_prime_power(n, m, p, q): | |
| """Compute ``binomial(n, m) % p**q`` for a prime ``p``. | |
| Parameters | |
| ========== | |
| n : positive integer | |
| m : a nonnegative integer | |
| p : a prime | |
| q : a positive integer (the prime exponent) | |
| Examples | |
| ======== | |
| >>> from sympy.ntheory.residue_ntheory import _binomial_mod_prime_power | |
| >>> _binomial_mod_prime_power(10, 2, 3, 2) # binomial(10, 2) = 45 | |
| 0 | |
| >>> _binomial_mod_prime_power(17, 9, 2, 4) # binomial(17, 9) = 24310 | |
| 6 | |
| References | |
| ========== | |
| .. [1] Binomial coefficients modulo prime powers, Andrew Granville, | |
| Available: https://web.archive.org/web/20170202003812/http://www.dms.umontreal.ca/~andrew/PDF/BinCoeff.pdf | |
| """ | |
| # Function/variable naming within this function follows Ref.[1] | |
| # n!_p will be used to denote the product of integers <= n not divisible by | |
| # p, with binomial(n, m)_p the same as binomial(n, m), but defined using | |
| # n!_p in place of n! | |
| modulo = pow(p, q) | |
| def up_factorial(u): | |
| """Compute (u*p)!_p modulo p^q.""" | |
| r = q // 2 | |
| fac = prod = 1 | |
| if r == 1 and p == 2 or 2*r + 1 in (p, p*p): | |
| if q % 2 == 1: r += 1 | |
| modulo, div = pow(p, 2*r), pow(p, 2*r - q) | |
| else: | |
| modulo, div = pow(p, 2*r + 1), pow(p, (2*r + 1) - q) | |
| for j in range(1, r + 1): | |
| for mul in range((j - 1)*p + 1, j*p): # ignore jp itself | |
| fac *= mul | |
| fac %= modulo | |
| bj_ = bj(u, j, r) | |
| prod *= pow(fac, bj_, modulo) | |
| prod %= modulo | |
| if p == 2: | |
| sm = u // 2 | |
| for j in range(1, r + 1): sm += j//2 * bj(u, j, r) | |
| if sm % 2 == 1: prod *= -1 | |
| prod %= modulo//div | |
| return prod % modulo | |
| def bj(u, j, r): | |
| """Compute the exponent of (j*p)!_p in the calculation of (u*p)!_p.""" | |
| prod = u | |
| for i in range(1, r + 1): | |
| if i != j: prod *= u*u - i*i | |
| for i in range(1, r + 1): | |
| if i != j: prod //= j*j - i*i | |
| return prod // j | |
| def up_plus_v_binom(u, v): | |
| """Compute binomial(u*p + v, v)_p modulo p^q.""" | |
| prod = 1 | |
| div = invert(factorial(v), modulo) | |
| for j in range(1, q): | |
| b = div | |
| for v_ in range(j*p + 1, j*p + v + 1): | |
| b *= v_ | |
| b %= modulo | |
| aj = u | |
| for i in range(1, q): | |
| if i != j: aj *= u - i | |
| for i in range(1, q): | |
| if i != j: aj //= j - i | |
| aj //= j | |
| prod *= pow(b, aj, modulo) | |
| prod %= modulo | |
| return prod | |
| def factorial(v, prev): | |
| """Compute v! modulo p^q.""" | |
| return v*prev[-1] % modulo | |
| def factorial_p(n): | |
| """Compute n!_p modulo p^q.""" | |
| u, v = divmod(n, p) | |
| return (factorial(v) * up_factorial(u) * up_plus_v_binom(u, v)) % modulo | |
| prod = 1 | |
| Nj, Mj, Rj = n, m, n - m | |
| # e0 will be the p-adic valuation of binomial(n, m) at p | |
| e0 = carry = eq_1 = j = 0 | |
| while Nj: | |
| numerator = factorial_p(Nj % modulo) | |
| denominator = factorial_p(Mj % modulo) * factorial_p(Rj % modulo) % modulo | |
| Nj, (Mj, mj), (Rj, rj) = Nj//p, divmod(Mj, p), divmod(Rj, p) | |
| carry = (mj + rj + carry) // p | |
| e0 += carry | |
| if j >= q - 1: eq_1 += carry | |
| prod *= numerator * invert(denominator, modulo) | |
| prod %= modulo | |
| j += 1 | |
| mul = pow(1 if p == 2 and q >= 3 else -1, eq_1, modulo) | |
| return (pow(p, e0, modulo) * mul * prod) % modulo | |