galoistools.py 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363
  1. """Dense univariate polynomials with coefficients in Galois fields. """
  2. from math import ceil as _ceil, sqrt as _sqrt, prod
  3. from sympy.core.random import uniform
  4. from sympy.external.gmpy import SYMPY_INTS
  5. from sympy.polys.polyconfig import query
  6. from sympy.polys.polyerrors import ExactQuotientFailed
  7. from sympy.polys.polyutils import _sort_factors
  8. def gf_crt(U, M, K=None):
  9. """
  10. Chinese Remainder Theorem.
  11. Given a set of integer residues ``u_0,...,u_n`` and a set of
  12. co-prime integer moduli ``m_0,...,m_n``, returns an integer
  13. ``u``, such that ``u = u_i mod m_i`` for ``i = ``0,...,n``.
  14. Examples
  15. ========
  16. Consider a set of residues ``U = [49, 76, 65]``
  17. and a set of moduli ``M = [99, 97, 95]``. Then we have::
  18. >>> from sympy.polys.domains import ZZ
  19. >>> from sympy.polys.galoistools import gf_crt
  20. >>> gf_crt([49, 76, 65], [99, 97, 95], ZZ)
  21. 639985
  22. This is the correct result because::
  23. >>> [639985 % m for m in [99, 97, 95]]
  24. [49, 76, 65]
  25. Note: this is a low-level routine with no error checking.
  26. See Also
  27. ========
  28. sympy.ntheory.modular.crt : a higher level crt routine
  29. sympy.ntheory.modular.solve_congruence
  30. """
  31. p = prod(M, start=K.one)
  32. v = K.zero
  33. for u, m in zip(U, M):
  34. e = p // m
  35. s, _, _ = K.gcdex(e, m)
  36. v += e*(u*s % m)
  37. return v % p
  38. def gf_crt1(M, K):
  39. """
  40. First part of the Chinese Remainder Theorem.
  41. Examples
  42. ========
  43. >>> from sympy.polys.domains import ZZ
  44. >>> from sympy.polys.galoistools import gf_crt1
  45. >>> gf_crt1([99, 97, 95], ZZ)
  46. (912285, [9215, 9405, 9603], [62, 24, 12])
  47. """
  48. E, S = [], []
  49. p = prod(M, start=K.one)
  50. for m in M:
  51. E.append(p // m)
  52. S.append(K.gcdex(E[-1], m)[0] % m)
  53. return p, E, S
  54. def gf_crt2(U, M, p, E, S, K):
  55. """
  56. Second part of the Chinese Remainder Theorem.
  57. Examples
  58. ========
  59. >>> from sympy.polys.domains import ZZ
  60. >>> from sympy.polys.galoistools import gf_crt2
  61. >>> U = [49, 76, 65]
  62. >>> M = [99, 97, 95]
  63. >>> p = 912285
  64. >>> E = [9215, 9405, 9603]
  65. >>> S = [62, 24, 12]
  66. >>> gf_crt2(U, M, p, E, S, ZZ)
  67. 639985
  68. """
  69. v = K.zero
  70. for u, m, e, s in zip(U, M, E, S):
  71. v += e*(u*s % m)
  72. return v % p
  73. def gf_int(a, p):
  74. """
  75. Coerce ``a mod p`` to an integer in the range ``[-p/2, p/2]``.
  76. Examples
  77. ========
  78. >>> from sympy.polys.galoistools import gf_int
  79. >>> gf_int(2, 7)
  80. 2
  81. >>> gf_int(5, 7)
  82. -2
  83. """
  84. if a <= p // 2:
  85. return a
  86. else:
  87. return a - p
  88. def gf_degree(f):
  89. """
  90. Return the leading degree of ``f``.
  91. Examples
  92. ========
  93. >>> from sympy.polys.galoistools import gf_degree
  94. >>> gf_degree([1, 1, 2, 0])
  95. 3
  96. >>> gf_degree([])
  97. -1
  98. """
  99. return len(f) - 1
  100. def gf_LC(f, K):
  101. """
  102. Return the leading coefficient of ``f``.
  103. Examples
  104. ========
  105. >>> from sympy.polys.domains import ZZ
  106. >>> from sympy.polys.galoistools import gf_LC
  107. >>> gf_LC([3, 0, 1], ZZ)
  108. 3
  109. """
  110. if not f:
  111. return K.zero
  112. else:
  113. return f[0]
  114. def gf_TC(f, K):
  115. """
  116. Return the trailing coefficient of ``f``.
  117. Examples
  118. ========
  119. >>> from sympy.polys.domains import ZZ
  120. >>> from sympy.polys.galoistools import gf_TC
  121. >>> gf_TC([3, 0, 1], ZZ)
  122. 1
  123. """
  124. if not f:
  125. return K.zero
  126. else:
  127. return f[-1]
  128. def gf_strip(f):
  129. """
  130. Remove leading zeros from ``f``.
  131. Examples
  132. ========
  133. >>> from sympy.polys.galoistools import gf_strip
  134. >>> gf_strip([0, 0, 0, 3, 0, 1])
  135. [3, 0, 1]
  136. """
  137. if not f or f[0]:
  138. return f
  139. k = 0
  140. for coeff in f:
  141. if coeff:
  142. break
  143. else:
  144. k += 1
  145. return f[k:]
  146. def gf_trunc(f, p):
  147. """
  148. Reduce all coefficients modulo ``p``.
  149. Examples
  150. ========
  151. >>> from sympy.polys.galoistools import gf_trunc
  152. >>> gf_trunc([7, -2, 3], 5)
  153. [2, 3, 3]
  154. """
  155. return gf_strip([ a % p for a in f ])
  156. def gf_normal(f, p, K):
  157. """
  158. Normalize all coefficients in ``K``.
  159. Examples
  160. ========
  161. >>> from sympy.polys.domains import ZZ
  162. >>> from sympy.polys.galoistools import gf_normal
  163. >>> gf_normal([5, 10, 21, -3], 5, ZZ)
  164. [1, 2]
  165. """
  166. return gf_trunc(list(map(K, f)), p)
  167. def gf_from_dict(f, p, K):
  168. """
  169. Create a ``GF(p)[x]`` polynomial from a dict.
  170. Examples
  171. ========
  172. >>> from sympy.polys.domains import ZZ
  173. >>> from sympy.polys.galoistools import gf_from_dict
  174. >>> gf_from_dict({10: ZZ(4), 4: ZZ(33), 0: ZZ(-1)}, 5, ZZ)
  175. [4, 0, 0, 0, 0, 0, 3, 0, 0, 0, 4]
  176. """
  177. n, h = max(f.keys()), []
  178. if isinstance(n, SYMPY_INTS):
  179. for k in range(n, -1, -1):
  180. h.append(f.get(k, K.zero) % p)
  181. else:
  182. (n,) = n
  183. for k in range(n, -1, -1):
  184. h.append(f.get((k,), K.zero) % p)
  185. return gf_trunc(h, p)
  186. def gf_to_dict(f, p, symmetric=True):
  187. """
  188. Convert a ``GF(p)[x]`` polynomial to a dict.
  189. Examples
  190. ========
  191. >>> from sympy.polys.galoistools import gf_to_dict
  192. >>> gf_to_dict([4, 0, 0, 0, 0, 0, 3, 0, 0, 0, 4], 5)
  193. {0: -1, 4: -2, 10: -1}
  194. >>> gf_to_dict([4, 0, 0, 0, 0, 0, 3, 0, 0, 0, 4], 5, symmetric=False)
  195. {0: 4, 4: 3, 10: 4}
  196. """
  197. n, result = gf_degree(f), {}
  198. for k in range(0, n + 1):
  199. if symmetric:
  200. a = gf_int(f[n - k], p)
  201. else:
  202. a = f[n - k]
  203. if a:
  204. result[k] = a
  205. return result
  206. def gf_from_int_poly(f, p):
  207. """
  208. Create a ``GF(p)[x]`` polynomial from ``Z[x]``.
  209. Examples
  210. ========
  211. >>> from sympy.polys.galoistools import gf_from_int_poly
  212. >>> gf_from_int_poly([7, -2, 3], 5)
  213. [2, 3, 3]
  214. """
  215. return gf_trunc(f, p)
  216. def gf_to_int_poly(f, p, symmetric=True):
  217. """
  218. Convert a ``GF(p)[x]`` polynomial to ``Z[x]``.
  219. Examples
  220. ========
  221. >>> from sympy.polys.galoistools import gf_to_int_poly
  222. >>> gf_to_int_poly([2, 3, 3], 5)
  223. [2, -2, -2]
  224. >>> gf_to_int_poly([2, 3, 3], 5, symmetric=False)
  225. [2, 3, 3]
  226. """
  227. if symmetric:
  228. return [ gf_int(c, p) for c in f ]
  229. else:
  230. return f
  231. def gf_neg(f, p, K):
  232. """
  233. Negate a polynomial in ``GF(p)[x]``.
  234. Examples
  235. ========
  236. >>> from sympy.polys.domains import ZZ
  237. >>> from sympy.polys.galoistools import gf_neg
  238. >>> gf_neg([3, 2, 1, 0], 5, ZZ)
  239. [2, 3, 4, 0]
  240. """
  241. return [ -coeff % p for coeff in f ]
  242. def gf_add_ground(f, a, p, K):
  243. """
  244. Compute ``f + a`` where ``f`` in ``GF(p)[x]`` and ``a`` in ``GF(p)``.
  245. Examples
  246. ========
  247. >>> from sympy.polys.domains import ZZ
  248. >>> from sympy.polys.galoistools import gf_add_ground
  249. >>> gf_add_ground([3, 2, 4], 2, 5, ZZ)
  250. [3, 2, 1]
  251. """
  252. if not f:
  253. a = a % p
  254. else:
  255. a = (f[-1] + a) % p
  256. if len(f) > 1:
  257. return f[:-1] + [a]
  258. if not a:
  259. return []
  260. else:
  261. return [a]
  262. def gf_sub_ground(f, a, p, K):
  263. """
  264. Compute ``f - a`` where ``f`` in ``GF(p)[x]`` and ``a`` in ``GF(p)``.
  265. Examples
  266. ========
  267. >>> from sympy.polys.domains import ZZ
  268. >>> from sympy.polys.galoistools import gf_sub_ground
  269. >>> gf_sub_ground([3, 2, 4], 2, 5, ZZ)
  270. [3, 2, 2]
  271. """
  272. if not f:
  273. a = -a % p
  274. else:
  275. a = (f[-1] - a) % p
  276. if len(f) > 1:
  277. return f[:-1] + [a]
  278. if not a:
  279. return []
  280. else:
  281. return [a]
  282. def gf_mul_ground(f, a, p, K):
  283. """
  284. Compute ``f * a`` where ``f`` in ``GF(p)[x]`` and ``a`` in ``GF(p)``.
  285. Examples
  286. ========
  287. >>> from sympy.polys.domains import ZZ
  288. >>> from sympy.polys.galoistools import gf_mul_ground
  289. >>> gf_mul_ground([3, 2, 4], 2, 5, ZZ)
  290. [1, 4, 3]
  291. """
  292. if not a:
  293. return []
  294. else:
  295. return [ (a*b) % p for b in f ]
  296. def gf_quo_ground(f, a, p, K):
  297. """
  298. Compute ``f/a`` where ``f`` in ``GF(p)[x]`` and ``a`` in ``GF(p)``.
  299. Examples
  300. ========
  301. >>> from sympy.polys.domains import ZZ
  302. >>> from sympy.polys.galoistools import gf_quo_ground
  303. >>> gf_quo_ground(ZZ.map([3, 2, 4]), ZZ(2), 5, ZZ)
  304. [4, 1, 2]
  305. """
  306. return gf_mul_ground(f, K.invert(a, p), p, K)
  307. def gf_add(f, g, p, K):
  308. """
  309. Add polynomials in ``GF(p)[x]``.
  310. Examples
  311. ========
  312. >>> from sympy.polys.domains import ZZ
  313. >>> from sympy.polys.galoistools import gf_add
  314. >>> gf_add([3, 2, 4], [2, 2, 2], 5, ZZ)
  315. [4, 1]
  316. """
  317. if not f:
  318. return g
  319. if not g:
  320. return f
  321. df = gf_degree(f)
  322. dg = gf_degree(g)
  323. if df == dg:
  324. return gf_strip([ (a + b) % p for a, b in zip(f, g) ])
  325. else:
  326. k = abs(df - dg)
  327. if df > dg:
  328. h, f = f[:k], f[k:]
  329. else:
  330. h, g = g[:k], g[k:]
  331. return h + [ (a + b) % p for a, b in zip(f, g) ]
  332. def gf_sub(f, g, p, K):
  333. """
  334. Subtract polynomials in ``GF(p)[x]``.
  335. Examples
  336. ========
  337. >>> from sympy.polys.domains import ZZ
  338. >>> from sympy.polys.galoistools import gf_sub
  339. >>> gf_sub([3, 2, 4], [2, 2, 2], 5, ZZ)
  340. [1, 0, 2]
  341. """
  342. if not g:
  343. return f
  344. if not f:
  345. return gf_neg(g, p, K)
  346. df = gf_degree(f)
  347. dg = gf_degree(g)
  348. if df == dg:
  349. return gf_strip([ (a - b) % p for a, b in zip(f, g) ])
  350. else:
  351. k = abs(df - dg)
  352. if df > dg:
  353. h, f = f[:k], f[k:]
  354. else:
  355. h, g = gf_neg(g[:k], p, K), g[k:]
  356. return h + [ (a - b) % p for a, b in zip(f, g) ]
  357. def gf_mul(f, g, p, K):
  358. """
  359. Multiply polynomials in ``GF(p)[x]``.
  360. Examples
  361. ========
  362. >>> from sympy.polys.domains import ZZ
  363. >>> from sympy.polys.galoistools import gf_mul
  364. >>> gf_mul([3, 2, 4], [2, 2, 2], 5, ZZ)
  365. [1, 0, 3, 2, 3]
  366. """
  367. df = gf_degree(f)
  368. dg = gf_degree(g)
  369. dh = df + dg
  370. h = [0]*(dh + 1)
  371. for i in range(0, dh + 1):
  372. coeff = K.zero
  373. for j in range(max(0, i - dg), min(i, df) + 1):
  374. coeff += f[j]*g[i - j]
  375. h[i] = coeff % p
  376. return gf_strip(h)
  377. def gf_sqr(f, p, K):
  378. """
  379. Square polynomials in ``GF(p)[x]``.
  380. Examples
  381. ========
  382. >>> from sympy.polys.domains import ZZ
  383. >>> from sympy.polys.galoistools import gf_sqr
  384. >>> gf_sqr([3, 2, 4], 5, ZZ)
  385. [4, 2, 3, 1, 1]
  386. """
  387. df = gf_degree(f)
  388. dh = 2*df
  389. h = [0]*(dh + 1)
  390. for i in range(0, dh + 1):
  391. coeff = K.zero
  392. jmin = max(0, i - df)
  393. jmax = min(i, df)
  394. n = jmax - jmin + 1
  395. jmax = jmin + n // 2 - 1
  396. for j in range(jmin, jmax + 1):
  397. coeff += f[j]*f[i - j]
  398. coeff += coeff
  399. if n & 1:
  400. elem = f[jmax + 1]
  401. coeff += elem**2
  402. h[i] = coeff % p
  403. return gf_strip(h)
  404. def gf_add_mul(f, g, h, p, K):
  405. """
  406. Returns ``f + g*h`` where ``f``, ``g``, ``h`` in ``GF(p)[x]``.
  407. Examples
  408. ========
  409. >>> from sympy.polys.domains import ZZ
  410. >>> from sympy.polys.galoistools import gf_add_mul
  411. >>> gf_add_mul([3, 2, 4], [2, 2, 2], [1, 4], 5, ZZ)
  412. [2, 3, 2, 2]
  413. """
  414. return gf_add(f, gf_mul(g, h, p, K), p, K)
  415. def gf_sub_mul(f, g, h, p, K):
  416. """
  417. Compute ``f - g*h`` where ``f``, ``g``, ``h`` in ``GF(p)[x]``.
  418. Examples
  419. ========
  420. >>> from sympy.polys.domains import ZZ
  421. >>> from sympy.polys.galoistools import gf_sub_mul
  422. >>> gf_sub_mul([3, 2, 4], [2, 2, 2], [1, 4], 5, ZZ)
  423. [3, 3, 2, 1]
  424. """
  425. return gf_sub(f, gf_mul(g, h, p, K), p, K)
  426. def gf_expand(F, p, K):
  427. """
  428. Expand results of :func:`~.factor` in ``GF(p)[x]``.
  429. Examples
  430. ========
  431. >>> from sympy.polys.domains import ZZ
  432. >>> from sympy.polys.galoistools import gf_expand
  433. >>> gf_expand([([3, 2, 4], 1), ([2, 2], 2), ([3, 1], 3)], 5, ZZ)
  434. [4, 3, 0, 3, 0, 1, 4, 1]
  435. """
  436. if isinstance(F, tuple):
  437. lc, F = F
  438. else:
  439. lc = K.one
  440. g = [lc]
  441. for f, k in F:
  442. f = gf_pow(f, k, p, K)
  443. g = gf_mul(g, f, p, K)
  444. return g
  445. def gf_div(f, g, p, K):
  446. """
  447. Division with remainder in ``GF(p)[x]``.
  448. Given univariate polynomials ``f`` and ``g`` with coefficients in a
  449. finite field with ``p`` elements, returns polynomials ``q`` and ``r``
  450. (quotient and remainder) such that ``f = q*g + r``.
  451. Consider polynomials ``x**3 + x + 1`` and ``x**2 + x`` in GF(2)::
  452. >>> from sympy.polys.domains import ZZ
  453. >>> from sympy.polys.galoistools import gf_div, gf_add_mul
  454. >>> gf_div(ZZ.map([1, 0, 1, 1]), ZZ.map([1, 1, 0]), 2, ZZ)
  455. ([1, 1], [1])
  456. As result we obtained quotient ``x + 1`` and remainder ``1``, thus::
  457. >>> gf_add_mul(ZZ.map([1]), ZZ.map([1, 1]), ZZ.map([1, 1, 0]), 2, ZZ)
  458. [1, 0, 1, 1]
  459. References
  460. ==========
  461. .. [1] [Monagan93]_
  462. .. [2] [Gathen99]_
  463. """
  464. df = gf_degree(f)
  465. dg = gf_degree(g)
  466. if not g:
  467. raise ZeroDivisionError("polynomial division")
  468. elif df < dg:
  469. return [], f
  470. inv = K.invert(g[0], p)
  471. h, dq, dr = list(f), df - dg, dg - 1
  472. for i in range(0, df + 1):
  473. coeff = h[i]
  474. for j in range(max(0, dg - i), min(df - i, dr) + 1):
  475. coeff -= h[i + j - dg] * g[dg - j]
  476. if i <= dq:
  477. coeff *= inv
  478. h[i] = coeff % p
  479. return h[:dq + 1], gf_strip(h[dq + 1:])
  480. def gf_rem(f, g, p, K):
  481. """
  482. Compute polynomial remainder in ``GF(p)[x]``.
  483. Examples
  484. ========
  485. >>> from sympy.polys.domains import ZZ
  486. >>> from sympy.polys.galoistools import gf_rem
  487. >>> gf_rem(ZZ.map([1, 0, 1, 1]), ZZ.map([1, 1, 0]), 2, ZZ)
  488. [1]
  489. """
  490. return gf_div(f, g, p, K)[1]
  491. def gf_quo(f, g, p, K):
  492. """
  493. Compute exact quotient in ``GF(p)[x]``.
  494. Examples
  495. ========
  496. >>> from sympy.polys.domains import ZZ
  497. >>> from sympy.polys.galoistools import gf_quo
  498. >>> gf_quo(ZZ.map([1, 0, 1, 1]), ZZ.map([1, 1, 0]), 2, ZZ)
  499. [1, 1]
  500. >>> gf_quo(ZZ.map([1, 0, 3, 2, 3]), ZZ.map([2, 2, 2]), 5, ZZ)
  501. [3, 2, 4]
  502. """
  503. df = gf_degree(f)
  504. dg = gf_degree(g)
  505. if not g:
  506. raise ZeroDivisionError("polynomial division")
  507. elif df < dg:
  508. return []
  509. inv = K.invert(g[0], p)
  510. h, dq, dr = f[:], df - dg, dg - 1
  511. for i in range(0, dq + 1):
  512. coeff = h[i]
  513. for j in range(max(0, dg - i), min(df - i, dr) + 1):
  514. coeff -= h[i + j - dg] * g[dg - j]
  515. h[i] = (coeff * inv) % p
  516. return h[:dq + 1]
  517. def gf_exquo(f, g, p, K):
  518. """
  519. Compute polynomial quotient in ``GF(p)[x]``.
  520. Examples
  521. ========
  522. >>> from sympy.polys.domains import ZZ
  523. >>> from sympy.polys.galoistools import gf_exquo
  524. >>> gf_exquo(ZZ.map([1, 0, 3, 2, 3]), ZZ.map([2, 2, 2]), 5, ZZ)
  525. [3, 2, 4]
  526. >>> gf_exquo(ZZ.map([1, 0, 1, 1]), ZZ.map([1, 1, 0]), 2, ZZ)
  527. Traceback (most recent call last):
  528. ...
  529. ExactQuotientFailed: [1, 1, 0] does not divide [1, 0, 1, 1]
  530. """
  531. q, r = gf_div(f, g, p, K)
  532. if not r:
  533. return q
  534. else:
  535. raise ExactQuotientFailed(f, g)
  536. def gf_lshift(f, n, K):
  537. """
  538. Efficiently multiply ``f`` by ``x**n``.
  539. Examples
  540. ========
  541. >>> from sympy.polys.domains import ZZ
  542. >>> from sympy.polys.galoistools import gf_lshift
  543. >>> gf_lshift([3, 2, 4], 4, ZZ)
  544. [3, 2, 4, 0, 0, 0, 0]
  545. """
  546. if not f:
  547. return f
  548. else:
  549. return f + [K.zero]*n
  550. def gf_rshift(f, n, K):
  551. """
  552. Efficiently divide ``f`` by ``x**n``.
  553. Examples
  554. ========
  555. >>> from sympy.polys.domains import ZZ
  556. >>> from sympy.polys.galoistools import gf_rshift
  557. >>> gf_rshift([1, 2, 3, 4, 0], 3, ZZ)
  558. ([1, 2], [3, 4, 0])
  559. """
  560. if not n:
  561. return f, []
  562. else:
  563. return f[:-n], f[-n:]
  564. def gf_pow(f, n, p, K):
  565. """
  566. Compute ``f**n`` in ``GF(p)[x]`` using repeated squaring.
  567. Examples
  568. ========
  569. >>> from sympy.polys.domains import ZZ
  570. >>> from sympy.polys.galoistools import gf_pow
  571. >>> gf_pow([3, 2, 4], 3, 5, ZZ)
  572. [2, 4, 4, 2, 2, 1, 4]
  573. """
  574. if not n:
  575. return [K.one]
  576. elif n == 1:
  577. return f
  578. elif n == 2:
  579. return gf_sqr(f, p, K)
  580. h = [K.one]
  581. while True:
  582. if n & 1:
  583. h = gf_mul(h, f, p, K)
  584. n -= 1
  585. n >>= 1
  586. if not n:
  587. break
  588. f = gf_sqr(f, p, K)
  589. return h
  590. def gf_frobenius_monomial_base(g, p, K):
  591. """
  592. return the list of ``x**(i*p) mod g in Z_p`` for ``i = 0, .., n - 1``
  593. where ``n = gf_degree(g)``
  594. Examples
  595. ========
  596. >>> from sympy.polys.domains import ZZ
  597. >>> from sympy.polys.galoistools import gf_frobenius_monomial_base
  598. >>> g = ZZ.map([1, 0, 2, 1])
  599. >>> gf_frobenius_monomial_base(g, 5, ZZ)
  600. [[1], [4, 4, 2], [1, 2]]
  601. """
  602. n = gf_degree(g)
  603. if n == 0:
  604. return []
  605. b = [0]*n
  606. b[0] = [1]
  607. if p < n:
  608. for i in range(1, n):
  609. mon = gf_lshift(b[i - 1], p, K)
  610. b[i] = gf_rem(mon, g, p, K)
  611. elif n > 1:
  612. b[1] = gf_pow_mod([K.one, K.zero], p, g, p, K)
  613. for i in range(2, n):
  614. b[i] = gf_mul(b[i - 1], b[1], p, K)
  615. b[i] = gf_rem(b[i], g, p, K)
  616. return b
  617. def gf_frobenius_map(f, g, b, p, K):
  618. """
  619. compute gf_pow_mod(f, p, g, p, K) using the Frobenius map
  620. Parameters
  621. ==========
  622. f, g : polynomials in ``GF(p)[x]``
  623. b : frobenius monomial base
  624. p : prime number
  625. K : domain
  626. Examples
  627. ========
  628. >>> from sympy.polys.domains import ZZ
  629. >>> from sympy.polys.galoistools import gf_frobenius_monomial_base, gf_frobenius_map
  630. >>> f = ZZ.map([2, 1, 0, 1])
  631. >>> g = ZZ.map([1, 0, 2, 1])
  632. >>> p = 5
  633. >>> b = gf_frobenius_monomial_base(g, p, ZZ)
  634. >>> r = gf_frobenius_map(f, g, b, p, ZZ)
  635. >>> gf_frobenius_map(f, g, b, p, ZZ)
  636. [4, 0, 3]
  637. """
  638. m = gf_degree(g)
  639. if gf_degree(f) >= m:
  640. f = gf_rem(f, g, p, K)
  641. if not f:
  642. return []
  643. n = gf_degree(f)
  644. sf = [f[-1]]
  645. for i in range(1, n + 1):
  646. v = gf_mul_ground(b[i], f[n - i], p, K)
  647. sf = gf_add(sf, v, p, K)
  648. return sf
  649. def _gf_pow_pnm1d2(f, n, g, b, p, K):
  650. """
  651. utility function for ``gf_edf_zassenhaus``
  652. Compute ``f**((p**n - 1) // 2)`` in ``GF(p)[x]/(g)``
  653. ``f**((p**n - 1) // 2) = (f*f**p*...*f**(p**n - 1))**((p - 1) // 2)``
  654. """
  655. f = gf_rem(f, g, p, K)
  656. h = f
  657. r = f
  658. for i in range(1, n):
  659. h = gf_frobenius_map(h, g, b, p, K)
  660. r = gf_mul(r, h, p, K)
  661. r = gf_rem(r, g, p, K)
  662. res = gf_pow_mod(r, (p - 1)//2, g, p, K)
  663. return res
  664. def gf_pow_mod(f, n, g, p, K):
  665. """
  666. Compute ``f**n`` in ``GF(p)[x]/(g)`` using repeated squaring.
  667. Given polynomials ``f`` and ``g`` in ``GF(p)[x]`` and a non-negative
  668. integer ``n``, efficiently computes ``f**n (mod g)`` i.e. the remainder
  669. of ``f**n`` from division by ``g``, using the repeated squaring algorithm.
  670. Examples
  671. ========
  672. >>> from sympy.polys.domains import ZZ
  673. >>> from sympy.polys.galoistools import gf_pow_mod
  674. >>> gf_pow_mod(ZZ.map([3, 2, 4]), 3, ZZ.map([1, 1]), 5, ZZ)
  675. []
  676. References
  677. ==========
  678. .. [1] [Gathen99]_
  679. """
  680. if not n:
  681. return [K.one]
  682. elif n == 1:
  683. return gf_rem(f, g, p, K)
  684. elif n == 2:
  685. return gf_rem(gf_sqr(f, p, K), g, p, K)
  686. h = [K.one]
  687. while True:
  688. if n & 1:
  689. h = gf_mul(h, f, p, K)
  690. h = gf_rem(h, g, p, K)
  691. n -= 1
  692. n >>= 1
  693. if not n:
  694. break
  695. f = gf_sqr(f, p, K)
  696. f = gf_rem(f, g, p, K)
  697. return h
  698. def gf_gcd(f, g, p, K):
  699. """
  700. Euclidean Algorithm in ``GF(p)[x]``.
  701. Examples
  702. ========
  703. >>> from sympy.polys.domains import ZZ
  704. >>> from sympy.polys.galoistools import gf_gcd
  705. >>> gf_gcd(ZZ.map([3, 2, 4]), ZZ.map([2, 2, 3]), 5, ZZ)
  706. [1, 3]
  707. """
  708. while g:
  709. f, g = g, gf_rem(f, g, p, K)
  710. return gf_monic(f, p, K)[1]
  711. def gf_lcm(f, g, p, K):
  712. """
  713. Compute polynomial LCM in ``GF(p)[x]``.
  714. Examples
  715. ========
  716. >>> from sympy.polys.domains import ZZ
  717. >>> from sympy.polys.galoistools import gf_lcm
  718. >>> gf_lcm(ZZ.map([3, 2, 4]), ZZ.map([2, 2, 3]), 5, ZZ)
  719. [1, 2, 0, 4]
  720. """
  721. if not f or not g:
  722. return []
  723. h = gf_quo(gf_mul(f, g, p, K),
  724. gf_gcd(f, g, p, K), p, K)
  725. return gf_monic(h, p, K)[1]
  726. def gf_cofactors(f, g, p, K):
  727. """
  728. Compute polynomial GCD and cofactors in ``GF(p)[x]``.
  729. Examples
  730. ========
  731. >>> from sympy.polys.domains import ZZ
  732. >>> from sympy.polys.galoistools import gf_cofactors
  733. >>> gf_cofactors(ZZ.map([3, 2, 4]), ZZ.map([2, 2, 3]), 5, ZZ)
  734. ([1, 3], [3, 3], [2, 1])
  735. """
  736. if not f and not g:
  737. return ([], [], [])
  738. h = gf_gcd(f, g, p, K)
  739. return (h, gf_quo(f, h, p, K),
  740. gf_quo(g, h, p, K))
  741. def gf_gcdex(f, g, p, K):
  742. """
  743. Extended Euclidean Algorithm in ``GF(p)[x]``.
  744. Given polynomials ``f`` and ``g`` in ``GF(p)[x]``, computes polynomials
  745. ``s``, ``t`` and ``h``, such that ``h = gcd(f, g)`` and ``s*f + t*g = h``.
  746. The typical application of EEA is solving polynomial diophantine equations.
  747. Consider polynomials ``f = (x + 7) (x + 1)``, ``g = (x + 7) (x**2 + 1)``
  748. in ``GF(11)[x]``. Application of Extended Euclidean Algorithm gives::
  749. >>> from sympy.polys.domains import ZZ
  750. >>> from sympy.polys.galoistools import gf_gcdex, gf_mul, gf_add
  751. >>> s, t, g = gf_gcdex(ZZ.map([1, 8, 7]), ZZ.map([1, 7, 1, 7]), 11, ZZ)
  752. >>> s, t, g
  753. ([5, 6], [6], [1, 7])
  754. As result we obtained polynomials ``s = 5*x + 6`` and ``t = 6``, and
  755. additionally ``gcd(f, g) = x + 7``. This is correct because::
  756. >>> S = gf_mul(s, ZZ.map([1, 8, 7]), 11, ZZ)
  757. >>> T = gf_mul(t, ZZ.map([1, 7, 1, 7]), 11, ZZ)
  758. >>> gf_add(S, T, 11, ZZ) == [1, 7]
  759. True
  760. References
  761. ==========
  762. .. [1] [Gathen99]_
  763. """
  764. if not (f or g):
  765. return [K.one], [], []
  766. p0, r0 = gf_monic(f, p, K)
  767. p1, r1 = gf_monic(g, p, K)
  768. if not f:
  769. return [], [K.invert(p1, p)], r1
  770. if not g:
  771. return [K.invert(p0, p)], [], r0
  772. s0, s1 = [K.invert(p0, p)], []
  773. t0, t1 = [], [K.invert(p1, p)]
  774. while True:
  775. Q, R = gf_div(r0, r1, p, K)
  776. if not R:
  777. break
  778. (lc, r1), r0 = gf_monic(R, p, K), r1
  779. inv = K.invert(lc, p)
  780. s = gf_sub_mul(s0, s1, Q, p, K)
  781. t = gf_sub_mul(t0, t1, Q, p, K)
  782. s1, s0 = gf_mul_ground(s, inv, p, K), s1
  783. t1, t0 = gf_mul_ground(t, inv, p, K), t1
  784. return s1, t1, r1
  785. def gf_monic(f, p, K):
  786. """
  787. Compute LC and a monic polynomial in ``GF(p)[x]``.
  788. Examples
  789. ========
  790. >>> from sympy.polys.domains import ZZ
  791. >>> from sympy.polys.galoistools import gf_monic
  792. >>> gf_monic(ZZ.map([3, 2, 4]), 5, ZZ)
  793. (3, [1, 4, 3])
  794. """
  795. if not f:
  796. return K.zero, []
  797. else:
  798. lc = f[0]
  799. if K.is_one(lc):
  800. return lc, list(f)
  801. else:
  802. return lc, gf_quo_ground(f, lc, p, K)
  803. def gf_diff(f, p, K):
  804. """
  805. Differentiate polynomial in ``GF(p)[x]``.
  806. Examples
  807. ========
  808. >>> from sympy.polys.domains import ZZ
  809. >>> from sympy.polys.galoistools import gf_diff
  810. >>> gf_diff([3, 2, 4], 5, ZZ)
  811. [1, 2]
  812. """
  813. df = gf_degree(f)
  814. h, n = [K.zero]*df, df
  815. for coeff in f[:-1]:
  816. coeff *= K(n)
  817. coeff %= p
  818. if coeff:
  819. h[df - n] = coeff
  820. n -= 1
  821. return gf_strip(h)
  822. def gf_eval(f, a, p, K):
  823. """
  824. Evaluate ``f(a)`` in ``GF(p)`` using Horner scheme.
  825. Examples
  826. ========
  827. >>> from sympy.polys.domains import ZZ
  828. >>> from sympy.polys.galoistools import gf_eval
  829. >>> gf_eval([3, 2, 4], 2, 5, ZZ)
  830. 0
  831. """
  832. result = K.zero
  833. for c in f:
  834. result *= a
  835. result += c
  836. result %= p
  837. return result
  838. def gf_multi_eval(f, A, p, K):
  839. """
  840. Evaluate ``f(a)`` for ``a`` in ``[a_1, ..., a_n]``.
  841. Examples
  842. ========
  843. >>> from sympy.polys.domains import ZZ
  844. >>> from sympy.polys.galoistools import gf_multi_eval
  845. >>> gf_multi_eval([3, 2, 4], [0, 1, 2, 3, 4], 5, ZZ)
  846. [4, 4, 0, 2, 0]
  847. """
  848. return [ gf_eval(f, a, p, K) for a in A ]
  849. def gf_compose(f, g, p, K):
  850. """
  851. Compute polynomial composition ``f(g)`` in ``GF(p)[x]``.
  852. Examples
  853. ========
  854. >>> from sympy.polys.domains import ZZ
  855. >>> from sympy.polys.galoistools import gf_compose
  856. >>> gf_compose([3, 2, 4], [2, 2, 2], 5, ZZ)
  857. [2, 4, 0, 3, 0]
  858. """
  859. if len(g) <= 1:
  860. return gf_strip([gf_eval(f, gf_LC(g, K), p, K)])
  861. if not f:
  862. return []
  863. h = [f[0]]
  864. for c in f[1:]:
  865. h = gf_mul(h, g, p, K)
  866. h = gf_add_ground(h, c, p, K)
  867. return h
  868. def gf_compose_mod(g, h, f, p, K):
  869. """
  870. Compute polynomial composition ``g(h)`` in ``GF(p)[x]/(f)``.
  871. Examples
  872. ========
  873. >>> from sympy.polys.domains import ZZ
  874. >>> from sympy.polys.galoistools import gf_compose_mod
  875. >>> gf_compose_mod(ZZ.map([3, 2, 4]), ZZ.map([2, 2, 2]), ZZ.map([4, 3]), 5, ZZ)
  876. [4]
  877. """
  878. if not g:
  879. return []
  880. comp = [g[0]]
  881. for a in g[1:]:
  882. comp = gf_mul(comp, h, p, K)
  883. comp = gf_add_ground(comp, a, p, K)
  884. comp = gf_rem(comp, f, p, K)
  885. return comp
  886. def gf_trace_map(a, b, c, n, f, p, K):
  887. """
  888. Compute polynomial trace map in ``GF(p)[x]/(f)``.
  889. Given a polynomial ``f`` in ``GF(p)[x]``, polynomials ``a``, ``b``,
  890. ``c`` in the quotient ring ``GF(p)[x]/(f)`` such that ``b = c**t
  891. (mod f)`` for some positive power ``t`` of ``p``, and a positive
  892. integer ``n``, returns a mapping::
  893. a -> a**t**n, a + a**t + a**t**2 + ... + a**t**n (mod f)
  894. In factorization context, ``b = x**p mod f`` and ``c = x mod f``.
  895. This way we can efficiently compute trace polynomials in equal
  896. degree factorization routine, much faster than with other methods,
  897. like iterated Frobenius algorithm, for large degrees.
  898. Examples
  899. ========
  900. >>> from sympy.polys.domains import ZZ
  901. >>> from sympy.polys.galoistools import gf_trace_map
  902. >>> gf_trace_map([1, 2], [4, 4], [1, 1], 4, [3, 2, 4], 5, ZZ)
  903. ([1, 3], [1, 3])
  904. References
  905. ==========
  906. .. [1] [Gathen92]_
  907. """
  908. u = gf_compose_mod(a, b, f, p, K)
  909. v = b
  910. if n & 1:
  911. U = gf_add(a, u, p, K)
  912. V = b
  913. else:
  914. U = a
  915. V = c
  916. n >>= 1
  917. while n:
  918. u = gf_add(u, gf_compose_mod(u, v, f, p, K), p, K)
  919. v = gf_compose_mod(v, v, f, p, K)
  920. if n & 1:
  921. U = gf_add(U, gf_compose_mod(u, V, f, p, K), p, K)
  922. V = gf_compose_mod(v, V, f, p, K)
  923. n >>= 1
  924. return gf_compose_mod(a, V, f, p, K), U
  925. def _gf_trace_map(f, n, g, b, p, K):
  926. """
  927. utility for ``gf_edf_shoup``
  928. """
  929. f = gf_rem(f, g, p, K)
  930. h = f
  931. r = f
  932. for i in range(1, n):
  933. h = gf_frobenius_map(h, g, b, p, K)
  934. r = gf_add(r, h, p, K)
  935. r = gf_rem(r, g, p, K)
  936. return r
  937. def gf_random(n, p, K):
  938. """
  939. Generate a random polynomial in ``GF(p)[x]`` of degree ``n``.
  940. Examples
  941. ========
  942. >>> from sympy.polys.domains import ZZ
  943. >>> from sympy.polys.galoistools import gf_random
  944. >>> gf_random(10, 5, ZZ) #doctest: +SKIP
  945. [1, 2, 3, 2, 1, 1, 1, 2, 0, 4, 2]
  946. """
  947. return [K.one] + [ K(int(uniform(0, p))) for i in range(0, n) ]
  948. def gf_irreducible(n, p, K):
  949. """
  950. Generate random irreducible polynomial of degree ``n`` in ``GF(p)[x]``.
  951. Examples
  952. ========
  953. >>> from sympy.polys.domains import ZZ
  954. >>> from sympy.polys.galoistools import gf_irreducible
  955. >>> gf_irreducible(10, 5, ZZ) #doctest: +SKIP
  956. [1, 4, 2, 2, 3, 2, 4, 1, 4, 0, 4]
  957. """
  958. while True:
  959. f = gf_random(n, p, K)
  960. if gf_irreducible_p(f, p, K):
  961. return f
  962. def gf_irred_p_ben_or(f, p, K):
  963. """
  964. Ben-Or's polynomial irreducibility test over finite fields.
  965. Examples
  966. ========
  967. >>> from sympy.polys.domains import ZZ
  968. >>> from sympy.polys.galoistools import gf_irred_p_ben_or
  969. >>> gf_irred_p_ben_or(ZZ.map([1, 4, 2, 2, 3, 2, 4, 1, 4, 0, 4]), 5, ZZ)
  970. True
  971. >>> gf_irred_p_ben_or(ZZ.map([3, 2, 4]), 5, ZZ)
  972. False
  973. """
  974. n = gf_degree(f)
  975. if n <= 1:
  976. return True
  977. _, f = gf_monic(f, p, K)
  978. if n < 5:
  979. H = h = gf_pow_mod([K.one, K.zero], p, f, p, K)
  980. for i in range(0, n//2):
  981. g = gf_sub(h, [K.one, K.zero], p, K)
  982. if gf_gcd(f, g, p, K) == [K.one]:
  983. h = gf_compose_mod(h, H, f, p, K)
  984. else:
  985. return False
  986. else:
  987. b = gf_frobenius_monomial_base(f, p, K)
  988. H = h = gf_frobenius_map([K.one, K.zero], f, b, p, K)
  989. for i in range(0, n//2):
  990. g = gf_sub(h, [K.one, K.zero], p, K)
  991. if gf_gcd(f, g, p, K) == [K.one]:
  992. h = gf_frobenius_map(h, f, b, p, K)
  993. else:
  994. return False
  995. return True
  996. def gf_irred_p_rabin(f, p, K):
  997. """
  998. Rabin's polynomial irreducibility test over finite fields.
  999. Examples
  1000. ========
  1001. >>> from sympy.polys.domains import ZZ
  1002. >>> from sympy.polys.galoistools import gf_irred_p_rabin
  1003. >>> gf_irred_p_rabin(ZZ.map([1, 4, 2, 2, 3, 2, 4, 1, 4, 0, 4]), 5, ZZ)
  1004. True
  1005. >>> gf_irred_p_rabin(ZZ.map([3, 2, 4]), 5, ZZ)
  1006. False
  1007. """
  1008. n = gf_degree(f)
  1009. if n <= 1:
  1010. return True
  1011. _, f = gf_monic(f, p, K)
  1012. x = [K.one, K.zero]
  1013. from sympy.ntheory import factorint
  1014. indices = { n//d for d in factorint(n) }
  1015. b = gf_frobenius_monomial_base(f, p, K)
  1016. h = b[1]
  1017. for i in range(1, n):
  1018. if i in indices:
  1019. g = gf_sub(h, x, p, K)
  1020. if gf_gcd(f, g, p, K) != [K.one]:
  1021. return False
  1022. h = gf_frobenius_map(h, f, b, p, K)
  1023. return h == x
  1024. _irred_methods = {
  1025. 'ben-or': gf_irred_p_ben_or,
  1026. 'rabin': gf_irred_p_rabin,
  1027. }
  1028. def gf_irreducible_p(f, p, K):
  1029. """
  1030. Test irreducibility of a polynomial ``f`` in ``GF(p)[x]``.
  1031. Examples
  1032. ========
  1033. >>> from sympy.polys.domains import ZZ
  1034. >>> from sympy.polys.galoistools import gf_irreducible_p
  1035. >>> gf_irreducible_p(ZZ.map([1, 4, 2, 2, 3, 2, 4, 1, 4, 0, 4]), 5, ZZ)
  1036. True
  1037. >>> gf_irreducible_p(ZZ.map([3, 2, 4]), 5, ZZ)
  1038. False
  1039. """
  1040. method = query('GF_IRRED_METHOD')
  1041. if method is not None:
  1042. irred = _irred_methods[method](f, p, K)
  1043. else:
  1044. irred = gf_irred_p_rabin(f, p, K)
  1045. return irred
  1046. def gf_sqf_p(f, p, K):
  1047. """
  1048. Return ``True`` if ``f`` is square-free in ``GF(p)[x]``.
  1049. Examples
  1050. ========
  1051. >>> from sympy.polys.domains import ZZ
  1052. >>> from sympy.polys.galoistools import gf_sqf_p
  1053. >>> gf_sqf_p(ZZ.map([3, 2, 4]), 5, ZZ)
  1054. True
  1055. >>> gf_sqf_p(ZZ.map([2, 4, 4, 2, 2, 1, 4]), 5, ZZ)
  1056. False
  1057. """
  1058. _, f = gf_monic(f, p, K)
  1059. if not f:
  1060. return True
  1061. else:
  1062. return gf_gcd(f, gf_diff(f, p, K), p, K) == [K.one]
  1063. def gf_sqf_part(f, p, K):
  1064. """
  1065. Return square-free part of a ``GF(p)[x]`` polynomial.
  1066. Examples
  1067. ========
  1068. >>> from sympy.polys.domains import ZZ
  1069. >>> from sympy.polys.galoistools import gf_sqf_part
  1070. >>> gf_sqf_part(ZZ.map([1, 1, 3, 0, 1, 0, 2, 2, 1]), 5, ZZ)
  1071. [1, 4, 3]
  1072. """
  1073. _, sqf = gf_sqf_list(f, p, K)
  1074. g = [K.one]
  1075. for f, _ in sqf:
  1076. g = gf_mul(g, f, p, K)
  1077. return g
  1078. def gf_sqf_list(f, p, K, all=False):
  1079. """
  1080. Return the square-free decomposition of a ``GF(p)[x]`` polynomial.
  1081. Given a polynomial ``f`` in ``GF(p)[x]``, returns the leading coefficient
  1082. of ``f`` and a square-free decomposition ``f_1**e_1 f_2**e_2 ... f_k**e_k``
  1083. such that all ``f_i`` are monic polynomials and ``(f_i, f_j)`` for ``i != j``
  1084. are co-prime and ``e_1 ... e_k`` are given in increasing order. All trivial
  1085. terms (i.e. ``f_i = 1``) are not included in the output.
  1086. Consider polynomial ``f = x**11 + 1`` over ``GF(11)[x]``::
  1087. >>> from sympy.polys.domains import ZZ
  1088. >>> from sympy.polys.galoistools import (
  1089. ... gf_from_dict, gf_diff, gf_sqf_list, gf_pow,
  1090. ... )
  1091. ... # doctest: +NORMALIZE_WHITESPACE
  1092. >>> f = gf_from_dict({11: ZZ(1), 0: ZZ(1)}, 11, ZZ)
  1093. Note that ``f'(x) = 0``::
  1094. >>> gf_diff(f, 11, ZZ)
  1095. []
  1096. This phenomenon does not happen in characteristic zero. However we can
  1097. still compute square-free decomposition of ``f`` using ``gf_sqf()``::
  1098. >>> gf_sqf_list(f, 11, ZZ)
  1099. (1, [([1, 1], 11)])
  1100. We obtained factorization ``f = (x + 1)**11``. This is correct because::
  1101. >>> gf_pow([1, 1], 11, 11, ZZ) == f
  1102. True
  1103. References
  1104. ==========
  1105. .. [1] [Geddes92]_
  1106. """
  1107. n, sqf, factors, r = 1, False, [], int(p)
  1108. lc, f = gf_monic(f, p, K)
  1109. if gf_degree(f) < 1:
  1110. return lc, []
  1111. while True:
  1112. F = gf_diff(f, p, K)
  1113. if F != []:
  1114. g = gf_gcd(f, F, p, K)
  1115. h = gf_quo(f, g, p, K)
  1116. i = 1
  1117. while h != [K.one]:
  1118. G = gf_gcd(g, h, p, K)
  1119. H = gf_quo(h, G, p, K)
  1120. if gf_degree(H) > 0:
  1121. factors.append((H, i*n))
  1122. g, h, i = gf_quo(g, G, p, K), G, i + 1
  1123. if g == [K.one]:
  1124. sqf = True
  1125. else:
  1126. f = g
  1127. if not sqf:
  1128. d = gf_degree(f) // r
  1129. for i in range(0, d + 1):
  1130. f[i] = f[i*r]
  1131. f, n = f[:d + 1], n*r
  1132. else:
  1133. break
  1134. if all:
  1135. raise ValueError("'all=True' is not supported yet")
  1136. return lc, factors
  1137. def gf_Qmatrix(f, p, K):
  1138. """
  1139. Calculate Berlekamp's ``Q`` matrix.
  1140. Examples
  1141. ========
  1142. >>> from sympy.polys.domains import ZZ
  1143. >>> from sympy.polys.galoistools import gf_Qmatrix
  1144. >>> gf_Qmatrix([3, 2, 4], 5, ZZ)
  1145. [[1, 0],
  1146. [3, 4]]
  1147. >>> gf_Qmatrix([1, 0, 0, 0, 1], 5, ZZ)
  1148. [[1, 0, 0, 0],
  1149. [0, 4, 0, 0],
  1150. [0, 0, 1, 0],
  1151. [0, 0, 0, 4]]
  1152. """
  1153. n, r = gf_degree(f), int(p)
  1154. q = [K.one] + [K.zero]*(n - 1)
  1155. Q = [list(q)] + [[]]*(n - 1)
  1156. for i in range(1, (n - 1)*r + 1):
  1157. qq, c = [(-q[-1]*f[-1]) % p], q[-1]
  1158. for j in range(1, n):
  1159. qq.append((q[j - 1] - c*f[-j - 1]) % p)
  1160. if not (i % r):
  1161. Q[i//r] = list(qq)
  1162. q = qq
  1163. return Q
  1164. def gf_Qbasis(Q, p, K):
  1165. """
  1166. Compute a basis of the kernel of ``Q``.
  1167. Examples
  1168. ========
  1169. >>> from sympy.polys.domains import ZZ
  1170. >>> from sympy.polys.galoistools import gf_Qmatrix, gf_Qbasis
  1171. >>> gf_Qbasis(gf_Qmatrix([1, 0, 0, 0, 1], 5, ZZ), 5, ZZ)
  1172. [[1, 0, 0, 0], [0, 0, 1, 0]]
  1173. >>> gf_Qbasis(gf_Qmatrix([3, 2, 4], 5, ZZ), 5, ZZ)
  1174. [[1, 0]]
  1175. """
  1176. Q, n = [ list(q) for q in Q ], len(Q)
  1177. for k in range(0, n):
  1178. Q[k][k] = (Q[k][k] - K.one) % p
  1179. for k in range(0, n):
  1180. for i in range(k, n):
  1181. if Q[k][i]:
  1182. break
  1183. else:
  1184. continue
  1185. inv = K.invert(Q[k][i], p)
  1186. for j in range(0, n):
  1187. Q[j][i] = (Q[j][i]*inv) % p
  1188. for j in range(0, n):
  1189. t = Q[j][k]
  1190. Q[j][k] = Q[j][i]
  1191. Q[j][i] = t
  1192. for i in range(0, n):
  1193. if i != k:
  1194. q = Q[k][i]
  1195. for j in range(0, n):
  1196. Q[j][i] = (Q[j][i] - Q[j][k]*q) % p
  1197. for i in range(0, n):
  1198. for j in range(0, n):
  1199. if i == j:
  1200. Q[i][j] = (K.one - Q[i][j]) % p
  1201. else:
  1202. Q[i][j] = (-Q[i][j]) % p
  1203. basis = []
  1204. for q in Q:
  1205. if any(q):
  1206. basis.append(q)
  1207. return basis
  1208. def gf_berlekamp(f, p, K):
  1209. """
  1210. Factor a square-free ``f`` in ``GF(p)[x]`` for small ``p``.
  1211. Examples
  1212. ========
  1213. >>> from sympy.polys.domains import ZZ
  1214. >>> from sympy.polys.galoistools import gf_berlekamp
  1215. >>> gf_berlekamp([1, 0, 0, 0, 1], 5, ZZ)
  1216. [[1, 0, 2], [1, 0, 3]]
  1217. """
  1218. Q = gf_Qmatrix(f, p, K)
  1219. V = gf_Qbasis(Q, p, K)
  1220. for i, v in enumerate(V):
  1221. V[i] = gf_strip(list(reversed(v)))
  1222. factors = [f]
  1223. for k in range(1, len(V)):
  1224. for f in list(factors):
  1225. s = K.zero
  1226. while s < p:
  1227. g = gf_sub_ground(V[k], s, p, K)
  1228. h = gf_gcd(f, g, p, K)
  1229. if h != [K.one] and h != f:
  1230. factors.remove(f)
  1231. f = gf_quo(f, h, p, K)
  1232. factors.extend([f, h])
  1233. if len(factors) == len(V):
  1234. return _sort_factors(factors, multiple=False)
  1235. s += K.one
  1236. return _sort_factors(factors, multiple=False)
  1237. def gf_ddf_zassenhaus(f, p, K):
  1238. """
  1239. Cantor-Zassenhaus: Deterministic Distinct Degree Factorization
  1240. Given a monic square-free polynomial ``f`` in ``GF(p)[x]``, computes
  1241. partial distinct degree factorization ``f_1 ... f_d`` of ``f`` where
  1242. ``deg(f_i) != deg(f_j)`` for ``i != j``. The result is returned as a
  1243. list of pairs ``(f_i, e_i)`` where ``deg(f_i) > 0`` and ``e_i > 0``
  1244. is an argument to the equal degree factorization routine.
  1245. Consider the polynomial ``x**15 - 1`` in ``GF(11)[x]``::
  1246. >>> from sympy.polys.domains import ZZ
  1247. >>> from sympy.polys.galoistools import gf_from_dict
  1248. >>> f = gf_from_dict({15: ZZ(1), 0: ZZ(-1)}, 11, ZZ)
  1249. Distinct degree factorization gives::
  1250. >>> from sympy.polys.galoistools import gf_ddf_zassenhaus
  1251. >>> gf_ddf_zassenhaus(f, 11, ZZ)
  1252. [([1, 0, 0, 0, 0, 10], 1), ([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2)]
  1253. which means ``x**15 - 1 = (x**5 - 1) (x**10 + x**5 + 1)``. To obtain
  1254. factorization into irreducibles, use equal degree factorization
  1255. procedure (EDF) with each of the factors.
  1256. References
  1257. ==========
  1258. .. [1] [Gathen99]_
  1259. .. [2] [Geddes92]_
  1260. """
  1261. i, g, factors = 1, [K.one, K.zero], []
  1262. b = gf_frobenius_monomial_base(f, p, K)
  1263. while 2*i <= gf_degree(f):
  1264. g = gf_frobenius_map(g, f, b, p, K)
  1265. h = gf_gcd(f, gf_sub(g, [K.one, K.zero], p, K), p, K)
  1266. if h != [K.one]:
  1267. factors.append((h, i))
  1268. f = gf_quo(f, h, p, K)
  1269. g = gf_rem(g, f, p, K)
  1270. b = gf_frobenius_monomial_base(f, p, K)
  1271. i += 1
  1272. if f != [K.one]:
  1273. return factors + [(f, gf_degree(f))]
  1274. else:
  1275. return factors
  1276. def gf_edf_zassenhaus(f, n, p, K):
  1277. """
  1278. Cantor-Zassenhaus: Probabilistic Equal Degree Factorization
  1279. Given a monic square-free polynomial ``f`` in ``GF(p)[x]`` and
  1280. an integer ``n``, such that ``n`` divides ``deg(f)``, returns all
  1281. irreducible factors ``f_1,...,f_d`` of ``f``, each of degree ``n``.
  1282. EDF procedure gives complete factorization over Galois fields.
  1283. Consider the square-free polynomial ``f = x**3 + x**2 + x + 1`` in
  1284. ``GF(5)[x]``. Let's compute its irreducible factors of degree one::
  1285. >>> from sympy.polys.domains import ZZ
  1286. >>> from sympy.polys.galoistools import gf_edf_zassenhaus
  1287. >>> gf_edf_zassenhaus([1,1,1,1], 1, 5, ZZ)
  1288. [[1, 1], [1, 2], [1, 3]]
  1289. Notes
  1290. =====
  1291. The case p == 2 is handled by Cohen's Algorithm 3.4.8. The case p odd is
  1292. as in Geddes Algorithm 8.9 (or Cohen's Algorithm 3.4.6).
  1293. References
  1294. ==========
  1295. .. [1] [Gathen99]_
  1296. .. [2] [Geddes92]_ Algorithm 8.9
  1297. .. [3] [Cohen93]_ Algorithm 3.4.8
  1298. """
  1299. factors = [f]
  1300. if gf_degree(f) <= n:
  1301. return factors
  1302. N = gf_degree(f) // n
  1303. if p != 2:
  1304. b = gf_frobenius_monomial_base(f, p, K)
  1305. t = [K.one, K.zero]
  1306. while len(factors) < N:
  1307. if p == 2:
  1308. h = r = t
  1309. for i in range(n - 1):
  1310. r = gf_pow_mod(r, 2, f, p, K)
  1311. h = gf_add(h, r, p, K)
  1312. g = gf_gcd(f, h, p, K)
  1313. t += [K.zero, K.zero]
  1314. else:
  1315. r = gf_random(2 * n - 1, p, K)
  1316. h = _gf_pow_pnm1d2(r, n, f, b, p, K)
  1317. g = gf_gcd(f, gf_sub_ground(h, K.one, p, K), p, K)
  1318. if g != [K.one] and g != f:
  1319. factors = gf_edf_zassenhaus(g, n, p, K) \
  1320. + gf_edf_zassenhaus(gf_quo(f, g, p, K), n, p, K)
  1321. return _sort_factors(factors, multiple=False)
  1322. def gf_ddf_shoup(f, p, K):
  1323. """
  1324. Kaltofen-Shoup: Deterministic Distinct Degree Factorization
  1325. Given a monic square-free polynomial ``f`` in ``GF(p)[x]``, computes
  1326. partial distinct degree factorization ``f_1,...,f_d`` of ``f`` where
  1327. ``deg(f_i) != deg(f_j)`` for ``i != j``. The result is returned as a
  1328. list of pairs ``(f_i, e_i)`` where ``deg(f_i) > 0`` and ``e_i > 0``
  1329. is an argument to the equal degree factorization routine.
  1330. This algorithm is an improved version of Zassenhaus algorithm for
  1331. large ``deg(f)`` and modulus ``p`` (especially for ``deg(f) ~ lg(p)``).
  1332. Examples
  1333. ========
  1334. >>> from sympy.polys.domains import ZZ
  1335. >>> from sympy.polys.galoistools import gf_ddf_shoup, gf_from_dict
  1336. >>> f = gf_from_dict({6: ZZ(1), 5: ZZ(-1), 4: ZZ(1), 3: ZZ(1), 1: ZZ(-1)}, 3, ZZ)
  1337. >>> gf_ddf_shoup(f, 3, ZZ)
  1338. [([1, 1, 0], 1), ([1, 1, 0, 1, 2], 2)]
  1339. References
  1340. ==========
  1341. .. [1] [Kaltofen98]_
  1342. .. [2] [Shoup95]_
  1343. .. [3] [Gathen92]_
  1344. """
  1345. n = gf_degree(f)
  1346. k = int(_ceil(_sqrt(n//2)))
  1347. b = gf_frobenius_monomial_base(f, p, K)
  1348. h = gf_frobenius_map([K.one, K.zero], f, b, p, K)
  1349. # U[i] = x**(p**i)
  1350. U = [[K.one, K.zero], h] + [K.zero]*(k - 1)
  1351. for i in range(2, k + 1):
  1352. U[i] = gf_frobenius_map(U[i-1], f, b, p, K)
  1353. h, U = U[k], U[:k]
  1354. # V[i] = x**(p**(k*(i+1)))
  1355. V = [h] + [K.zero]*(k - 1)
  1356. for i in range(1, k):
  1357. V[i] = gf_compose_mod(V[i - 1], h, f, p, K)
  1358. factors = []
  1359. for i, v in enumerate(V):
  1360. h, j = [K.one], k - 1
  1361. for u in U:
  1362. g = gf_sub(v, u, p, K)
  1363. h = gf_mul(h, g, p, K)
  1364. h = gf_rem(h, f, p, K)
  1365. g = gf_gcd(f, h, p, K)
  1366. f = gf_quo(f, g, p, K)
  1367. for u in reversed(U):
  1368. h = gf_sub(v, u, p, K)
  1369. F = gf_gcd(g, h, p, K)
  1370. if F != [K.one]:
  1371. factors.append((F, k*(i + 1) - j))
  1372. g, j = gf_quo(g, F, p, K), j - 1
  1373. if f != [K.one]:
  1374. factors.append((f, gf_degree(f)))
  1375. return factors
  1376. def gf_edf_shoup(f, n, p, K):
  1377. """
  1378. Gathen-Shoup: Probabilistic Equal Degree Factorization
  1379. Given a monic square-free polynomial ``f`` in ``GF(p)[x]`` and integer
  1380. ``n`` such that ``n`` divides ``deg(f)``, returns all irreducible factors
  1381. ``f_1,...,f_d`` of ``f``, each of degree ``n``. This is a complete
  1382. factorization over Galois fields.
  1383. This algorithm is an improved version of Zassenhaus algorithm for
  1384. large ``deg(f)`` and modulus ``p`` (especially for ``deg(f) ~ lg(p)``).
  1385. Examples
  1386. ========
  1387. >>> from sympy.polys.domains import ZZ
  1388. >>> from sympy.polys.galoistools import gf_edf_shoup
  1389. >>> gf_edf_shoup(ZZ.map([1, 2837, 2277]), 1, 2917, ZZ)
  1390. [[1, 852], [1, 1985]]
  1391. References
  1392. ==========
  1393. .. [1] [Shoup91]_
  1394. .. [2] [Gathen92]_
  1395. """
  1396. N, q = gf_degree(f), int(p)
  1397. if not N:
  1398. return []
  1399. if N <= n:
  1400. return [f]
  1401. factors, x = [f], [K.one, K.zero]
  1402. r = gf_random(N - 1, p, K)
  1403. if p == 2:
  1404. h = gf_pow_mod(x, q, f, p, K)
  1405. H = gf_trace_map(r, h, x, n - 1, f, p, K)[1]
  1406. h1 = gf_gcd(f, H, p, K)
  1407. h2 = gf_quo(f, h1, p, K)
  1408. factors = gf_edf_shoup(h1, n, p, K) \
  1409. + gf_edf_shoup(h2, n, p, K)
  1410. else:
  1411. b = gf_frobenius_monomial_base(f, p, K)
  1412. H = _gf_trace_map(r, n, f, b, p, K)
  1413. h = gf_pow_mod(H, (q - 1)//2, f, p, K)
  1414. h1 = gf_gcd(f, h, p, K)
  1415. h2 = gf_gcd(f, gf_sub_ground(h, K.one, p, K), p, K)
  1416. h3 = gf_quo(f, gf_mul(h1, h2, p, K), p, K)
  1417. factors = gf_edf_shoup(h1, n, p, K) \
  1418. + gf_edf_shoup(h2, n, p, K) \
  1419. + gf_edf_shoup(h3, n, p, K)
  1420. return _sort_factors(factors, multiple=False)
  1421. def gf_zassenhaus(f, p, K):
  1422. """
  1423. Factor a square-free ``f`` in ``GF(p)[x]`` for medium ``p``.
  1424. Examples
  1425. ========
  1426. >>> from sympy.polys.domains import ZZ
  1427. >>> from sympy.polys.galoistools import gf_zassenhaus
  1428. >>> gf_zassenhaus(ZZ.map([1, 4, 3]), 5, ZZ)
  1429. [[1, 1], [1, 3]]
  1430. """
  1431. factors = []
  1432. for factor, n in gf_ddf_zassenhaus(f, p, K):
  1433. factors += gf_edf_zassenhaus(factor, n, p, K)
  1434. return _sort_factors(factors, multiple=False)
  1435. def gf_shoup(f, p, K):
  1436. """
  1437. Factor a square-free ``f`` in ``GF(p)[x]`` for large ``p``.
  1438. Examples
  1439. ========
  1440. >>> from sympy.polys.domains import ZZ
  1441. >>> from sympy.polys.galoistools import gf_shoup
  1442. >>> gf_shoup(ZZ.map([1, 4, 3]), 5, ZZ)
  1443. [[1, 1], [1, 3]]
  1444. """
  1445. factors = []
  1446. for factor, n in gf_ddf_shoup(f, p, K):
  1447. factors += gf_edf_shoup(factor, n, p, K)
  1448. return _sort_factors(factors, multiple=False)
  1449. _factor_methods = {
  1450. 'berlekamp': gf_berlekamp, # ``p`` : small
  1451. 'zassenhaus': gf_zassenhaus, # ``p`` : medium
  1452. 'shoup': gf_shoup, # ``p`` : large
  1453. }
  1454. def gf_factor_sqf(f, p, K, method=None):
  1455. """
  1456. Factor a square-free polynomial ``f`` in ``GF(p)[x]``.
  1457. Examples
  1458. ========
  1459. >>> from sympy.polys.domains import ZZ
  1460. >>> from sympy.polys.galoistools import gf_factor_sqf
  1461. >>> gf_factor_sqf(ZZ.map([3, 2, 4]), 5, ZZ)
  1462. (3, [[1, 1], [1, 3]])
  1463. """
  1464. lc, f = gf_monic(f, p, K)
  1465. if gf_degree(f) < 1:
  1466. return lc, []
  1467. method = method or query('GF_FACTOR_METHOD')
  1468. if method is not None:
  1469. factors = _factor_methods[method](f, p, K)
  1470. else:
  1471. factors = gf_zassenhaus(f, p, K)
  1472. return lc, factors
  1473. def gf_factor(f, p, K):
  1474. """
  1475. Factor (non square-free) polynomials in ``GF(p)[x]``.
  1476. Given a possibly non square-free polynomial ``f`` in ``GF(p)[x]``,
  1477. returns its complete factorization into irreducibles::
  1478. f_1(x)**e_1 f_2(x)**e_2 ... f_d(x)**e_d
  1479. where each ``f_i`` is a monic polynomial and ``gcd(f_i, f_j) == 1``,
  1480. for ``i != j``. The result is given as a tuple consisting of the
  1481. leading coefficient of ``f`` and a list of factors of ``f`` with
  1482. their multiplicities.
  1483. The algorithm proceeds by first computing square-free decomposition
  1484. of ``f`` and then iteratively factoring each of square-free factors.
  1485. Consider a non square-free polynomial ``f = (7*x + 1) (x + 2)**2`` in
  1486. ``GF(11)[x]``. We obtain its factorization into irreducibles as follows::
  1487. >>> from sympy.polys.domains import ZZ
  1488. >>> from sympy.polys.galoistools import gf_factor
  1489. >>> gf_factor(ZZ.map([5, 2, 7, 2]), 11, ZZ)
  1490. (5, [([1, 2], 1), ([1, 8], 2)])
  1491. We arrived with factorization ``f = 5 (x + 2) (x + 8)**2``. We did not
  1492. recover the exact form of the input polynomial because we requested to
  1493. get monic factors of ``f`` and its leading coefficient separately.
  1494. Square-free factors of ``f`` can be factored into irreducibles over
  1495. ``GF(p)`` using three very different methods:
  1496. Berlekamp
  1497. efficient for very small values of ``p`` (usually ``p < 25``)
  1498. Cantor-Zassenhaus
  1499. efficient on average input and with "typical" ``p``
  1500. Shoup-Kaltofen-Gathen
  1501. efficient with very large inputs and modulus
  1502. If you want to use a specific factorization method, instead of the default
  1503. one, set ``GF_FACTOR_METHOD`` with one of ``berlekamp``, ``zassenhaus`` or
  1504. ``shoup`` values.
  1505. References
  1506. ==========
  1507. .. [1] [Gathen99]_
  1508. """
  1509. lc, f = gf_monic(f, p, K)
  1510. if gf_degree(f) < 1:
  1511. return lc, []
  1512. factors = []
  1513. for g, n in gf_sqf_list(f, p, K)[1]:
  1514. for h in gf_factor_sqf(g, p, K)[1]:
  1515. factors.append((h, n))
  1516. return lc, _sort_factors(factors)
  1517. def gf_value(f, a):
  1518. """
  1519. Value of polynomial 'f' at 'a' in field R.
  1520. Examples
  1521. ========
  1522. >>> from sympy.polys.galoistools import gf_value
  1523. >>> gf_value([1, 7, 2, 4], 11)
  1524. 2204
  1525. """
  1526. result = 0
  1527. for c in f:
  1528. result *= a
  1529. result += c
  1530. return result
  1531. def linear_congruence(a, b, m):
  1532. """
  1533. Returns the values of x satisfying a*x congruent b mod(m)
  1534. Here m is positive integer and a, b are natural numbers.
  1535. This function returns only those values of x which are distinct mod(m).
  1536. Examples
  1537. ========
  1538. >>> from sympy.polys.galoistools import linear_congruence
  1539. >>> linear_congruence(3, 12, 15)
  1540. [4, 9, 14]
  1541. There are 3 solutions distinct mod(15) since gcd(a, m) = gcd(3, 15) = 3.
  1542. References
  1543. ==========
  1544. .. [1] https://en.wikipedia.org/wiki/Linear_congruence_theorem
  1545. """
  1546. from sympy.polys.polytools import gcdex
  1547. if a % m == 0:
  1548. if b % m == 0:
  1549. return list(range(m))
  1550. else:
  1551. return []
  1552. r, _, g = gcdex(a, m)
  1553. if b % g != 0:
  1554. return []
  1555. return [(r * b // g + t * m // g) % m for t in range(g)]
  1556. def _raise_mod_power(x, s, p, f):
  1557. """
  1558. Used in gf_csolve to generate solutions of f(x) cong 0 mod(p**(s + 1))
  1559. from the solutions of f(x) cong 0 mod(p**s).
  1560. Examples
  1561. ========
  1562. >>> from sympy.polys.galoistools import _raise_mod_power
  1563. >>> from sympy.polys.galoistools import csolve_prime
  1564. These is the solutions of f(x) = x**2 + x + 7 cong 0 mod(3)
  1565. >>> f = [1, 1, 7]
  1566. >>> csolve_prime(f, 3)
  1567. [1]
  1568. >>> [ i for i in range(3) if not (i**2 + i + 7) % 3]
  1569. [1]
  1570. The solutions of f(x) cong 0 mod(9) are constructed from the
  1571. values returned from _raise_mod_power:
  1572. >>> x, s, p = 1, 1, 3
  1573. >>> V = _raise_mod_power(x, s, p, f)
  1574. >>> [x + v * p**s for v in V]
  1575. [1, 4, 7]
  1576. And these are confirmed with the following:
  1577. >>> [ i for i in range(3**2) if not (i**2 + i + 7) % 3**2]
  1578. [1, 4, 7]
  1579. """
  1580. from sympy.polys.domains import ZZ
  1581. f_f = gf_diff(f, p, ZZ)
  1582. alpha = gf_value(f_f, x)
  1583. beta = - gf_value(f, x) // p**s
  1584. return linear_congruence(alpha, beta, p)
  1585. def csolve_prime(f, p, e=1):
  1586. """
  1587. Solutions of f(x) congruent 0 mod(p**e).
  1588. Examples
  1589. ========
  1590. >>> from sympy.polys.galoistools import csolve_prime
  1591. >>> csolve_prime([1, 1, 7], 3, 1)
  1592. [1]
  1593. >>> csolve_prime([1, 1, 7], 3, 2)
  1594. [1, 4, 7]
  1595. Solutions [7, 4, 1] (mod 3**2) are generated by ``_raise_mod_power()``
  1596. from solution [1] (mod 3).
  1597. """
  1598. from sympy.polys.domains import ZZ
  1599. X1 = [i for i in range(p) if gf_eval(f, i, p, ZZ) == 0]
  1600. if e == 1:
  1601. return X1
  1602. X = []
  1603. S = list(zip(X1, [1]*len(X1)))
  1604. while S:
  1605. x, s = S.pop()
  1606. if s == e:
  1607. X.append(x)
  1608. else:
  1609. s1 = s + 1
  1610. ps = p**s
  1611. S.extend([(x + v*ps, s1) for v in _raise_mod_power(x, s, p, f)])
  1612. return sorted(X)
  1613. def gf_csolve(f, n):
  1614. """
  1615. To solve f(x) congruent 0 mod(n).
  1616. n is divided into canonical factors and f(x) cong 0 mod(p**e) will be
  1617. solved for each factor. Applying the Chinese Remainder Theorem to the
  1618. results returns the final answers.
  1619. Examples
  1620. ========
  1621. Solve [1, 1, 7] congruent 0 mod(189):
  1622. >>> from sympy.polys.galoistools import gf_csolve
  1623. >>> gf_csolve([1, 1, 7], 189)
  1624. [13, 49, 76, 112, 139, 175]
  1625. References
  1626. ==========
  1627. .. [1] 'An introduction to the Theory of Numbers' 5th Edition by Ivan Niven,
  1628. Zuckerman and Montgomery.
  1629. """
  1630. from sympy.polys.domains import ZZ
  1631. from sympy.ntheory import factorint
  1632. P = factorint(n)
  1633. X = [csolve_prime(f, p, e) for p, e in P.items()]
  1634. pools = list(map(tuple, X))
  1635. perms = [[]]
  1636. for pool in pools:
  1637. perms = [x + [y] for x in perms for y in pool]
  1638. dist_factors = [pow(p, e) for p, e in P.items()]
  1639. return sorted([gf_crt(per, dist_factors, ZZ) for per in perms])