def carmichael_lambda(n): """Returns the smallest positive integer m such that a**m = 1 (mod n) for all integers a coprime to n. Parameters * n: int or list (n > 1) The modulus. This value can be an int or a factorization. Returns: * m: int Raises: * ValueError: If n <= 0 or n is not an integer or factorization. Examples: >>> carmichael_lambda(100) 20 >>> carmichael_lambda([(2, 2), (5, 2)]) 20 >>> carmichael_lambda(113) 112 >>> carmichael_lambda(0) Traceback (most recent call last): ... ValueError: carmichael_lambda: Must have n > 0. Details: The Carmichael lambda function gives the exponent of the multiplicative group of integers modulo n. For each n, lambda(n) divides euler_phi(n). This method uses the standard definition of the Carmichael lambda function. In particular, lambda(n) is the least common multiple of the values of lambda(p**e) for each prime power in the factorization of n. See "Fundamental Number Theory with Applications" by Mollin for details. """ if isinstance(n, (int, long)): if n == 1: return 1 elif n <= 0: raise ValueError("carmichael_lambda: Must have n > 0.") n_factorization = factor.factor(n) elif isinstance(n, list) and n[0][0] > 0: n_factorization = n else: raise ValueError("carmichael_lambda: Input must be a positive integer or a factorization.") terms = [] for (p, e) in n_factorization: if p != 2: term = p**(e - 1)*(p - 1) else: if e <= 2: term = 1 << (e - 1) else: term = 1 << (e - 2) terms.append(term) value = lcm_list(terms) return value
def tau(n): """Returns the number of divisors of n. Parameters * n: int (n > 0) The value of n can be an int or a factorization. Returns: * s: int Raises: * ValueError: If n <= 0 or if n is not an int or factorization. Examples: >>> tau(9) 3 >>> tau([(2, 2), (5, 2)]) 9 >>> tau(100) 9 >>> tau('cat') Traceback (most recent call last): ... ValueError: tau: Input must be a positive integer or a factorization. >>> tau(-1) Traceback (most recent call last): ... ValueError: tau: Must have n > 0. Details: The divisor tau function is a multiplicative function satisfying tau(p**k) = 1 + k for prime powers p**k. For positive integers n, this method computes the factorization of n, and then uses this product definition. If instead a factorization of n is given, then the product formula is used directly. """ if isinstance(n, (int, long)): if n == 1: return 1 elif n <= 0: raise ValueError("tau: Must have n > 0.") n_factorization = factor.factor(n) elif isinstance(n, list) and n[0][0] > 0: n_factorization = n else: raise ValueError("tau: Input must be a positive integer or a factorization.") prod = 1 for (p, e) in n_factorization: prod *= (e + 1) return prod
def sqrts_mod_n(a, n, n_factorization=None): """Returns the solutions x to the congruence x**2 = a (mod n). Given integers a and n, this function returns all solutions to the congruence x**2 = a (mod n). Input: * a: int A square modulo n. * n: int The modulus. * n_factorization: list (default=None) The factorization of n. Returns: * roots: list A sorted list of all square roots of a modulo n. Examples: >>> sqrts_mod_n(3, 11**3*23**3) [16152263, 28026114, 150110933, 161984784] >>> sqrts_mod_n(49, 53**3*61**4) [7, 721465236980, 1339862033577, 2061327270550] >>> sqrts_mod_n(-1, 5**3*7**2) [] """ if n_factorization is None: n_factorization = factor.factor(n) congruences = [] moduli = [] for (p, k) in n_factorization: roots = _sqrts_mod_pk(a, p, k) pk = p**k pairs = [(r, pk) for r in roots] congruences.append(pairs) moduli.append(pk) if len(congruences) == 1: values = [r for (r, _) in congruences[0]] else: preconditioning_data = crt_preconditioning_data(moduli) values = [] for system in itertools.product(*congruences): values.append(chinese_preconditioned(system, preconditioning_data)) values.sort() return values
def test_factor(self): factorizations = { 5**77 - 1: [(2, 2), (19531, 1), (12207031L, 1), (527093491L, 1), (8090594434231L, 1), (162715052426691233701L, 1)], 5**93 + 1: [(2, 1), (3, 2), (7, 1), (1303, 1), (21207101L, 1), (258065887L, 1), (28086211607L, 1), (75005167927L, 1), (53345671490722200466369L, 1)], 1303**6 * 11213**4: [(1303, 6), (11213, 4)], 2**91 - 1: [(127, 1), (911, 1), (8191, 1), (112901153L, 1), (23140471537L, 1)], 10**22 + 1: [(89, 1), (101, 1), (1052788969L, 1), (1056689261L, 1)] } for (n, n_fac) in factorizations.items(): self.assertEqual(n_fac, factor(n))
def test_factor(self): factorizations = { 5**77 - 1: [ (2, 2), (19531, 1), (12207031L, 1), (527093491L, 1), (8090594434231L, 1), (162715052426691233701L, 1) ], 5**93 + 1: [ (2, 1), (3, 2), (7, 1), (1303, 1), (21207101L, 1), (258065887L, 1), (28086211607L, 1), (75005167927L, 1), (53345671490722200466369L, 1) ], 1303**6*11213**4: [ (1303, 6), (11213, 4) ], 2**91 - 1: [ (127, 1), (911, 1), (8191, 1), (112901153L, 1), (23140471537L, 1) ], 10**22 + 1: [ (89, 1), (101, 1), (1052788969L, 1), (1056689261L, 1) ] } for (n, n_fac) in factorizations.items(): self.assertEqual(n_fac, factor(n))
def moebius(n=None, factorization=None): r"""Returns the value of the Moebius function ``mu(n)``. Parameters ---------- n : int, optional factorization : list, optional Factorization of `n` given as a list of (prime, exponent) pairs. Returns ------- mu : int The value moebius(n). Raises ------ ValueError If ``n <= 0``. See Also -------- moebius_sum, moebius_list Notes ----- The Moebius mu function is a multiplicative function satisfying .. math:: mu(p_1 p_2 \cdots p_k) = (-1)^k and ``mu(p**e) = 0`` for ``e >= 2``. For positive integers `n`, this method computes the factorization of `n`, and then uses this product definition. If instead a factorization of `n` is given, then the product formula is used directly. See section 2.2 of [1] for details, and see section 3.9 for information related to the growth rate. References ---------- .. [1] T.M. Apostol, "Introduction to Analytic Number Theory", Springer-Verlag, New York, 1976. Examples -------- >>> moebius(1) 1 >>> moebius(35) 1 >>> moebius(factorization=[(5, 1), (7, 1)]) 1 >>> moebius(100) 0 >>> moebius(2) -1 >>> moebius(-1) Traceback (most recent call last): ... ValueError: moebius: Must have n > 0. """ if n is None and factorization is None: raise TypeError("euler_phi: Expected at least one argument.") elif factorization is None: if n == 1: return 1 elif n <= 0: raise ValueError("moebius: Must have n > 0.") factorization = factor.factor(n) else: if factorization[0][0] == -1: raise ValueError("moebius: Must have n > 0.") if factor.is_squarefree(factorization=factorization): k = len(factorization) return (-1)**k else: return 0
def euler_phi_inverse(n): """Returns a sorted list of all positive integers k such that euler_phi(k) = n. Parameters * n: int or list (n > 0) The value of n can be an int or a factorization. Returns: * values: list Raises: * ValueError: If n <= 0 or n is not an int or factorization. Examples: >>> euler_phi_inverse(40) [41, 55, 75, 82, 88, 100, 110, 132, 150] >>> euler_phi_inverse(100) [101, 125, 202, 250] >>> euler_phi_inverse([(2, 2), (5, 2)]) [101, 125, 202, 250] >>> euler_phi_inverse(103) [] >>> euler_phi_inverse(-1) Traceback (most recent call last): ... ValueError: euler_phi_inverse: Must have n > 0. Details: This uses the algorithm outlined in "Discovering Mathematics with Magma" by Bosma and Cannon. For a different method, see the paper "Euler's Totient Function and its Inverse" by Gupta. """ if isinstance(n, (int, long)): if n == 1: return [1, 2] elif n > 1 and n % 2 == 1: # We know euler_phi(n) is even for n >= 3 return [] elif n <= 0: raise ValueError("euler_phi_inverse: Must have n > 0.") n_factorization = factor.factor(n) elif isinstance(n, list) and n[0][0] > 0: n_factorization = n n = factor.factor_back(n_factorization) else: raise ValueError("euler_phi_inverse: Input must be a positive integer or a factorization.") powers_of_two = set([1]) if n % 2 == 0: for i in xrange(1, n_factorization[0][1] + 1): powers_of_two.add(2**i) # Odd primes p that divide n must have the property that p - 1 divides m. prime_list = [] for d in factor.divisors(factorization=n_factorization)[1:]: if primality.is_prime(d + 1): prime_list.append(d + 1) # Here, we store pairs (a, b) such that a is odd and phi(a) = m/b, with b # even or 1. Every pair contributes at least one solution. When b = 1, the # pair contributes two solutions. pairs = [(1, n)] for p in reversed(prime_list): new_pairs = [] for (a, b) in pairs: if b == 1: continue pk = p d = b//(p - 1) mmod = b % (p - 1) while mmod == 0: if d % 2 == 0 or d == 1: new_pairs.append((pk*a, d)) pk *= p mmod = d % p d = d//p pairs.extend(new_pairs) # When b = 2^k, we have the solution 2*b*a. values = [] for (a, b) in pairs: if b in powers_of_two: values.append(2*b*a) if b == 1: values.append(a) values.sort() return values
def euler_phi(n=None, factorization=None): r"""Returns the Euler totient of `n`. For positive integers `n`, this method returns the number of positive integers less than or equal to `n` which are relatively prime to `n`. Parameters ---------- n : int, optional factorization : list, optional Factorization of `n` given as a list of (prime, exponent) pairs. Returns ------- phi : int The value euler_phi(n) Raises ------ ValueError If ``n <= 0``. See Also -------- euler_phi_sum, euler_phi_list Notes ----- The Euler phi function is a multiplicative function satisfying .. math:: \phi(p^k) = (p - 1) p^{k - 1} for prime powers ``p^k``. For positive integers `n`, this method computes the factorization of `n`, and then uses this product definition. If instead a factorization of `n` is given, then the product formula is used directly. See section 2.3 of [1] for details, and see section 3.7 for information related to the growth rate. References ---------- .. [1] T.M. Apostol, "Introduction to Analytic Number Theory", Springer-Verlag, New York, 1976. Examples -------- >>> euler_phi(1) 1 >>> euler_phi(7) 6 >>> euler_phi(100) 40 >>> euler_phi(factorization=[(2, 2), (5, 2)]) 40 >>> euler_phi(0) Traceback (most recent call last): ... ValueError: euler_phi: Must have n > 0. """ if n is None and factorization is None: raise TypeError("euler_phi: Expected at least one argument.") elif factorization is None: if n == 1: return 1 elif n <= 0: raise ValueError("euler_phi: Must have n > 0.") factorization = factor.factor(n) else: if factorization[0][0] == -1: raise ValueError("euler_phi: Must have n > 0.") prod = 1 for (p, e) in factorization: prod *= p**(e - 1)*(p - 1) return prod
def sigma(n, k=1): """Returns the sum of the kth powers of the divisors of n. Parameters * n: int or list (n > 0) The value of n can be an int or a factorization. * k: int (k >= 0) (default=1) Returns: * s: int Raises: * ValueError: If n <= 0 or k < 0. Examples: >>> sigma(9) 13 >>> 1 + 3 + 9 13 >>> sigma([(3, 2)]) 13 >>> sigma(10, 2) 130 >>> 1**2 + 2**2 + 5**2 + 10**2 130 >>> sigma(10, 0) 4 >>> sigma(-1) Traceback (most recent call last): ... ValueError: sigma: Must have n > 0. >>> sigma(10, -1) Traceback (most recent call last): ... ValueError: sigma: Must have k >= 0. Details: The divisor sigma function is a multiplicative function satisfying sigma(p**e, k) = 1 + p**k + p**(2*k) + ... + p**(e*k) for prime powers p**e. For positive integers n, this method computes the factorization of n, and then uses this product definition. If instead a factorization of n is given, then the product formula is used directly. """ if k < 0: raise ValueError("sigma: Must have k >= 0.") elif k == 0: return tau(n) if isinstance(n, (int, long)): if n == 1: return 1 elif n <= 0: raise ValueError("sigma: Must have n > 0.") n_factorization = factor.factor(n) elif isinstance(n, list) and n[0][0] > 0: n_factorization = n else: raise ValueError("sigma: Input must be a positive integer or a factorization.") prod = 1 for (p, e) in n_factorization: pk = p**k prod *= (pk**(e + 1) - 1)//(pk - 1) return prod
def primitive_root(n, n_factorization=None, phi=None, phi_divisors=None): """Returns a primitive root modulo n. Given a positive integer n of the form n = 2, 4, p**a, or 2*p**a for p an odd prime and a >= 1, this function returns the least primitive root of n. Input: * n: int (n > 1) * n_factorization: list (default=None) The prime factorization of n. * phi: int (default=None) The value of euler_phi(n). * phi_divisors: list (default=None) A list of the prime divisors of euler_phi(n). Returns: * g: int The least primitive root of n. Raises: * ValueError: if primitive roots don't exist modulo n. Examples: >>> primitive_root(7) 3 >>> primitive_root(11) 2 >>> primitive_root(14) 3 >>> primitive_root(12) Traceback (most recent call last): ... ValueError: primitive_root: No primitive root for n. Details: An integer a is called a primitive root mod n if a generates the multiplicative group of units modulo n. Equivalently, a is a primitive root modulo n if the order of a modulo m is euler_phi(n). As noted above, primitive roots only exist for moduli of the form n = 2, 4, p**a, 2*p**a, where p > 2 is prime, and a >= 1. See Theorem 6.11 of "Elementary Number Theory" by Jones and Jones for a proof of this fact. See also Chapter 10 of "Introduction to Analytic Number Theory" by Apostol. This method uses the fact that an integer a coprime to n is a primitive root if and only if a**(euler_phi(n)/p) != 1 (mod n) for each prime p dividing euler_phi(n). See Lemma 6.4 in Jones and Jones for a proof of this. Note also that there is another way to construct primitive roots for composite moduli. To find a primitive root modulu p**a for p an odd prime and a >= 2, first compute g, a primitive root modulo p using the above criteria. Next, compute g1 = g**(p - 1) (mod p**2). If g1 != 1, then g is a primitive root modulo p**a for every a >= 1. Otherwise, g + p is. Finally, note that when p is an odd prime, if g is a primitive root modulo p**a, then either g or g + p**a (whichever is odd) is a primitive root modulo 2*p**a. See Lemma 1.4.5 of "A Course in Computational Algebraic Number Theory" by Cohen for more details. """ if n_factorization is None: n_factorization = factor.factor(n) if n % 4 == 0 and n != 4: raise ValueError("primitive_root: No primitive root for n.") if len(n_factorization) > 2: raise ValueError("primitive_root: No primitive root for n.") if n % 2 == 1 and len(n_factorization) > 1: raise ValueError("primitive_root: No primitive root for n.") if phi is None: phi = functions.euler_phi(n_factorization) if phi_divisors is None: phi_divisors = factor.prime_divisors(phi) for g in xrange(1, n): if is_primitive_root(g, n, phi=phi, phi_divisors=phi_divisors): return g
def quadratic_congruence(coeff_list, n, n_factorization=None): """Returns a list of the roots of the quadratic equation modulo n. Input: * coeff_list: list A list of the coefficients of the quadratic. * n: int The modulus. * n_factorization: list (default=None) The factorization of the modulus n. Returns: * roots: list A sorted list of the roots of the quadratic modulo n. Examples: >>> quadratic_congruence([1, 3, -18], 1000) [3, 378, 619, 994] >>> quadratic_congruence([1, -31, -12], 36) [7, 15, 16, 24] >>> quadratic_congruence([11, 5, 18], 29) [22, 25] Details: The algorithm proceeds by finding roots modulo p**k for each prime power p**k in the factorization of the modulus n. These roots are then combined using the Chinese Remainder Theorem. See Chapter 5 of "The Theory of Numbers - A Text and Source Book of Problems" by Adler and Coury for detailed information. """ if n_factorization is None: n_factorization = factor.factor(n) (a, b, c) = coeff_list discriminant = b * b - 4 * a * c all_roots = [] if gcd(2 * a, n) == 1: # This is the easy case where we can complete the square. # First, we solve the congruence y^2 = b^2 - 4*a*c (mod n) for y. discriminant_roots = sqrts_mod_n(discriminant, n, n_factorization) # Next, we solve the congruence 2*a*x = y - b (mod n) for x to obtain # the solutions to the quadratic. Since gcd(2*a, n) == 1, this is # simple, and each each value of y leads to one value of x. inverse = inverse_mod(2 * a, n) for y in discriminant_roots: x = (y - b) * inverse % n all_roots.append(x) else: # Here, gcd(4*a, n) != 1, so we can't complete the square as usual. # Write 4*a = a1*a2, with a2 coprime to n. a1 = 1 a2 = 4 * a d = gcd(n, a2) while d > 1: a1 *= d a2 /= d d = gcd(n, a2) # We solve the congruence y^2 = b^2 - 4*a*c (mod a1*n) for y. discriminant_roots = sqrts_mod_n(discriminant, a1 * n) # For each solution y, we solve 2*a*x = y - b (mod a1*n) for x. Since # gcd(2*a, n) > 1, each solution y leads to multiple values of x. for y in discriminant_roots: roots = linear_congruence(2 * a, y - b, a1 * n) all_roots.extend(roots) # Eliminate repeated solutions, and reduce modulo the original modulus. distinct_roots = {x % n for x in all_roots} all_roots = list(distinct_roots) all_roots.sort() return all_roots
def euler_phi_inverse(n): """Returns a sorted list of all positive integers k such that euler_phi(k) = n. Parameters * n: int or list (n > 0) The value of n can be an int or a factorization. Returns: * values: list Raises: * ValueError: If n <= 0 or n is not an int or factorization. Examples: >>> euler_phi_inverse(40) [41, 55, 75, 82, 88, 100, 110, 132, 150] >>> euler_phi_inverse(100) [101, 125, 202, 250] >>> euler_phi_inverse([(2, 2), (5, 2)]) [101, 125, 202, 250] >>> euler_phi_inverse(103) [] >>> euler_phi_inverse(-1) Traceback (most recent call last): ... ValueError: euler_phi_inverse: Must have n > 0. Details: This uses the algorithm outlined in "Discovering Mathematics with Magma" by Bosma and Cannon. For a different method, see the paper "Euler's Totient Function and its Inverse" by Gupta. """ if isinstance(n, (int, long)): if n == 1: return [1, 2] elif n > 1 and n % 2 == 1: # We know euler_phi(n) is even for n >= 3 return [] elif n <= 0: raise ValueError("euler_phi_inverse: Must have n > 0.") n_factorization = factor.factor(n) elif isinstance(n, list) and n[0][0] > 0: n_factorization = n n = factor.factor_back(n_factorization) else: raise ValueError( "euler_phi_inverse: Input must be a positive integer or a factorization." ) powers_of_two = set([1]) if n % 2 == 0: for i in xrange(1, n_factorization[0][1] + 1): powers_of_two.add(2**i) # Odd primes p that divide n must have the property that p - 1 divides m. prime_list = [] for d in factor.divisors(factorization=n_factorization)[1:]: if primality.is_prime(d + 1): prime_list.append(d + 1) # Here, we store pairs (a, b) such that a is odd and phi(a) = m/b, with b # even or 1. Every pair contributes at least one solution. When b = 1, the # pair contributes two solutions. pairs = [(1, n)] for p in reversed(prime_list): new_pairs = [] for (a, b) in pairs: if b == 1: continue pk = p d = b // (p - 1) mmod = b % (p - 1) while mmod == 0: if d % 2 == 0 or d == 1: new_pairs.append((pk * a, d)) pk *= p mmod = d % p d = d // p pairs.extend(new_pairs) # When b = 2^k, we have the solution 2*b*a. values = [] for (a, b) in pairs: if b in powers_of_two: values.append(2 * b * a) if b == 1: values.append(a) values.sort() return values
def quadratic_congruence(coeff_list, n, n_factorization=None): """Returns a list of the roots of the quadratic equation modulo n. Input: * coeff_list: list A list of the coefficients of the quadratic. * n: int The modulus. * n_factorization: list (default=None) The factorization of the modulus n. Returns: * roots: list A sorted list of the roots of the quadratic modulo n. Examples: >>> quadratic_congruence([1, 3, -18], 1000) [3, 378, 619, 994] >>> quadratic_congruence([1, -31, -12], 36) [7, 15, 16, 24] >>> quadratic_congruence([11, 5, 18], 29) [22, 25] Details: The algorithm proceeds by finding roots modulo p**k for each prime power p**k in the factorization of the modulus n. These roots are then combined using the Chinese Remainder Theorem. See Chapter 5 of "The Theory of Numbers - A Text and Source Book of Problems" by Adler and Coury for detailed information. """ if n_factorization is None: n_factorization = factor.factor(n) (a, b, c) = coeff_list discriminant = b*b - 4*a*c all_roots = [] if gcd(2*a, n) == 1: # This is the easy case where we can complete the square. # First, we solve the congruence y^2 = b^2 - 4*a*c (mod n) for y. discriminant_roots = sqrts_mod_n(discriminant, n, n_factorization) # Next, we solve the congruence 2*a*x = y - b (mod n) for x to obtain # the solutions to the quadratic. Since gcd(2*a, n) == 1, this is # simple, and each each value of y leads to one value of x. inverse = inverse_mod(2*a, n) for y in discriminant_roots: x = (y - b)*inverse % n all_roots.append(x) else: # Here, gcd(4*a, n) != 1, so we can't complete the square as usual. # Write 4*a = a1*a2, with a2 coprime to n. a1 = 1 a2 = 4*a d = gcd(n, a2) while d > 1: a1 *= d a2 /= d d = gcd(n, a2) # We solve the congruence y^2 = b^2 - 4*a*c (mod a1*n) for y. discriminant_roots = sqrts_mod_n(discriminant, a1*n) # For each solution y, we solve 2*a*x = y - b (mod a1*n) for x. Since # gcd(2*a, n) > 1, each solution y leads to multiple values of x. for y in discriminant_roots: roots = linear_congruence(2*a, y - b, a1*n) all_roots.extend(roots) # Eliminate repeated solutions, and reduce modulo the original modulus. distinct_roots = {x % n for x in all_roots} all_roots = list(distinct_roots) all_roots.sort() return all_roots