galoistools.py 51 KB

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