123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355 |
- """Dense univariate polynomials with coefficients in Galois fields. """
- from sympy.core.random import uniform
- from math import ceil as _ceil, sqrt as _sqrt
- from sympy.core.mul import prod
- from sympy.external.gmpy import SYMPY_INTS
- from sympy.ntheory import factorint
- from sympy.polys.polyconfig import query
- from sympy.polys.polyerrors import ExactQuotientFailed
- from sympy.polys.polyutils import _sort_factors
- 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``.
- Examples
- ========
- 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
- >>> 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
- """
- if a <= p // 2:
- return a
- else:
- return a - p
- 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
- """
- return len(f) - 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]
- """
- if not f or f[0]:
- return f
- k = 0
- for coeff in f:
- if coeff:
- break
- else:
- k += 1
- return f[k:]
- 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]
- """
- return gf_strip([ a % p for a in f ])
- 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.galoistools import gf_from_int_poly
- >>> gf_from_int_poly([7, -2, 3], 5)
- [2, 3, 3]
- """
- return gf_trunc(f, p)
- 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]
- """
- if symmetric:
- return [ gf_int(c, p) for c in f ]
- 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]
- """
- return [ -coeff % p for coeff in f ]
- 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]
- """
- 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_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]
- """
- if not a:
- return []
- else:
- return [ (a*b) % p for b in f ]
- 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]
- """
- return gf_mul_ground(f, K.invert(a, p), p, K)
- 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]
- """
- if not f:
- return g
- if not g:
- return f
- df = gf_degree(f)
- dg = gf_degree(g)
- if df == dg:
- return gf_strip([ (a + b) % p for a, b in zip(f, g) ])
- else:
- k = abs(df - dg)
- if df > dg:
- h, f = f[:k], f[k:]
- else:
- h, g = g[:k], g[k:]
- return h + [ (a + b) % p for a, b in zip(f, g) ]
- 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]
- """
- if not g:
- return f
- if not f:
- return gf_neg(g, p, K)
- df = gf_degree(f)
- dg = gf_degree(g)
- if df == dg:
- return gf_strip([ (a - b) % p for a, b in zip(f, g) ])
- else:
- k = abs(df - dg)
- if df > dg:
- h, f = f[:k], f[k:]
- else:
- h, g = gf_neg(g[:k], p, K), g[k:]
- return h + [ (a - b) % p for a, b in zip(f, g) ]
- 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]
- """
- df = gf_degree(f)
- dg = gf_degree(g)
- dh = df + dg
- h = [0]*(dh + 1)
- for i in range(0, dh + 1):
- coeff = K.zero
- for j in range(max(0, i - dg), min(i, df) + 1):
- coeff += f[j]*g[i - j]
- h[i] = coeff % p
- return gf_strip(h)
- 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]
- """
- df = gf_degree(f)
- dh = 2*df
- h = [0]*(dh + 1)
- for i in range(0, dh + 1):
- coeff = K.zero
- jmin = max(0, i - df)
- jmax = min(i, df)
- n = jmax - jmin + 1
- jmax = jmin + n // 2 - 1
- for j in range(jmin, jmax + 1):
- coeff += f[j]*f[i - j]
- coeff += coeff
- if n & 1:
- elem = f[jmax + 1]
- coeff += elem**2
- h[i] = coeff % p
- return gf_strip(h)
- 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]
- """
- return gf_add(f, gf_mul(g, h, p, K), p, K)
- 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]
- """
- return gf_sub(f, gf_mul(g, h, p, K), p, K)
- 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 isinstance(F, 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]_
- """
- df = gf_degree(f)
- dg = gf_degree(g)
- if not g:
- raise ZeroDivisionError("polynomial division")
- elif df < dg:
- return [], f
- inv = K.invert(g[0], p)
- h, dq, dr = list(f), df - dg, dg - 1
- for i in range(0, df + 1):
- coeff = h[i]
- for j in range(max(0, dg - i), min(df - i, dr) + 1):
- coeff -= h[i + j - dg] * g[dg - j]
- if i <= dq:
- coeff *= inv
- h[i] = coeff % p
- return h[:dq + 1], gf_strip(h[dq + 1:])
- 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]
- """
- return gf_div(f, g, p, K)[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]
- """
- df = gf_degree(f)
- dg = gf_degree(g)
- if not g:
- raise ZeroDivisionError("polynomial division")
- elif df < dg:
- return []
- inv = K.invert(g[0], p)
- h, dq, dr = f[:], df - dg, dg - 1
- for i in range(0, dq + 1):
- coeff = h[i]
- for j in range(max(0, dg - i), min(df - i, dr) + 1):
- coeff -= h[i + j - dg] * g[dg - j]
- h[i] = (coeff * inv) % p
- return h[:dq + 1]
- 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]
- """
- if not f:
- return f
- else:
- return f + [K.zero]*n
- 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]]
- """
- n = gf_degree(g)
- if n == 0:
- return []
- b = [0]*n
- b[0] = [1]
- if p < n:
- for i in range(1, n):
- mon = gf_lshift(b[i - 1], p, K)
- b[i] = gf_rem(mon, g, p, K)
- elif n > 1:
- b[1] = gf_pow_mod([K.one, K.zero], p, g, p, K)
- for i in range(2, n):
- b[i] = gf_mul(b[i - 1], b[1], p, K)
- b[i] = gf_rem(b[i], g, p, K)
- return b
- 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]
- """
- m = gf_degree(g)
- if gf_degree(f) >= m:
- f = gf_rem(f, g, p, K)
- if not f:
- return []
- n = gf_degree(f)
- sf = [f[-1]]
- for i in range(1, n + 1):
- v = gf_mul_ground(b[i], f[n - i], p, K)
- sf = gf_add(sf, v, p, K)
- return sf
- 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)``
- """
- 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_mul(r, h, p, K)
- r = gf_rem(r, g, p, K)
- res = gf_pow_mod(r, (p - 1)//2, g, p, K)
- return res
- 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]_
- """
- if not n:
- return [K.one]
- elif n == 1:
- return gf_rem(f, g, p, K)
- elif n == 2:
- return gf_rem(gf_sqr(f, p, K), g, p, K)
- h = [K.one]
- while True:
- if n & 1:
- h = gf_mul(h, f, p, K)
- h = gf_rem(h, g, p, K)
- n -= 1
- n >>= 1
- if not n:
- break
- f = gf_sqr(f, p, K)
- f = gf_rem(f, g, p, K)
- return h
- 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]
- """
- while g:
- f, g = g, gf_rem(f, g, p, K)
- return gf_monic(f, p, K)[1]
- 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]_
- """
- if not (f or g):
- return [K.one], [], []
- p0, r0 = gf_monic(f, p, K)
- p1, r1 = gf_monic(g, p, K)
- if not f:
- return [], [K.invert(p1, p)], r1
- if not g:
- return [K.invert(p0, p)], [], r0
- s0, s1 = [K.invert(p0, p)], []
- t0, t1 = [], [K.invert(p1, p)]
- while True:
- Q, R = gf_div(r0, r1, p, K)
- if not R:
- break
- (lc, r1), r0 = gf_monic(R, p, K), r1
- inv = K.invert(lc, p)
- s = gf_sub_mul(s0, s1, Q, p, K)
- t = gf_sub_mul(t0, t1, Q, p, K)
- s1, s0 = gf_mul_ground(s, inv, p, K), s1
- t1, t0 = gf_mul_ground(t, inv, p, K), t1
- return s1, t1, r1
- 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])
- """
- if not f:
- return K.zero, []
- else:
- lc = f[0]
- if K.is_one(lc):
- return lc, list(f)
- else:
- return lc, gf_quo_ground(f, lc, p, K)
- 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]
- """
- df = gf_degree(f)
- h, n = [K.zero]*df, df
- for coeff in f[:-1]:
- coeff *= K(n)
- coeff %= p
- if coeff:
- h[df - n] = coeff
- n -= 1
- return gf_strip(h)
- 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]
- """
- return [K.one] + [ K(int(uniform(0, p))) for i in range(0, n) ]
- 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
- """
- _, f = gf_monic(f, p, K)
- if not f:
- return True
- else:
- return gf_gcd(f, gf_diff(f, p, K), p, K) == [K.one]
- 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]_
- """
- i, g, factors = 1, [K.one, K.zero], []
- b = gf_frobenius_monomial_base(f, p, K)
- while 2*i <= gf_degree(f):
- g = gf_frobenius_map(g, f, b, p, K)
- h = gf_gcd(f, gf_sub(g, [K.one, K.zero], p, K), p, K)
- if h != [K.one]:
- factors.append((h, i))
- f = gf_quo(f, h, p, K)
- g = gf_rem(g, f, p, K)
- b = gf_frobenius_monomial_base(f, p, K)
- i += 1
- if f != [K.one]:
- return factors + [(f, gf_degree(f))]
- else:
- return factors
- 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]_
- """
- factors = [f]
- if gf_degree(f) <= n:
- return factors
- N = gf_degree(f) // n
- if p != 2:
- b = gf_frobenius_monomial_base(f, p, K)
- while len(factors) < N:
- r = gf_random(2*n - 1, p, K)
- if p == 2:
- 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:
- h = _gf_pow_pnm1d2(r, n, f, b, p, K)
- g = gf_gcd(f, gf_sub_ground(h, K.one, p, K), p, K)
- if g != [K.one] and g != f:
- factors = gf_edf_zassenhaus(g, n, p, K) \
- + gf_edf_zassenhaus(gf_quo(f, g, p, K), n, p, K)
- return _sort_factors(factors, multiple=False)
- 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]]
- """
- factors = []
- for factor, n in gf_ddf_zassenhaus(f, p, K):
- factors += gf_edf_zassenhaus(factor, n, p, K)
- return _sort_factors(factors, multiple=False)
- 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]])
- """
- lc, f = gf_monic(f, p, K)
- if gf_degree(f) < 1:
- return lc, []
- method = method or query('GF_FACTOR_METHOD')
- if method is not None:
- factors = _factor_methods[method](f, p, K)
- else:
- factors = gf_zassenhaus(f, p, K)
- return lc, factors
- 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.
- References
- ==========
- .. [1] https://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])
|