galoistools.py 56 KB

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