Coverage for sympy/polys/galoistools.py : 33%
        
        
    Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
| 
 """Dense univariate polynomials with coefficients in Galois fields. """ 
 from __future__ import print_function, division 
 from random import uniform from math import ceil as _ceil, sqrt as _sqrt 
 from sympy.core.compatibility import SYMPY_INTS, range from sympy.core.mul import prod from sympy.polys.polyutils import _sort_factors from sympy.polys.polyconfig import query from sympy.polys.polyerrors import ExactQuotientFailed 
 from sympy.ntheory import factorint 
 def gf_crt(U, M, K=None): """ Chinese Remainder Theorem. 
 Given a set of integer residues ``u_0,...,u_n`` and a set of co-prime integer moduli ``m_0,...,m_n``, returns an integer ``u``, such that ``u = u_i mod m_i`` for ``i = ``0,...,n``. 
 As an example consider a set of residues ``U = [49, 76, 65]`` and a set of moduli ``M = [99, 97, 95]``. Then we have:: 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_crt >>> from sympy.ntheory.modular import solve_congruence 
 >>> gf_crt([49, 76, 65], [99, 97, 95], ZZ) 639985 
 This is the correct result because:: 
 >>> [639985 % m for m in [99, 97, 95]] [49, 76, 65] 
 Note: this is a low-level routine with no error checking. 
 See Also ======== 
 sympy.ntheory.modular.crt : a higher level crt routine sympy.ntheory.modular.solve_congruence 
 """ p = prod(M, start=K.one) v = K.zero 
 for u, m in zip(U, M): e = p // m s, _, _ = K.gcdex(e, m) v += e*(u*s % m) 
 return v % p 
 
 def gf_crt1(M, K): """ First part of the Chinese Remainder Theorem. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_crt1 
 >>> gf_crt1([99, 97, 95], ZZ) (912285, [9215, 9405, 9603], [62, 24, 12]) 
 """ E, S = [], [] p = prod(M, start=K.one) 
 for m in M: E.append(p // m) S.append(K.gcdex(E[-1], m)[0] % m) 
 return p, E, S 
 
 def gf_crt2(U, M, p, E, S, K): """ Second part of the Chinese Remainder Theorem. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_crt2 
 >>> U = [49, 76, 65] >>> M = [99, 97, 95] >>> p = 912285 >>> E = [9215, 9405, 9603] >>> S = [62, 24, 12] 
 >>> gf_crt2(U, M, p, E, S, ZZ) 639985 
 """ v = K.zero 
 for u, m, e, s in zip(U, M, E, S): v += e*(u*s % m) 
 return v % p 
 
 def gf_int(a, p): """ Coerce ``a mod p`` to an integer in the range ``[-p/2, p/2]``. 
 Examples ======== 
 >>> from sympy.polys.galoistools import gf_int 
 >>> gf_int(2, 7) 2 >>> gf_int(5, 7) -2 
 """ else: 
 
 def gf_degree(f): """ Return the leading degree of ``f``. 
 Examples ======== 
 >>> from sympy.polys.galoistools import gf_degree 
 >>> gf_degree([1, 1, 2, 0]) 3 >>> gf_degree([]) -1 
 """ 
 
 def gf_LC(f, K): """ Return the leading coefficient of ``f``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_LC 
 >>> gf_LC([3, 0, 1], ZZ) 3 
 """ if not f: return K.zero else: return f[0] 
 
 def gf_TC(f, K): """ Return the trailing coefficient of ``f``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_TC 
 >>> gf_TC([3, 0, 1], ZZ) 1 
 """ if not f: return K.zero else: return f[-1] 
 
 def gf_strip(f): """ Remove leading zeros from ``f``. 
 
 Examples ======== 
 >>> from sympy.polys.galoistools import gf_strip 
 >>> gf_strip([0, 0, 0, 3, 0, 1]) [3, 0, 1] 
 """ 
 
 else: 
 
 
 def gf_trunc(f, p): """ Reduce all coefficients modulo ``p``. 
 Examples ======== 
 >>> from sympy.polys.galoistools import gf_trunc 
 >>> gf_trunc([7, -2, 3], 5) [2, 3, 3] 
 """ 
 
 def gf_normal(f, p, K): """ Normalize all coefficients in ``K``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_normal 
 >>> gf_normal([5, 10, 21, -3], 5, ZZ) [1, 2] 
 """ return gf_trunc(list(map(K, f)), p) 
 
 def gf_from_dict(f, p, K): """ Create a ``GF(p)[x]`` polynomial from a dict. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_from_dict 
 >>> gf_from_dict({10: ZZ(4), 4: ZZ(33), 0: ZZ(-1)}, 5, ZZ) [4, 0, 0, 0, 0, 0, 3, 0, 0, 0, 4] 
 """ n, h = max(f.keys()), [] 
 if isinstance(n, SYMPY_INTS): for k in range(n, -1, -1): h.append(f.get(k, K.zero) % p) else: (n,) = n 
 for k in range(n, -1, -1): h.append(f.get((k,), K.zero) % p) 
 return gf_trunc(h, p) 
 
 def gf_to_dict(f, p, symmetric=True): """ Convert a ``GF(p)[x]`` polynomial to a dict. 
 Examples ======== 
 >>> from sympy.polys.galoistools import gf_to_dict 
 >>> gf_to_dict([4, 0, 0, 0, 0, 0, 3, 0, 0, 0, 4], 5) {0: -1, 4: -2, 10: -1} >>> gf_to_dict([4, 0, 0, 0, 0, 0, 3, 0, 0, 0, 4], 5, symmetric=False) {0: 4, 4: 3, 10: 4} 
 """ n, result = gf_degree(f), {} 
 for k in range(0, n + 1): if symmetric: a = gf_int(f[n - k], p) else: a = f[n - k] 
 if a: result[k] = a 
 return result 
 
 def gf_from_int_poly(f, p): """ Create a ``GF(p)[x]`` polynomial from ``Z[x]``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_from_int_poly 
 >>> gf_from_int_poly([7, -2, 3], 5) [2, 3, 3] 
 """ 
 
 def gf_to_int_poly(f, p, symmetric=True): """ Convert a ``GF(p)[x]`` polynomial to ``Z[x]``. 
 
 Examples ======== 
 >>> from sympy.polys.galoistools import gf_to_int_poly 
 >>> gf_to_int_poly([2, 3, 3], 5) [2, -2, -2] >>> gf_to_int_poly([2, 3, 3], 5, symmetric=False) [2, 3, 3] 
 """ else: return f 
 
 def gf_neg(f, p, K): """ Negate a polynomial in ``GF(p)[x]``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_neg 
 >>> gf_neg([3, 2, 1, 0], 5, ZZ) [2, 3, 4, 0] 
 """ 
 
 def gf_add_ground(f, a, p, K): """ Compute ``f + a`` where ``f`` in ``GF(p)[x]`` and ``a`` in ``GF(p)``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_add_ground 
 >>> gf_add_ground([3, 2, 4], 2, 5, ZZ) [3, 2, 1] 
 """ if not f: a = a % p else: a = (f[-1] + a) % p 
 if len(f) > 1: return f[:-1] + [a] 
 if not a: return [] else: return [a] 
 
 def gf_sub_ground(f, a, p, K): """ Compute ``f - a`` where ``f`` in ``GF(p)[x]`` and ``a`` in ``GF(p)``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_sub_ground 
 >>> gf_sub_ground([3, 2, 4], 2, 5, ZZ) [3, 2, 2] 
 """ a = -a % p else: 
 
 else: 
 
 def gf_mul_ground(f, a, p, K): """ Compute ``f * a`` where ``f`` in ``GF(p)[x]`` and ``a`` in ``GF(p)``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_mul_ground 
 >>> gf_mul_ground([3, 2, 4], 2, 5, ZZ) [1, 4, 3] 
 """ else: 
 
 def gf_quo_ground(f, a, p, K): """ Compute ``f/a`` where ``f`` in ``GF(p)[x]`` and ``a`` in ``GF(p)``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_quo_ground 
 >>> gf_quo_ground(ZZ.map([3, 2, 4]), ZZ(2), 5, ZZ) [4, 1, 2] 
 """ 
 
 def gf_add(f, g, p, K): """ Add polynomials in ``GF(p)[x]``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_add 
 >>> gf_add([3, 2, 4], [2, 2, 2], 5, ZZ) [4, 1] 
 """ return g 
 
 else: 
 else: 
 
 
 def gf_sub(f, g, p, K): """ Subtract polynomials in ``GF(p)[x]``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_sub 
 >>> gf_sub([3, 2, 4], [2, 2, 2], 5, ZZ) [1, 0, 2] 
 """ 
 
 else: 
 else: 
 
 
 def gf_mul(f, g, p, K): """ Multiply polynomials in ``GF(p)[x]``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_mul 
 >>> gf_mul([3, 2, 4], [2, 2, 2], 5, ZZ) [1, 0, 3, 2, 3] 
 """ 
 
 
 
 
 
 
 def gf_sqr(f, p, K): """ Square polynomials in ``GF(p)[x]``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_sqr 
 >>> gf_sqr([3, 2, 4], 5, ZZ) [4, 2, 3, 1, 1] 
 """ 
 
 
 
 
 
 
 
 
 
 
 
 def gf_add_mul(f, g, h, p, K): """ Returns ``f + g*h`` where ``f``, ``g``, ``h`` in ``GF(p)[x]``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_add_mul >>> gf_add_mul([3, 2, 4], [2, 2, 2], [1, 4], 5, ZZ) [2, 3, 2, 2] """ 
 
 def gf_sub_mul(f, g, h, p, K): """ Compute ``f - g*h`` where ``f``, ``g``, ``h`` in ``GF(p)[x]``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_sub_mul 
 >>> gf_sub_mul([3, 2, 4], [2, 2, 2], [1, 4], 5, ZZ) [3, 3, 2, 1] 
 """ 
 
 def gf_expand(F, p, K): """ Expand results of :func:`factor` in ``GF(p)[x]``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_expand 
 >>> gf_expand([([3, 2, 4], 1), ([2, 2], 2), ([3, 1], 3)], 5, ZZ) [4, 3, 0, 3, 0, 1, 4, 1] 
 """ if type(F) is tuple: lc, F = F else: lc = K.one 
 g = [lc] 
 for f, k in F: f = gf_pow(f, k, p, K) g = gf_mul(g, f, p, K) 
 return g 
 
 def gf_div(f, g, p, K): """ Division with remainder in ``GF(p)[x]``. 
 Given univariate polynomials ``f`` and ``g`` with coefficients in a finite field with ``p`` elements, returns polynomials ``q`` and ``r`` (quotient and remainder) such that ``f = q*g + r``. 
 Consider polynomials ``x**3 + x + 1`` and ``x**2 + x`` in GF(2):: 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_div, gf_add_mul 
 >>> gf_div(ZZ.map([1, 0, 1, 1]), ZZ.map([1, 1, 0]), 2, ZZ) ([1, 1], [1]) 
 As result we obtained quotient ``x + 1`` and remainder ``1``, thus:: 
 >>> gf_add_mul(ZZ.map([1]), ZZ.map([1, 1]), ZZ.map([1, 1, 0]), 2, ZZ) [1, 0, 1, 1] 
 References ========== 
 1. [Monagan93]_ 2. [Gathen99]_ 
 """ 
 raise ZeroDivisionError("polynomial division") 
 
 
 
 
 
 
 
 
 def gf_rem(f, g, p, K): """ Compute polynomial remainder in ``GF(p)[x]``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_rem 
 >>> gf_rem(ZZ.map([1, 0, 1, 1]), ZZ.map([1, 1, 0]), 2, ZZ) [1] 
 """ 
 
 def gf_quo(f, g, p, K): """ Compute exact quotient in ``GF(p)[x]``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_quo 
 >>> gf_quo(ZZ.map([1, 0, 1, 1]), ZZ.map([1, 1, 0]), 2, ZZ) [1, 1] >>> gf_quo(ZZ.map([1, 0, 3, 2, 3]), ZZ.map([2, 2, 2]), 5, ZZ) [3, 2, 4] 
 """ 
 raise ZeroDivisionError("polynomial division") return [] 
 
 
 
 
 
 
 
 def gf_exquo(f, g, p, K): """ Compute polynomial quotient in ``GF(p)[x]``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_exquo 
 >>> gf_exquo(ZZ.map([1, 0, 3, 2, 3]), ZZ.map([2, 2, 2]), 5, ZZ) [3, 2, 4] 
 >>> gf_exquo(ZZ.map([1, 0, 1, 1]), ZZ.map([1, 1, 0]), 2, ZZ) Traceback (most recent call last): ... ExactQuotientFailed: [1, 1, 0] does not divide [1, 0, 1, 1] 
 """ q, r = gf_div(f, g, p, K) 
 if not r: return q else: raise ExactQuotientFailed(f, g) 
 
 def gf_lshift(f, n, K): """ Efficiently multiply ``f`` by ``x**n``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_lshift 
 >>> gf_lshift([3, 2, 4], 4, ZZ) [3, 2, 4, 0, 0, 0, 0] 
 """ return f else: 
 
 def gf_rshift(f, n, K): """ Efficiently divide ``f`` by ``x**n``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_rshift 
 >>> gf_rshift([1, 2, 3, 4, 0], 3, ZZ) ([1, 2], [3, 4, 0]) 
 """ if not n: return f, [] else: return f[:-n], f[-n:] 
 
 def gf_pow(f, n, p, K): """ Compute ``f**n`` in ``GF(p)[x]`` using repeated squaring. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_pow 
 >>> gf_pow([3, 2, 4], 3, 5, ZZ) [2, 4, 4, 2, 2, 1, 4] 
 """ if not n: return [K.one] elif n == 1: return f elif n == 2: return gf_sqr(f, p, K) 
 h = [K.one] 
 while True: if n & 1: h = gf_mul(h, f, p, K) n -= 1 
 n >>= 1 
 if not n: break 
 f = gf_sqr(f, p, K) 
 return h 
 def gf_frobenius_monomial_base(g, p, K): """ return the list of ``x**(i*p) mod g in Z_p`` for ``i = 0, .., n - 1`` where ``n = gf_degree(g)`` 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_frobenius_monomial_base >>> g = ZZ.map([1, 0, 2, 1]) >>> gf_frobenius_monomial_base(g, 5, ZZ) [[1], [4, 4, 2], [1, 2]] 
 """ 
 
 def gf_frobenius_map(f, g, b, p, K): """ compute gf_pow_mod(f, p, g, p, K) using the Frobenius map 
 Parameters ========== 
 f, g : polynomials in ``GF(p)[x]`` b : frobenius monomial base p : prime number K : domain 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_frobenius_monomial_base, gf_frobenius_map >>> f = ZZ.map([2, 1 , 0, 1]) >>> g = ZZ.map([1, 0, 2, 1]) >>> p = 5 >>> b = gf_frobenius_monomial_base(g, p, ZZ) >>> r = gf_frobenius_map(f, g, b, p, ZZ) >>> gf_frobenius_map(f, g, b, p, ZZ) [4, 0, 3] """ f = gf_rem(f, g, p, K) return [] 
 def _gf_pow_pnm1d2(f, n, g, b, p, K): """ utility function for ``gf_edf_zassenhaus`` Compute ``f**((p**n - 1) // 2)`` in ``GF(p)[x]/(g)`` ``f**((p**n - 1) // 2) = (f*f**p*...*f**(p**n - 1))**((p - 1) // 2)`` """ 
 
 def gf_pow_mod(f, n, g, p, K): """ Compute ``f**n`` in ``GF(p)[x]/(g)`` using repeated squaring. 
 Given polynomials ``f`` and ``g`` in ``GF(p)[x]`` and a non-negative integer ``n``, efficiently computes ``f**n (mod g)`` i.e. the remainder of ``f**n`` from division by ``g``, using the repeated squaring algorithm. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_pow_mod 
 >>> gf_pow_mod(ZZ.map([3, 2, 4]), 3, ZZ.map([1, 1]), 5, ZZ) [] 
 References ========== 
 1. [Gathen99]_ 
 """ return [K.one] 
 
 
 
 
 
 
 
 def gf_gcd(f, g, p, K): """ Euclidean Algorithm in ``GF(p)[x]``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_gcd 
 >>> gf_gcd(ZZ.map([3, 2, 4]), ZZ.map([2, 2, 3]), 5, ZZ) [1, 3] 
 """ 
 
 
 def gf_lcm(f, g, p, K): """ Compute polynomial LCM in ``GF(p)[x]``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_lcm 
 >>> gf_lcm(ZZ.map([3, 2, 4]), ZZ.map([2, 2, 3]), 5, ZZ) [1, 2, 0, 4] 
 """ if not f or not g: return [] 
 h = gf_quo(gf_mul(f, g, p, K), gf_gcd(f, g, p, K), p, K) 
 return gf_monic(h, p, K)[1] 
 
 def gf_cofactors(f, g, p, K): """ Compute polynomial GCD and cofactors in ``GF(p)[x]``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_cofactors 
 >>> gf_cofactors(ZZ.map([3, 2, 4]), ZZ.map([2, 2, 3]), 5, ZZ) ([1, 3], [3, 3], [2, 1]) 
 """ if not f and not g: return ([], [], []) 
 h = gf_gcd(f, g, p, K) 
 return (h, gf_quo(f, h, p, K), gf_quo(g, h, p, K)) 
 
 def gf_gcdex(f, g, p, K): """ Extended Euclidean Algorithm in ``GF(p)[x]``. 
 Given polynomials ``f`` and ``g`` in ``GF(p)[x]``, computes polynomials ``s``, ``t`` and ``h``, such that ``h = gcd(f, g)`` and ``s*f + t*g = h``. The typical application of EEA is solving polynomial diophantine equations. 
 Consider polynomials ``f = (x + 7) (x + 1)``, ``g = (x + 7) (x**2 + 1)`` in ``GF(11)[x]``. Application of Extended Euclidean Algorithm gives:: 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_gcdex, gf_mul, gf_add 
 >>> s, t, g = gf_gcdex(ZZ.map([1, 8, 7]), ZZ.map([1, 7, 1, 7]), 11, ZZ) >>> s, t, g ([5, 6], [6], [1, 7]) 
 As result we obtained polynomials ``s = 5*x + 6`` and ``t = 6``, and additionally ``gcd(f, g) = x + 7``. This is correct because:: 
 >>> S = gf_mul(s, ZZ.map([1, 8, 7]), 11, ZZ) >>> T = gf_mul(t, ZZ.map([1, 7, 1, 7]), 11, ZZ) 
 >>> gf_add(S, T, 11, ZZ) == [1, 7] True 
 References ========== 
 1. [Gathen99]_ 
 """ return [K.one], [], [] 
 
 return [], [K.invert(p1, p)], r1 return [K.invert(p0, p)], [], r0 
 
 
 
 
 
 
 
 
 
 def gf_monic(f, p, K): """ Compute LC and a monic polynomial in ``GF(p)[x]``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_monic 
 >>> gf_monic(ZZ.map([3, 2, 4]), 5, ZZ) (3, [1, 4, 3]) 
 """ return K.zero, [] else: 
 else: 
 
 def gf_diff(f, p, K): """ Differentiate polynomial in ``GF(p)[x]``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_diff 
 >>> gf_diff([3, 2, 4], 5, ZZ) [1, 2] 
 """ 
 
 
 
 
 
 
 def gf_eval(f, a, p, K): """ Evaluate ``f(a)`` in ``GF(p)`` using Horner scheme. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_eval 
 >>> gf_eval([3, 2, 4], 2, 5, ZZ) 0 
 """ result = K.zero 
 for c in f: result *= a result += c result %= p 
 return result 
 
 def gf_multi_eval(f, A, p, K): """ Evaluate ``f(a)`` for ``a`` in ``[a_1, ..., a_n]``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_multi_eval 
 >>> gf_multi_eval([3, 2, 4], [0, 1, 2, 3, 4], 5, ZZ) [4, 4, 0, 2, 0] 
 """ return [ gf_eval(f, a, p, K) for a in A ] 
 
 def gf_compose(f, g, p, K): """ Compute polynomial composition ``f(g)`` in ``GF(p)[x]``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_compose 
 >>> gf_compose([3, 2, 4], [2, 2, 2], 5, ZZ) [2, 4, 0, 3, 0] 
 """ if len(g) <= 1: return gf_strip([gf_eval(f, gf_LC(g, K), p, K)]) 
 if not f: return [] 
 h = [f[0]] 
 for c in f[1:]: h = gf_mul(h, g, p, K) h = gf_add_ground(h, c, p, K) 
 return h 
 
 def gf_compose_mod(g, h, f, p, K): """ Compute polynomial composition ``g(h)`` in ``GF(p)[x]/(f)``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_compose_mod 
 >>> gf_compose_mod(ZZ.map([3, 2, 4]), ZZ.map([2, 2, 2]), ZZ.map([4, 3]), 5, ZZ) [4] 
 """ if not g: return [] 
 comp = [g[0]] 
 for a in g[1:]: comp = gf_mul(comp, h, p, K) comp = gf_add_ground(comp, a, p, K) comp = gf_rem(comp, f, p, K) 
 return comp 
 
 def gf_trace_map(a, b, c, n, f, p, K): """ Compute polynomial trace map in ``GF(p)[x]/(f)``. 
 Given a polynomial ``f`` in ``GF(p)[x]``, polynomials ``a``, ``b``, ``c`` in the quotient ring ``GF(p)[x]/(f)`` such that ``b = c**t (mod f)`` for some positive power ``t`` of ``p``, and a positive integer ``n``, returns a mapping:: 
 a -> a**t**n, a + a**t + a**t**2 + ... + a**t**n (mod f) 
 In factorization context, ``b = x**p mod f`` and ``c = x mod f``. This way we can efficiently compute trace polynomials in equal degree factorization routine, much faster than with other methods, like iterated Frobenius algorithm, for large degrees. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_trace_map 
 >>> gf_trace_map([1, 2], [4, 4], [1, 1], 4, [3, 2, 4], 5, ZZ) ([1, 3], [1, 3]) 
 References ========== 
 1. [Gathen92]_ 
 """ u = gf_compose_mod(a, b, f, p, K) v = b 
 if n & 1: U = gf_add(a, u, p, K) V = b else: U = a V = c 
 n >>= 1 
 while n: u = gf_add(u, gf_compose_mod(u, v, f, p, K), p, K) v = gf_compose_mod(v, v, f, p, K) 
 if n & 1: U = gf_add(U, gf_compose_mod(u, V, f, p, K), p, K) V = gf_compose_mod(v, V, f, p, K) 
 n >>= 1 
 return gf_compose_mod(a, V, f, p, K), U 
 def _gf_trace_map(f, n, g, b, p, K): """ utility for ``gf_edf_shoup`` """ f = gf_rem(f, g, p, K) h = f r = f for i in range(1, n): h = gf_frobenius_map(h, g, b, p, K) r = gf_add(r, h, p, K) r = gf_rem(r, g, p, K) return r 
 
 def gf_random(n, p, K): """ Generate a random polynomial in ``GF(p)[x]`` of degree ``n``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_random >>> gf_random(10, 5, ZZ) #doctest: +SKIP [1, 2, 3, 2, 1, 1, 1, 2, 0, 4, 2] 
 """ 
 
 def gf_irreducible(n, p, K): """ Generate random irreducible polynomial of degree ``n`` in ``GF(p)[x]``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_irreducible >>> gf_irreducible(10, 5, ZZ) #doctest: +SKIP [1, 4, 2, 2, 3, 2, 4, 1, 4, 0, 4] 
 """ while True: f = gf_random(n, p, K) if gf_irreducible_p(f, p, K): return f 
 
 def gf_irred_p_ben_or(f, p, K): """ Ben-Or's polynomial irreducibility test over finite fields. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_irred_p_ben_or 
 >>> gf_irred_p_ben_or(ZZ.map([1, 4, 2, 2, 3, 2, 4, 1, 4, 0, 4]), 5, ZZ) True >>> gf_irred_p_ben_or(ZZ.map([3, 2, 4]), 5, ZZ) False 
 """ n = gf_degree(f) 
 if n <= 1: return True 
 _, f = gf_monic(f, p, K) if n < 5: H = h = gf_pow_mod([K.one, K.zero], p, f, p, K) 
 for i in range(0, n//2): g = gf_sub(h, [K.one, K.zero], p, K) 
 if gf_gcd(f, g, p, K) == [K.one]: h = gf_compose_mod(h, H, f, p, K) else: return False else: b = gf_frobenius_monomial_base(f, p, K) H = h = gf_frobenius_map([K.one, K.zero], f, b, p, K) for i in range(0, n//2): g = gf_sub(h, [K.one, K.zero], p, K) if gf_gcd(f, g, p, K) == [K.one]: h = gf_frobenius_map(h, f, b, p, K) else: return False 
 return True 
 
 def gf_irred_p_rabin(f, p, K): """ Rabin's polynomial irreducibility test over finite fields. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_irred_p_rabin 
 >>> gf_irred_p_rabin(ZZ.map([1, 4, 2, 2, 3, 2, 4, 1, 4, 0, 4]), 5, ZZ) True >>> gf_irred_p_rabin(ZZ.map([3, 2, 4]), 5, ZZ) False 
 """ n = gf_degree(f) 
 if n <= 1: return True 
 _, f = gf_monic(f, p, K) 
 x = [K.one, K.zero] 
 indices = { n//d for d in factorint(n) } 
 b = gf_frobenius_monomial_base(f, p, K) h = b[1] 
 for i in range(1, n): if i in indices: g = gf_sub(h, x, p, K) 
 if gf_gcd(f, g, p, K) != [K.one]: return False 
 h = gf_frobenius_map(h, f, b, p, K) 
 return h == x 
 _irred_methods = { 'ben-or': gf_irred_p_ben_or, 'rabin': gf_irred_p_rabin, } 
 
 def gf_irreducible_p(f, p, K): """ Test irreducibility of a polynomial ``f`` in ``GF(p)[x]``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_irreducible_p 
 >>> gf_irreducible_p(ZZ.map([1, 4, 2, 2, 3, 2, 4, 1, 4, 0, 4]), 5, ZZ) True >>> gf_irreducible_p(ZZ.map([3, 2, 4]), 5, ZZ) False 
 """ method = query('GF_IRRED_METHOD') 
 if method is not None: irred = _irred_methods[method](f, p, K) else: irred = gf_irred_p_rabin(f, p, K) 
 return irred 
 
 def gf_sqf_p(f, p, K): """ Return ``True`` if ``f`` is square-free in ``GF(p)[x]``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_sqf_p 
 >>> gf_sqf_p(ZZ.map([3, 2, 4]), 5, ZZ) True >>> gf_sqf_p(ZZ.map([2, 4, 4, 2, 2, 1, 4]), 5, ZZ) False 
 """ 
 return True else: 
 
 def gf_sqf_part(f, p, K): """ Return square-free part of a ``GF(p)[x]`` polynomial. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_sqf_part 
 >>> gf_sqf_part(ZZ.map([1, 1, 3, 0, 1, 0, 2, 2, 1]), 5, ZZ) [1, 4, 3] 
 """ _, sqf = gf_sqf_list(f, p, K) 
 g = [K.one] 
 for f, _ in sqf: g = gf_mul(g, f, p, K) 
 return g 
 
 def gf_sqf_list(f, p, K, all=False): """ Return the square-free decomposition of a ``GF(p)[x]`` polynomial. 
 Given a polynomial ``f`` in ``GF(p)[x]``, returns the leading coefficient of ``f`` and a square-free decomposition ``f_1**e_1 f_2**e_2 ... f_k**e_k`` such that all ``f_i`` are monic polynomials and ``(f_i, f_j)`` for ``i != j`` are co-prime and ``e_1 ... e_k`` are given in increasing order. All trivial terms (i.e. ``f_i = 1``) aren't included in the output. 
 Consider polynomial ``f = x**11 + 1`` over ``GF(11)[x]``:: 
 >>> from sympy.polys.domains import ZZ 
 >>> from sympy.polys.galoistools import ( ... gf_from_dict, gf_diff, gf_sqf_list, gf_pow, ... ) ... # doctest: +NORMALIZE_WHITESPACE 
 >>> f = gf_from_dict({11: ZZ(1), 0: ZZ(1)}, 11, ZZ) 
 Note that ``f'(x) = 0``:: 
 >>> gf_diff(f, 11, ZZ) [] 
 This phenomenon doesn't happen in characteristic zero. However we can still compute square-free decomposition of ``f`` using ``gf_sqf()``:: 
 >>> gf_sqf_list(f, 11, ZZ) (1, [([1, 1], 11)]) 
 We obtained factorization ``f = (x + 1)**11``. This is correct because:: 
 >>> gf_pow([1, 1], 11, 11, ZZ) == f True 
 References ========== 
 1. [Geddes92]_ 
 """ n, sqf, factors, r = 1, False, [], int(p) 
 lc, f = gf_monic(f, p, K) 
 if gf_degree(f) < 1: return lc, [] 
 while True: F = gf_diff(f, p, K) 
 if F != []: g = gf_gcd(f, F, p, K) h = gf_quo(f, g, p, K) 
 i = 1 
 while h != [K.one]: G = gf_gcd(g, h, p, K) H = gf_quo(h, G, p, K) 
 if gf_degree(H) > 0: factors.append((H, i*n)) 
 g, h, i = gf_quo(g, G, p, K), G, i + 1 
 if g == [K.one]: sqf = True else: f = g 
 if not sqf: d = gf_degree(f) // r 
 for i in range(0, d + 1): f[i] = f[i*r] 
 f, n = f[:d + 1], n*r else: break 
 if all: raise ValueError("'all=True' is not supported yet") 
 return lc, factors 
 
 def gf_Qmatrix(f, p, K): """ Calculate Berlekamp's ``Q`` matrix. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_Qmatrix 
 >>> gf_Qmatrix([3, 2, 4], 5, ZZ) [[1, 0], [3, 4]] 
 >>> gf_Qmatrix([1, 0, 0, 0, 1], 5, ZZ) [[1, 0, 0, 0], [0, 4, 0, 0], [0, 0, 1, 0], [0, 0, 0, 4]] 
 """ n, r = gf_degree(f), int(p) 
 q = [K.one] + [K.zero]*(n - 1) Q = [list(q)] + [[]]*(n - 1) 
 for i in range(1, (n - 1)*r + 1): qq, c = [(-q[-1]*f[-1]) % p], q[-1] 
 for j in range(1, n): qq.append((q[j - 1] - c*f[-j - 1]) % p) 
 if not (i % r): Q[i//r] = list(qq) 
 q = qq 
 return Q 
 
 def gf_Qbasis(Q, p, K): """ Compute a basis of the kernel of ``Q``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_Qmatrix, gf_Qbasis 
 >>> gf_Qbasis(gf_Qmatrix([1, 0, 0, 0, 1], 5, ZZ), 5, ZZ) [[1, 0, 0, 0], [0, 0, 1, 0]] 
 >>> gf_Qbasis(gf_Qmatrix([3, 2, 4], 5, ZZ), 5, ZZ) [[1, 0]] 
 """ Q, n = [ list(q) for q in Q ], len(Q) 
 for k in range(0, n): Q[k][k] = (Q[k][k] - K.one) % p 
 for k in range(0, n): for i in range(k, n): if Q[k][i]: break else: continue 
 inv = K.invert(Q[k][i], p) 
 for j in range(0, n): Q[j][i] = (Q[j][i]*inv) % p 
 for j in range(0, n): t = Q[j][k] Q[j][k] = Q[j][i] Q[j][i] = t 
 for i in range(0, n): if i != k: q = Q[k][i] 
 for j in range(0, n): Q[j][i] = (Q[j][i] - Q[j][k]*q) % p 
 for i in range(0, n): for j in range(0, n): if i == j: Q[i][j] = (K.one - Q[i][j]) % p else: Q[i][j] = (-Q[i][j]) % p 
 basis = [] 
 for q in Q: if any(q): basis.append(q) 
 return basis 
 
 def gf_berlekamp(f, p, K): """ Factor a square-free ``f`` in ``GF(p)[x]`` for small ``p``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_berlekamp 
 >>> gf_berlekamp([1, 0, 0, 0, 1], 5, ZZ) [[1, 0, 2], [1, 0, 3]] 
 """ Q = gf_Qmatrix(f, p, K) V = gf_Qbasis(Q, p, K) 
 for i, v in enumerate(V): V[i] = gf_strip(list(reversed(v))) 
 factors = [f] 
 for k in range(1, len(V)): for f in list(factors): s = K.zero 
 while s < p: g = gf_sub_ground(V[k], s, p, K) h = gf_gcd(f, g, p, K) 
 if h != [K.one] and h != f: factors.remove(f) 
 f = gf_quo(f, h, p, K) factors.extend([f, h]) 
 if len(factors) == len(V): return _sort_factors(factors, multiple=False) 
 s += K.one 
 return _sort_factors(factors, multiple=False) 
 
 def gf_ddf_zassenhaus(f, p, K): """ Cantor-Zassenhaus: Deterministic Distinct Degree Factorization 
 Given a monic square-free polynomial ``f`` in ``GF(p)[x]``, computes partial distinct degree factorization ``f_1 ... f_d`` of ``f`` where ``deg(f_i) != deg(f_j)`` for ``i != j``. The result is returned as a list of pairs ``(f_i, e_i)`` where ``deg(f_i) > 0`` and ``e_i > 0`` is an argument to the equal degree factorization routine. 
 Consider the polynomial ``x**15 - 1`` in ``GF(11)[x]``:: 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_from_dict 
 >>> f = gf_from_dict({15: ZZ(1), 0: ZZ(-1)}, 11, ZZ) 
 Distinct degree factorization gives:: 
 >>> from sympy.polys.galoistools import gf_ddf_zassenhaus 
 >>> gf_ddf_zassenhaus(f, 11, ZZ) [([1, 0, 0, 0, 0, 10], 1), ([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2)] 
 which means ``x**15 - 1 = (x**5 - 1) (x**10 + x**5 + 1)``. To obtain factorization into irreducibles, use equal degree factorization procedure (EDF) with each of the factors. 
 References ========== 
 1. [Gathen99]_ 2. [Geddes92]_ 
 """ 
 
 
 
 
 else: 
 
 def gf_edf_zassenhaus(f, n, p, K): """ Cantor-Zassenhaus: Probabilistic Equal Degree Factorization 
 Given a monic square-free polynomial ``f`` in ``GF(p)[x]`` and an integer ``n``, such that ``n`` divides ``deg(f)``, returns all irreducible factors ``f_1,...,f_d`` of ``f``, each of degree ``n``. EDF procedure gives complete factorization over Galois fields. 
 Consider the square-free polynomial ``f = x**3 + x**2 + x + 1`` in ``GF(5)[x]``. Let's compute its irreducible factors of degree one:: 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_edf_zassenhaus 
 >>> gf_edf_zassenhaus([1,1,1,1], 1, 5, ZZ) [[1, 1], [1, 2], [1, 3]] 
 References ========== 
 1. [Gathen99]_ 2. [Geddes92]_ 
 """ 
 
 
 
 h = r 
 for i in range(0, 2**(n*N - 1)): r = gf_pow_mod(r, 2, f, p, K) h = gf_add(h, r, p, K) 
 g = gf_gcd(f, h, p, K) else: 
 + gf_edf_zassenhaus(gf_quo(f, g, p, K), n, p, K) 
 
 
 def gf_ddf_shoup(f, p, K): """ Kaltofen-Shoup: Deterministic Distinct Degree Factorization 
 Given a monic square-free polynomial ``f`` in ``GF(p)[x]``, computes partial distinct degree factorization ``f_1,...,f_d`` of ``f`` where ``deg(f_i) != deg(f_j)`` for ``i != j``. The result is returned as a list of pairs ``(f_i, e_i)`` where ``deg(f_i) > 0`` and ``e_i > 0`` is an argument to the equal degree factorization routine. 
 This algorithm is an improved version of Zassenhaus algorithm for large ``deg(f)`` and modulus ``p`` (especially for ``deg(f) ~ lg(p)``). 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_ddf_shoup, gf_from_dict 
 >>> f = gf_from_dict({6: ZZ(1), 5: ZZ(-1), 4: ZZ(1), 3: ZZ(1), 1: ZZ(-1)}, 3, ZZ) 
 >>> gf_ddf_shoup(f, 3, ZZ) [([1, 1, 0], 1), ([1, 1, 0, 1, 2], 2)] 
 References ========== 
 1. [Kaltofen98]_ 2. [Shoup95]_ 3. [Gathen92]_ 
 """ n = gf_degree(f) k = int(_ceil(_sqrt(n//2))) b = gf_frobenius_monomial_base(f, p, K) h = gf_frobenius_map([K.one, K.zero], f, b, p, K) # U[i] = x**(p**i) U = [[K.one, K.zero], h] + [K.zero]*(k - 1) 
 for i in range(2, k + 1): U[i] = gf_frobenius_map(U[i-1], f, b, p, K) 
 h, U = U[k], U[:k] # V[i] = x**(p**(k*(i+1))) V = [h] + [K.zero]*(k - 1) 
 for i in range(1, k): V[i] = gf_compose_mod(V[i - 1], h, f, p, K) 
 factors = [] 
 for i, v in enumerate(V): h, j = [K.one], k - 1 
 for u in U: g = gf_sub(v, u, p, K) h = gf_mul(h, g, p, K) h = gf_rem(h, f, p, K) 
 g = gf_gcd(f, h, p, K) f = gf_quo(f, g, p, K) 
 for u in reversed(U): h = gf_sub(v, u, p, K) F = gf_gcd(g, h, p, K) 
 if F != [K.one]: factors.append((F, k*(i + 1) - j)) 
 g, j = gf_quo(g, F, p, K), j - 1 
 if f != [K.one]: factors.append((f, gf_degree(f))) 
 return factors 
 def gf_edf_shoup(f, n, p, K): """ Gathen-Shoup: Probabilistic Equal Degree Factorization 
 Given a monic square-free polynomial ``f`` in ``GF(p)[x]`` and integer ``n`` such that ``n`` divides ``deg(f)``, returns all irreducible factors ``f_1,...,f_d`` of ``f``, each of degree ``n``. This is a complete factorization over Galois fields. 
 This algorithm is an improved version of Zassenhaus algorithm for large ``deg(f)`` and modulus ``p`` (especially for ``deg(f) ~ lg(p)``). 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_edf_shoup 
 >>> gf_edf_shoup(ZZ.map([1, 2837, 2277]), 1, 2917, ZZ) [[1, 852], [1, 1985]] 
 References ========== 
 1. [Shoup91]_ 2. [Gathen92]_ 
 """ N, q = gf_degree(f), int(p) 
 if not N: return [] if N <= n: return [f] 
 factors, x = [f], [K.one, K.zero] 
 r = gf_random(N - 1, p, K) 
 if p == 2: h = gf_pow_mod(x, q, f, p, K) H = gf_trace_map(r, h, x, n - 1, f, p, K)[1] h1 = gf_gcd(f, H, p, K) h2 = gf_quo(f, h1, p, K) 
 factors = gf_edf_shoup(h1, n, p, K) \ + gf_edf_shoup(h2, n, p, K) else: b = gf_frobenius_monomial_base(f, p, K) H = _gf_trace_map(r, n, f, b, p, K) h = gf_pow_mod(H, (q - 1)//2, f, p, K) 
 h1 = gf_gcd(f, h, p, K) h2 = gf_gcd(f, gf_sub_ground(h, K.one, p, K), p, K) h3 = gf_quo(f, gf_mul(h1, h2, p, K), p, K) 
 factors = gf_edf_shoup(h1, n, p, K) \ + gf_edf_shoup(h2, n, p, K) \ + gf_edf_shoup(h3, n, p, K) 
 return _sort_factors(factors, multiple=False) 
 
 def gf_zassenhaus(f, p, K): """ Factor a square-free ``f`` in ``GF(p)[x]`` for medium ``p``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_zassenhaus 
 >>> gf_zassenhaus(ZZ.map([1, 4, 3]), 5, ZZ) [[1, 1], [1, 3]] 
 """ 
 
 
 
 def gf_shoup(f, p, K): """ Factor a square-free ``f`` in ``GF(p)[x]`` for large ``p``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_shoup 
 >>> gf_shoup(ZZ.map([1, 4, 3]), 5, ZZ) [[1, 1], [1, 3]] 
 """ factors = [] 
 for factor, n in gf_ddf_shoup(f, p, K): factors += gf_edf_shoup(factor, n, p, K) 
 return _sort_factors(factors, multiple=False) 
 _factor_methods = { 'berlekamp': gf_berlekamp, # ``p`` : small 'zassenhaus': gf_zassenhaus, # ``p`` : medium 'shoup': gf_shoup, # ``p`` : large } 
 
 def gf_factor_sqf(f, p, K, method=None): """ Factor a square-free polynomial ``f`` in ``GF(p)[x]``. 
 Examples ======== 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_factor_sqf 
 >>> gf_factor_sqf(ZZ.map([3, 2, 4]), 5, ZZ) (3, [[1, 1], [1, 3]]) 
 """ 
 return lc, [] 
 
 else: factors = gf_zassenhaus(f, p, K) 
 
 
 def gf_factor(f, p, K): """ Factor (non square-free) polynomials in ``GF(p)[x]``. 
 Given a possibly non square-free polynomial ``f`` in ``GF(p)[x]``, returns its complete factorization into irreducibles:: 
 f_1(x)**e_1 f_2(x)**e_2 ... f_d(x)**e_d 
 where each ``f_i`` is a monic polynomial and ``gcd(f_i, f_j) == 1``, for ``i != j``. The result is given as a tuple consisting of the leading coefficient of ``f`` and a list of factors of ``f`` with their multiplicities. 
 The algorithm proceeds by first computing square-free decomposition of ``f`` and then iteratively factoring each of square-free factors. 
 Consider a non square-free polynomial ``f = (7*x + 1) (x + 2)**2`` in ``GF(11)[x]``. We obtain its factorization into irreducibles as follows:: 
 >>> from sympy.polys.domains import ZZ >>> from sympy.polys.galoistools import gf_factor 
 >>> gf_factor(ZZ.map([5, 2, 7, 2]), 11, ZZ) (5, [([1, 2], 1), ([1, 8], 2)]) 
 We arrived with factorization ``f = 5 (x + 2) (x + 8)**2``. We didn't recover the exact form of the input polynomial because we requested to get monic factors of ``f`` and its leading coefficient separately. 
 Square-free factors of ``f`` can be factored into irreducibles over ``GF(p)`` using three very different methods: 
 Berlekamp efficient for very small values of ``p`` (usually ``p < 25``) Cantor-Zassenhaus efficient on average input and with "typical" ``p`` Shoup-Kaltofen-Gathen efficient with very large inputs and modulus 
 If you want to use a specific factorization method, instead of the default one, set ``GF_FACTOR_METHOD`` with one of ``berlekamp``, ``zassenhaus`` or ``shoup`` values. 
 References ========== 
 1. [Gathen99]_ 
 """ lc, f = gf_monic(f, p, K) 
 if gf_degree(f) < 1: return lc, [] 
 factors = [] 
 for g, n in gf_sqf_list(f, p, K)[1]: for h in gf_factor_sqf(g, p, K)[1]: factors.append((h, n)) 
 return lc, _sort_factors(factors) 
 
 def gf_value(f, a): """ Value of polynomial 'f' at 'a' in field R. 
 Examples ======== 
 >>> from sympy.polys.galoistools import gf_value 
 >>> gf_value([1, 7, 2, 4], 11) 2204 
 """ result = 0 for c in f: result *= a result += c return result 
 
 def linear_congruence(a, b, m): """ Returns the values of x satisfying a*x congruent b mod(m) 
 Here m is positive integer and a, b are natural numbers. This function returns only those values of x which are distinct mod(m). 
 Examples ======== 
 >>> from sympy.polys.galoistools import linear_congruence 
 >>> linear_congruence(3, 12, 15) [4, 9, 14] 
 There are 3 solutions distinct mod(15) since gcd(a, m) = gcd(3, 15) = 3. 
 **Reference** 1) Wikipedia http://en.wikipedia.org/wiki/Linear_congruence_theorem 
 """ from sympy.polys.polytools import gcdex if a % m == 0: if b % m == 0: return list(range(m)) else: return [] r, _, g = gcdex(a, m) if b % g != 0: return [] return [(r * b // g + t * m // g) % m for t in range(g)] 
 
 def _raise_mod_power(x, s, p, f): """ Used in gf_csolve to generate solutions of f(x) cong 0 mod(p**(s + 1)) from the solutions of f(x) cong 0 mod(p**s). 
 Examples ======== 
 >>> from sympy.polys.galoistools import _raise_mod_power >>> from sympy.polys.galoistools import csolve_prime 
 These is the solutions of f(x) = x**2 + x + 7 cong 0 mod(3) 
 >>> f = [1, 1, 7] >>> csolve_prime(f, 3) [1] >>> [ i for i in range(3) if not (i**2 + i + 7) % 3] [1] 
 The solutions of f(x) cong 0 mod(9) are constructed from the values returned from _raise_mod_power: 
 >>> x, s, p = 1, 1, 3 >>> V = _raise_mod_power(x, s, p, f) >>> [x + v * p**s for v in V] [1, 4, 7] 
 And these are confirmed with the following: 
 >>> [ i for i in range(3**2) if not (i**2 + i + 7) % 3**2] [1, 4, 7] 
 """ from sympy.polys.domains import ZZ f_f = gf_diff(f, p, ZZ) alpha = gf_value(f_f, x) beta = - gf_value(f, x) // p**s return linear_congruence(alpha, beta, p) 
 
 def csolve_prime(f, p, e=1): """ Solutions of f(x) congruent 0 mod(p**e). 
 Examples ======== 
 >>> from sympy.polys.galoistools import csolve_prime 
 >>> csolve_prime([1, 1, 7], 3, 1) [1] >>> csolve_prime([1, 1, 7], 3, 2) [1, 4, 7] 
 Solutions [7, 4, 1] (mod 3**2) are generated by ``_raise_mod_power()`` from solution [1] (mod 3). """ from sympy.polys.domains import ZZ X1 = [i for i in range(p) if gf_eval(f, i, p, ZZ) == 0] if e == 1: return X1 X = [] S = list(zip(X1, [1]*len(X1))) while S: x, s = S.pop() if s == e: X.append(x) else: s1 = s + 1 ps = p**s S.extend([(x + v*ps, s1) for v in _raise_mod_power(x, s, p, f)]) return sorted(X) 
 
 def gf_csolve(f, n): """ To solve f(x) congruent 0 mod(n). 
 n is divided into canonical factors and f(x) cong 0 mod(p**e) will be solved for each factor. Applying the Chinese Remainder Theorem to the results returns the final answers. 
 Examples ======== 
 Solve [1, 1, 7] congruent 0 mod(189): 
 >>> from sympy.polys.galoistools import gf_csolve >>> gf_csolve([1, 1, 7], 189) [13, 49, 76, 112, 139, 175] 
 References ========== 
 [1] 'An introduction to the Theory of Numbers' 5th Edition by Ivan Niven, Zuckerman and Montgomery. 
 """ from sympy.polys.domains import ZZ P = factorint(n) X = [csolve_prime(f, p, e) for p, e in P.items()] pools = list(map(tuple, X)) perms = [[]] for pool in pools: perms = [x + [y] for x in perms for y in pool] dist_factors = [pow(p, e) for p, e in P.items()] return sorted([gf_crt(per, dist_factors, ZZ) for per in perms])  |