B
    [                 @   s  d Z ddlmZmZ ddlmZ ddlmZm	Z
 ddlmZmZ ddlmZ ddlmZ ddlmZ dd	lmZ dd
lmZ dddZdd Zdd Zdd Zdd Zdd Zdd Zdd Zdd Z dd Z!d d! Z"dd#d$Z#d%d& Z$dd'd(Z%d)d* Z&d+d, Z'd-d. Z(d/d0 Z)d1d2 Z*d3d4 Z+d5d6 Z,d7d8 Z-d9d: Z.d;d< Z/d=d> Z0d?d@ Z1dAdB Z2dCdD Z3dEdF Z4dGdH Z5dIdJ Z6dKdL Z7dMdN Z8dOdP Z9dQdR Z:dSdT Z;dUdV Z<dWdX Z=dYdZ Z>d[d\ Z?d]d^ Z@d_d` ZAdadb ZBdcdd ZCdedf ZDdgdh ZEdidj ZFdkdl ZGdmdn ZHdodp ZIdqdr ZJdsdt ZKdudv ZLeKeLdwZMdxdy ZNdzd{ ZOd|d} ZPdddZQdd ZRdd ZSdd ZTdd ZUdd ZVdd ZWdd ZXdd ZYdd ZZeTeYeZdZ[dddZ\dd Z]dd Z^dd Z_dd Z`dddZadd ZbdS )zADense univariate polynomials with coefficients in Galois fields.     )print_functiondivision)uniform)ceilsqrt)
SYMPY_INTSrange)prod)_sort_factors)query)ExactQuotientFailed)	factorintNc       
      C   sb   t ||jd}|j}xDt| |D ]6\}}|| }|||\}}	}	|||| |  7 }q W || S )ay  
    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

    )start)r	   onezerozipgcdex)
UMKpvumes_ r   6lib/python3.7/site-packages/sympy/polys/galoistools.pygf_crt   s     r   c             C   s\   g g  }}t | |jd}x8| D ]0}|||  |||d |d |  qW |||fS )a  
    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])

    )r   r   )r	   r   appendr   )r   r   ESr   r   r   r   r   gf_crt1;   s    

"r$   c             C   sB   |j }x2t| |||D ] \}}}	}
||	||
 |  7 }qW || S )a`  
    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

    )r   r   )r   r   r   r"   r#   r   r   r   r   r   r   r   r   r   gf_crt2S   s    r%   c             C   s   | |d kr| S | | S dS )z
    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

       Nr   )ar   r   r   r   gf_into   s    r(   c             C   s   t | d S )z
    Return the leading degree of ``f``.

    Examples
    ========

    >>> from sympy.polys.galoistools import gf_degree

    >>> gf_degree([1, 1, 2, 0])
    3
    >>> gf_degree([])
    -1

       )len)fr   r   r   	gf_degree   s    r,   c             C   s   | s
|j S | d S dS )z
    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

    r   N)r   )r+   r   r   r   r   gf_LC   s    r-   c             C   s   | s
|j S | d S dS )z
    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

    r    N)r   )r+   r   r   r   r   gf_TC   s    r.   c             C   s>   | r| d r| S d}x| D ]}|r&P q|d7 }qW | |d S )z
    Remove leading zeros from ``f``.


    Examples
    ========

    >>> from sympy.polys.galoistools import gf_strip

    >>> gf_strip([0, 0, 0, 3, 0, 1])
    [3, 0, 1]

    r   r)   Nr   )r+   kcoeffr   r   r   gf_strip   s    
r1   c                s   t  fdd| D S )z
    Reduce all coefficients modulo ``p``.

    Examples
    ========

    >>> from sympy.polys.galoistools import gf_trunc

    >>> gf_trunc([7, -2, 3], 5)
    [2, 3, 3]

    c                s   g | ]}|  qS r   r   ).0r'   )r   r   r   
<listcomp>   s    zgf_trunc.<locals>.<listcomp>)r1   )r+   r   r   )r   r   gf_trunc   s    r4   c             C   s   t tt|| |S )z
    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]

    )r4   listmap)r+   r   r   r   r   r   	gf_normal   s    r7   c             C   s   t |  g  }}t|trLxdt|ddD ]}|| ||j|  q*W n6|\}x.t|ddD ]}|| |f|j|  q`W t||S )a  
    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]

    r    )	maxkeys
isinstancer   r   r!   getr   r4   )r+   r   r   nhr/   r   r   r   gf_from_dict   s    
r>   Tc             C   sZ   t | i  }}xFtd|d D ]4}|r:t| ||  |}n| ||  }|r|||< qW |S )aA  
    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}

    r   r)   )r,   r   r(   )r+   r   	symmetricr<   resultr/   r'   r   r   r   
gf_to_dict  s    rA   c             C   s
   t | |S )z
    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]

    )r4   )r+   r   r   r   r   gf_from_int_poly4  s    rB   c                s   |r fdd| D S | S dS )a  
    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]

    c                s   g | ]}t | qS r   )r(   )r2   c)r   r   r   r3   V  s    z"gf_to_int_poly.<locals>.<listcomp>Nr   )r+   r   r?   r   )r   r   gf_to_int_polyE  s    rD   c                s    fdd| D S )z
    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]

    c                s   g | ]}|   qS r   r   )r2   r0   )r   r   r   r3   i  s    zgf_neg.<locals>.<listcomp>r   )r+   r   r   r   )r   r   gf_neg[  s    rE   c             C   sN   | s|| }n.| d | | }t | dkr<| dd |g S |sDg S |gS dS )a  
    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]

    r    r)   N)r*   )r+   r'   r   r   r   r   r   gf_add_groundl  s    
rF   c             C   sP   | s| | }n.| d | | }t | dkr>| dd |g S |sFg S |gS dS )a  
    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]

    r    r)   N)r*   )r+   r'   r   r   r   r   r   gf_sub_ground  s    rG   c                s     sg S  fdd| D S dS )a  
    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]

    c                s   g | ]} |  qS r   r   )r2   b)r'   r   r   r   r3     s    z!gf_mul_ground.<locals>.<listcomp>Nr   )r+   r'   r   r   r   )r'   r   r   gf_mul_ground  s    rI   c             C   s   t | |||||S )a  
    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]

    )rI   invert)r+   r'   r   r   r   r   r   gf_quo_ground  s    rK   c                s   | s|S |s| S t | }t |}||krDt fddt| |D S t|| }||krt| d| | |d  }} n|d| ||d  }}| fddt| |D  S dS )z
    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]

    c                s   g | ]\}}||   qS r   r   )r2   r'   rH   )r   r   r   r3     s    zgf_add.<locals>.<listcomp>Nc                s   g | ]\}}||   qS r   r   )r2   r'   rH   )r   r   r   r3     s    )r,   r1   r   abs)r+   gr   r   dfdgr/   r=   r   )r   r   gf_add  s    rP   c                s   |s| S | st | |S t| }t|}||krLt fddt| |D S t|| }||kr|| d| | |d  }} n"t |d|  |||d  }}| fddt| |D  S dS )z
    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]

    c                s   g | ]\}}||   qS r   r   )r2   r'   rH   )r   r   r   r3     s    zgf_sub.<locals>.<listcomp>Nc                s   g | ]\}}||   qS r   r   )r2   r'   rH   )r   r   r   r3     s    )rE   r,   r1   r   rL   )r+   rM   r   r   rN   rO   r/   r=   r   )r   r   gf_sub  s    "rQ   c             C   s   t | }t |}|| }dg|d  }xhtd|d D ]V}|j}	x>ttd|| t||d D ]}
|	| |
 |||
   7 }	q`W |	| ||< q6W t|S )z
    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]

    r   r)   )r,   r   r   r8   minr1   )r+   rM   r   r   rN   rO   dhr=   ir0   jr   r   r   gf_mul  s    $rV   c             C   s   t | }d| }dg|d  }xtd|d D ]}|j}td|| }t||}	|	| d }
||
d  d }	x.t||	d D ]}|| | | ||   7 }q|W ||7 }|
d@ r| |	d  }||d 7 }|| ||< q.W t|S )z
    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]

    r&   r   r)   )r,   r   r   r8   rR   r1   )r+   r   r   rN   rS   r=   rT   r0   ZjminZjmaxr<   rU   elemr   r   r   gf_sqr.  s"    
rX   c             C   s   t | t||||||S )a  
    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]
    )rP   rV   )r+   rM   r=   r   r   r   r   r   
gf_add_mulY  s    rY   c             C   s   t | t||||||S )a  
    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]

    )rQ   rV   )r+   rM   r=   r   r   r   r   r   
gf_sub_mulh  s    rZ   c             C   sT   t | tkr| \}} n|j}|g}x,| D ]$\}}t||||}t||||}q(W |S )a  
    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]

    )typetupler   gf_powrV   )Fr   r   lcrM   r+   r/   r   r   r   	gf_expandy  s    
r`   c             C   s  t | }t |}|stdn||k r.g | fS ||d |}t| || |d   }}}	xtd|d D ]t}
||
 }xJttd||
 t||
 |	d D ]$}|||
| |  |||   8 }qW |
|kr||9 }|| ||
< qjW |d|d  t||d d fS )a  
    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]_

    zpolynomial divisionr   r)   N)r,   ZeroDivisionErrorrJ   r5   r   r8   rR   r1   )r+   rM   r   r   rN   rO   invr=   dqdrrT   r0   rU   r   r   r   gf_div  s     
($re   c             C   s   t | |||d S )z
    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]

    r)   )re   )r+   rM   r   r   r   r   r   gf_rem  s    rf   c             C   s   t | }t |}|stdn||k r*g S ||d |}| dd || |d   }}}	xztd|d D ]h}
||
 }xJttd||
 t||
 |	d D ]$}|||
| |  |||   8 }qW || | ||
< qjW |d|d  S )aG  
    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]

    zpolynomial divisionr   Nr)   )r,   ra   rJ   r   r8   rR   )r+   rM   r   r   rN   rO   rb   r=   rc   rd   rT   r0   rU   r   r   r   gf_quo  s    
 ($rg   c             C   s(   t | |||\}}|s|S t| |dS )a  
    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]

    N)re   r   )r+   rM   r   r   qrr   r   r   gf_exquo  s    rj   c             C   s   | s| S | |j g|  S dS )z
    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]

    N)r   )r+   r<   r   r   r   r   	gf_lshift  s    rk   c             C   s,   |s| g fS | d|  | | d fS dS )z
    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])

    Nr   )r+   r<   r   r   r   r   	gf_rshift2  s    rl   c             C   sv   |s|j gS |dkr| S |dkr,t| ||S |j g}x<|d@ rTt|| ||}|d8 }|dL }|sbP t| ||} q6W |S )z
    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]

    r)   r&   )r   rX   rV   )r+   r<   r   r   r=   r   r   r   r]   F  s     r]   c             C   s   t | }|dkrg S dg| }dg|d< ||k rlxtd|D ]*}t||d  ||}t|| ||||< q<W nl|dkrt|j|jg|| |||d< xFtd|D ]8}t||d  |d ||||< t|| | ||||< qW |S )ah  
    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]]

    r   r)   r&   )r,   r   rk   rf   
gf_pow_modr   r   rV   )rM   r   r   r<   rH   rT   Zmonr   r   r   gf_frobenius_monomial_basek  s    

rn   c       
      C   s   t |}t | |kr"t| |||} | s*g S t | }| d g}x>td|d D ],}t|| | ||  ||}	t||	||}qLW |S )aT  
    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]
    r    r)   )r,   rf   r   rI   rP   )
r+   rM   rH   r   r   r   r<   ZsfrT   r   r   r   r   gf_frobenius_map  s    
ro   c       
      C   sr   t | |||} | }| }x>td|D ]0}t|||||}t||||}t ||||}q"W t||d d |||}	|	S )z
    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)``
    r)   r&   )rf   r   ro   rV   rm   )
r+   r<   rM   rH   r   r   r=   ri   rT   Zresr   r   r   _gf_pow_pnm1d2  s    rp   c             C   s   |s|j gS |dkr"t| |||S |dkr@tt| |||||S |j g}xX|d@ rvt|| ||}t||||}|d8 }|dL }|sP t| ||} t| |||} qJW |S )a&  
    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]_

    r)   r&   )r   rf   rX   rV   )r+   r<   rM   r   r   r=   r   r   r   rm     s$    rm   c             C   s.   x|r|t | ||| } }qW t| ||d S )z
    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]

    r)   )rf   gf_monic)r+   rM   r   r   r   r   r   gf_gcd  s    rr   c             C   s>   | r|sg S t t| |||t| |||||}t|||d S )z
    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]

    r)   )rg   rV   rr   rq   )r+   rM   r   r   r=   r   r   r   gf_lcm  s
    rs   c             C   s>   | s|sg g g fS t | |||}|t| |||t||||fS )a   
    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])

    )rr   rg   )r+   rM   r   r   r=   r   r   r   gf_cofactors  s
    
rt   c             C   s   | s|s|j gg g fS t| ||\}}t|||\}}| sNg |||g|fS |sf|||gg |fS |||gg  }}	g |||g }
}xt||||\}}|sP t|||| \}}}|||}t||	|||}t|
||||}t|||||	 }	}t||||| }}
qW |	||fS )a  
    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]_

    )r   rq   rJ   re   rZ   rI   )r+   rM   r   r   Zp0Zr0Zp1Zr1Zs0s1Zt0Zt1QRr_   rb   r   tr   r   r   gf_gcdex4  s*    !ry   c             C   sB   | s|j g fS | d }||r,|t| fS |t| |||fS dS )z
    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])

    r   N)r   Zis_oner5   rK   )r+   r   r   r_   r   r   r   rq   v  s    

rq   c             C   sd   t | }|jg| | }}x@| dd D ]0}|||9 }||; }|rP|||| < |d8 }q(W t|S )z
    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]

    Nr    r)   )r,   r   r1   )r+   r   r   rN   r=   r<   r0   r   r   r   gf_diff  s    rz   c             C   s0   |j }x$| D ]}||9 }||7 }||; }qW |S )z
    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

    )r   )r+   r'   r   r   r@   rC   r   r   r   gf_eval  s    
r{   c                s    fdd|D S )a  
    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]

    c                s   g | ]}t | qS r   )r{   )r2   r'   )r   r+   r   r   r   r3     s    z!gf_multi_eval.<locals>.<listcomp>r   )r+   Ar   r   r   )r   r+   r   r   gf_multi_eval  s    r}   c             C   sn   t |dkr&tt| t||||gS | s.g S | d g}x0| dd D ] }t||||}t||||}qFW |S )a  
    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]

    r)   r   N)r*   r1   r{   r-   rV   rF   )r+   rM   r   r   r=   rC   r   r   r   
gf_compose  s    
r~   c             C   sV   | sg S | d g}x>| dd D ].}t ||||}t||||}t||||}q W |S )a&  
    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]

    r   r)   N)rV   rF   rf   )rM   r=   r+   r   r   compr'   r   r   r   gf_compose_mod  s    
r   c             C   s   t | ||||}|}|d@ r0t| |||}	|}
n| }	|}
|dL }xl|rt|t |||||||}t |||||}|d@ rt|	t ||
|||||}	t ||
|||}
|dL }qBW t | |
||||	fS )a  
    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]_

    r)   )r   rP   )r'   rH   rC   r<   r+   r   r   r   r   r   Vr   r   r   gf_trace_map  s     r   c       	      C   sZ   t | |||} | }| }x>td|D ]0}t|||||}t||||}t ||||}q"W |S )z&
    utility for ``gf_edf_shoup``
    r)   )rf   r   ro   rP   )	r+   r<   rM   rH   r   r   r=   ri   rT   r   r   r   _gf_trace_mapE  s    r   c                s"    j g fddtd| D  S )a  
    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]

    c                s   g | ]} t td qS )r   )intr   )r2   rT   )r   r   r   r   r3   `  s    zgf_random.<locals>.<listcomp>r   )r   r   )r<   r   r   r   )r   r   r   	gf_randomS  s    r   c             C   s&   x t | ||}t|||r|S qW dS )a,  
    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]

    N)r   gf_irreducible_p)r<   r   r   r+   r   r   r   gf_irreduciblec  s    r   c       
      C   s,  t | }|dkrdS t| ||\}} |dk rt|j|jg|| || }}xtd|d D ]F}t||j|jg||}t| ||||jgkrt||| ||}qXdS qXW nt	| ||}	t
|j|jg| |	|| }}xZtd|d D ]H}t||j|jg||}t| ||||jgkr t
|| |	||}qdS qW dS )a_  
    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

    r)   T   r   r&   F)r,   rq   rm   r   r   r   rQ   rr   r   rn   ro   )
r+   r   r   r<   r   Hr=   rT   rM   rH   r   r   r   gf_irred_p_ben_orv  s&    
r   c       
         s   t |   dkrdS t| ||\}} |j|jg} fddt D }t| ||}|d }xRtd D ]D}||krt||||}	t| |	|||jgkrdS t	|| |||}qfW ||kS )a[  
    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

    r)   Tc                s   h | ]} | qS r   r   )r2   d)r<   r   r   	<setcomp>  s    z#gf_irred_p_rabin.<locals>.<setcomp>F)
r,   rq   r   r   r   rn   r   rQ   rr   ro   )
r+   r   r   r   xindicesrH   r=   rT   rM   r   )r<   r   gf_irred_p_rabin  s    r   )zben-orZrabinc             C   s2   t d}|dk	r"t| | ||}nt| ||}|S )a[  
    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

    ZGF_IRRED_METHODN)r   _irred_methodsr   )r+   r   r   methodZirredr   r   r   r     s
    r   c             C   s:   t | ||\}} | sdS t| t| |||||jgkS dS )a5  
    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

    TN)rq   rr   rz   r   )r+   r   r   r   r   r   r   gf_sqf_p  s    r   c             C   s<   t | ||\}}|jg}x|D ]\} }t|| ||}qW |S )a  
    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]

    )gf_sqf_listr   rV   )r+   r   r   r   sqfrM   r   r   r   gf_sqf_part  s
    r   Fc             C   sh  ddg t |f\}}}}t| ||\}} t| dk r<|g fS xt| ||}	|	g krt| |	||}
t| |
||}d}xh||jgkrt|
|||}t||||}t|dkr|||| f t|
|||||d   }
}}qvW |
|jgkrd}n|
} |sLt| | }x(td|d D ]}| ||  | |< qW | d|d  ||  } }q@P q@W |r`t	d||fS )a  
    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]_

    r)   Fr   TNz'all=True' is not supported yet)
r   rq   r,   rz   rr   rg   r   r!   r   
ValueError)r+   r   r   allr<   r   factorsri   r_   r^   rM   r=   rT   Gr   r   r   r   r   r     s8    +"r   c          	   C   s   t | t| }}|jg|jg|d   }t|gg g|d   }xtd|d | d D ]~}|d  | d  | g|d  }}	x:td|D ],}
|||
d  |	| |
 d    |  qW || st|||| < |}qZW |S )ad  
    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]]

    r)   r    )r,   r   r   r   r5   r   r!   )r+   r   r   r<   ri   rh   rv   rT   ZqqrC   rU   r   r   r   
gf_Qmatrixu  s    ",r   c             C   s  dd | D t |  } }x0td|D ]"}| | | |j | | | |< q$W x&td|D ]}x"t||D ]}| | | rjP qjW qX|| | | |}x.td|D ] }| | | | | | | |< qW x>td|D ]0}| | | }| | | | | |< || | |< qW xhtd|D ]Z}||kr| | | }	x<td|D ].}| | | | | | |	  | | | |< q8W qW qXW xntd|D ]`}xXtd|D ]J}||kr|j| | |  | | | |< n| | |  | | | |< qW qW g }
x"| D ]}	t|	r|
|	 qW |
S )a_  
    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]]

    c             S   s   g | ]}t |qS r   )r5   )r2   rh   r   r   r   r3     s    zgf_Qbasis.<locals>.<listcomp>r   )r*   r   r   rJ   anyr!   )rv   r   r   r<   r/   rT   rb   rU   rx   rh   Zbasisr   r   r   	gf_Qbasis  s<    " 
8
 &

r   c             C   s  t | ||}t|||}x(t|D ]\}}ttt|||< q"W | g}xtdt|D ]}xt|D ]} |j}	x|	|k rt	|| |	||}
t
| |
||}||jgkr|| kr||  t| |||} || |g t|t|krt|ddS |	|j7 }	qrW qfW qXW t|ddS )a  
    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]]

    r)   F)multiple)r   r   	enumerater1   r5   reversedr   r*   r   rG   rr   r   removerg   extendr
   )r+   r   r   rv   r   rT   r   r   r/   r   rM   r=   r   r   r   gf_berlekamp  s&    

r   c             C   s   d|j |jgg   }}}t| ||}xd| t| krt|| |||}t| t||j |jg||||}||j gkr|||f t| |||} t	|| ||}t| ||}|d7 }q&W | |j gkr|| t| fg S |S dS )as  
    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]_

    r)   r&   N)
r   r   rn   r,   ro   rr   rQ   r!   rg   rf   )r+   r   r   rT   rM   r   rH   r=   r   r   r   gf_ddf_zassenhaus  s    # r   c             C   s4  | gt | }}t| |kr |S t| | }|dkr@t| ||}xt||k r&td| d ||}|dkr|}	x<tdd|| d  D ]"}
t|d| ||}t|	|||}	qW t| |	||}n,t	||| |||}	t| t
|	|j||||}||jgkrB|| krBt||||tt| |||||| }qBW t|ddS )a  
    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]_

    r&   r)   r   F)r   )r   r,   rn   r*   r   r   rm   rP   rr   rp   rG   r   gf_edf_zassenhausrg   r
   )r+   r<   r   r   r   rh   NrH   ri   r=   rT   rM   r   r   r   r   @  s(    r   c             C   s  t | }ttt|d }t| ||}t|j|jg| |||}|j|jg|g|jg|d   }x2td|d D ] }t||d  | |||||< qpW || |d|  }}|g|jg|d   }	x.td|D ] }t	|	|d  || |||	|< qW g }
xt
|	D ]\}}|jg|d  }}x8|D ]0}t||||}t||||}t|| ||}qW t| |||}t| |||} xnt|D ]b}t||||}t||||}||jgkr|
|||d  | f t|||||d  }}qxW  qW | |jgkr|
| t | f |
S )a  
    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]_

    r&   r)   N)r,   r   _ceil_sqrtrn   ro   r   r   r   r   r   rQ   rV   rf   rr   rg   r   r!   )r+   r   r   r<   r/   rH   r=   r   rT   r   r   r   rU   r   rM   r^   r   r   r   gf_ddf_shoupx  s:        
$r   c             C   sf  t | t| }}|sg S ||kr(| gS | g|j|jg }}t|d ||}|dkrt||| ||}	t||	||d | ||d }
t| |
||}t| |||}t	||||t	|||| }nt
| ||}t||| |||}
t|
|d d | ||}	t| |	||}t| t|	|j||||}t| t||||||}t	||||t	|||| t	|||| }t|ddS )a  
    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]_

    r)   r&   F)r   )r,   r   r   r   r   rm   r   rr   rg   gf_edf_shouprn   r   rG   rV   r
   )r+   r<   r   r   r   rh   r   r   ri   r=   r   Zh1Zh2rH   Zh3r   r   r   r     s,    *r   c             C   s<   g }x*t | ||D ]\}}|t||||7 }qW t|ddS )a  
    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]]

    F)r   )r   r   r
   )r+   r   r   r   factorr<   r   r   r   gf_zassenhaus  s    r   c             C   s<   g }x*t | ||D ]\}}|t||||7 }qW t|ddS )a  
    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]]

    F)r   )r   r   r
   )r+   r   r   r   r   r<   r   r   r   gf_shoup  s    r   )Z	berlekampZ
zassenhausZshoupc             C   s^   t | ||\}} t| dk r$|g fS |p.td}|dk	rJt| | ||}nt| ||}||fS )a  
    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]])

    r)   ZGF_FACTOR_METHODN)rq   r,   r   _factor_methodsr   )r+   r   r   r   r_   r   r   r   r   gf_factor_sqf5  s    r   c             C   sz   t | ||\}} t| dk r$|g fS g }xDt| ||d D ]0\}}x&t|||d D ]}|||f qTW q:W |t|fS )a  
    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]_

    r)   )rq   r,   r   r   r!   r
   )r+   r   r   r_   r   rM   r<   r=   r   r   r   	gf_factorR  s    2r   c             C   s&   d}x| D ]}||9 }||7 }q
W |S )z
    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

    r   r   )r+   r'   r@   rC   r   r   r   gf_value  s
    
r   c                sp   ddl m} |  dkr4  dkr0ttS g S || \}  dkrTg S  fddtD S )a  
    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

    r   )r   c                s(   g | ] }   |    qS r   r   )r2   rx   )rH   rM   r   ri   r   r   r3     s    z%linear_congruence.<locals>.<listcomp>)Zsympy.polys.polytoolsr   r5   r   )r'   rH   r   r   r   r   )rH   rM   r   ri   r   linear_congruence  s    r   c             C   sB   ddl m} t|||}t|| }t||  ||  }t|||S )a0  
    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]

    r   )ZZ)sympy.polys.domainsr   rz   r   r   )r   r   r   r+   r   Zf_fZalphaZbetar   r   r   _raise_mod_power  s
    !
r   r)   c                s   ddl m   fddtD }|dkr2|S g }tt|dgt| }x^|r| \}||krt| qP|d | |fddt	|D  qPW t
|S )aU  
    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).
    r   )r   c                s"   g | ]}t | d kr|qS )r   )r{   )r2   rT   )r   r+   r   r   r   r3   	  s    z csolve_prime.<locals>.<listcomp>r)   c                s   g | ]}|   fqS r   r   )r2   r   )psru   r   r   r   r3   	  s    )r   r   r   r5   r   r*   popr!   r   r   sorted)r+   r   r   ZX1Xr#   r   r   )r   r+   r   r   ru   r   r   csolve_prime  s    *r   c                s   ddl m  t|}fdd| D }ttt|}g g}x|D ]fdd|D }qDW dd | D t fdd|D S )a7  
    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.

    r   )r   c                s   g | ]\}}t  ||qS r   )r   )r2   r   r   )r+   r   r   r3   +	  s    zgf_csolve.<locals>.<listcomp>c                s    g | ]} D ]}||g qqS r   r   )r2   r   y)poolr   r   r3   /	  s    c             S   s   g | ]\}}t ||qS r   )pow)r2   r   r   r   r   r   r3   0	  s    c                s   g | ]}t | qS r   )r   )r2   Zper)r   dist_factorsr   r   r3   1	  s    )r   r   r   itemsr5   r6   r\   r   )r+   r<   Pr   ZpoolsZpermsr   )r   r   r+   r   r   	gf_csolve	  s    
r   )N)T)T)F)N)r)   )c__doc__Z
__future__r   r   Zrandomr   Zmathr   r   r   r   Zsympy.core.compatibilityr   r   Zsympy.core.mulr	   Zsympy.polys.polyutilsr
   Zsympy.polys.polyconfigr   Zsympy.polys.polyerrorsr   Zsympy.ntheoryr   r   r$   r%   r(   r,   r-   r.   r1   r4   r7   r>   rA   rB   rD   rE   rF   rG   rI   rK   rP   rQ   rV   rX   rY   rZ   r`   re   rf   rg   rj   rk   rl   r]   rn   ro   rp   rm   rr   rs   rt   ry   rq   rz   r{   r}   r~   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   <module>   s   
+

##+6'% %1B7-*
Y(>,98L?
@!(
"