Пример #1
0
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
Пример #2
0
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
Пример #3
0
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
Пример #4
0
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
Пример #5
0
    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))
Пример #6
0
    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))
Пример #7
0
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
Пример #8
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
Пример #9
0
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
Пример #10
0
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
Пример #11
0
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
Пример #12
0
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
Пример #13
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
Пример #14
0
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
Пример #15
0
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