12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363 |
- """Dense univariate polynomials with coefficients in Galois fields. """
- from math import ceil as _ceil, sqrt as _sqrt, prod
- from sympy.core.random import uniform
- from sympy.external.gmpy import SYMPY_INTS
- 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]
- from sympy.ntheory import factorint
- 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``) are not 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 does not 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]]
- Notes
- =====
- The case p == 2 is handled by Cohen's Algorithm 3.4.8. The case p odd is
- as in Geddes Algorithm 8.9 (or Cohen's Algorithm 3.4.6).
- References
- ==========
- .. [1] [Gathen99]_
- .. [2] [Geddes92]_ Algorithm 8.9
- .. [3] [Cohen93]_ Algorithm 3.4.8
- """
- 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)
- t = [K.one, K.zero]
- while len(factors) < N:
- if p == 2:
- h = r = t
- for i in range(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)
- t += [K.zero, K.zero]
- else:
- r = gf_random(2 * n - 1, p, K)
- 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 did not
- 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
- from sympy.ntheory import factorint
- 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])
|