2 hensel -- Hensel Lift classes for factorization 3 of integer coefficient polynomials 6 If you need lifted factors only once, you may want to use lift_upto 7 function. On the other hand, if you might happen to need another 8 lift, you would directly use one of the lifters and its lift method 12 from __future__
import division
20 the_ring = polyring.PolynomialRing(rational.theIntegerRing)
21 the_one = the_ring.one
22 the_zero = the_ring.zero
27 _extgcdp(f,g,p) -> u,v,w 29 Find u,v,w such that f*u + g*v = w = gcd(f,g) mod p. 30 p should be a prime number. 32 This is a private function. 34 modp =
lambda c: c % p
35 u, v, w, x, y, z = the_one, the_zero, f, the_zero, the_one, g
37 zlc = z.leading_coefficient()
39 normalizer = arith1.inverse(zlc, p)
40 x = (x * normalizer).coefficients_map(modp)
41 y = (y * normalizer).coefficients_map(modp)
42 z = (z * normalizer).coefficients_map(modp)
43 q = w.pseudo_floordiv(z)
44 u, v, w, x, y, z = x, y, z, u - q*x, v - q*y, w - q*z
45 x = x.coefficients_map(modp)
46 y = y.coefficients_map(modp)
47 z = z.coefficients_map(modp)
48 if w.degree() == 0
and w[0] != 1:
49 u = u.scalar_exact_division(w[0])
50 v = v.scalar_exact_division(w[0])
56 Return list of bi's; bi = a{i+1}*...*ar where ai's are of factors. 58 This is a private function. 61 for ai
in factors[-1:0:-1]:
62 bis.append(bis[-1] * ai)
69 A class represents integer polynomial pair which will be lifted by 72 def __init__(self, target, factor1, factor2, ladder1, ladder2, p, q=None):
74 HenselLiftPair(f, a1, a2, u1, u2, p, q) 76 The parameters satisfy that: 78 f == a1*a2 (mod q) and 79 a1*u1 + a2*u2 == 1 (mod p), 80 with positive integers p dividing q. 82 If p==q, q can be omit from the argument. 98 Create and return an instance of HenselLiftPair. 100 HenselLiftPair.from_factors(f, a1, a2, p) 102 The parameters satisfy that: 104 with a prime number p. 106 u1, u2 =
_extgcdp(factor1, factor2, p)[:2]
107 return cls(target, factor1, factor2, u1, u2, p)
111 Update factors by lifted integer coefficient polynomials Ai's: 112 f == A1*A2 (mod p*q) and 113 Ai == ai (mod q) (i = 1, 2). 114 Moreover, q is updated by p*q 116 PRECONDITIONS (automatically satisfied): 118 a1*u1 + a2*u2 == 1 (mod p) 121 assert not (self.
a1*self.
u1 + self.
a2*self.
u2 - the_one).coefficients_map(
lambda c: c % self.
p)
122 higher = (self.
f - self.
a1 * self.
a2).scalar_exact_division(self.
q)
123 quot, rem = (self.
u2 * higher).pseudo_divmod(self.
a1)
124 filterq =
lambda c: (c % self.
p) * self.
q 125 self.
a1 += rem.coefficients_map(filterq)
126 self.
a2 += (self.
u1 * higher + self.
a2 * quot).coefficients_map(filterq)
128 assert not (self.
a1*self.
u1 + self.
a2*self.
u2 - the_one).coefficients_map(
lambda c: c % self.
p)
132 Update u1 and u2 with U1 and U2: 133 a1*U1 + a2*U2 == 1 (mod p**2) 134 Ui == ui (mod p) (i = 1, 2) 135 then, update p with p**2. 137 PRECONDITIONS (automatically satisfied): 138 a1*u1 + a2*u2 == 1 (mod p) 140 higher = (the_one - self.
u1 * self.
a1 - self.
u2 * self.
a2).scalar_exact_division(self.
p)
141 quot, rem = (self.
u2 * higher).pseudo_divmod(self.
a1)
142 filterp =
lambda c: (c % self.
p) * self.
p 143 self.
u1 += (self.
u1 * higher + self.
a2 * quot).coefficients_map(filterp)
144 self.
u2 += rem.coefficients_map(filterp)
156 getter for factors of target mod q 158 return [self.
a1, self.
a2]
160 factors = property(_get_factors)
165 A class represents integer polynomials which will be lifted by 168 If the number of factors is two, you should use HenselLiftPair. 170 def __init__(self, target, factors, cofactors, ladder, p, q=None):
172 HenselLiftMulti(f, factors, ladder, p, q) 174 The parameters satisfy that: 175 + ai's of factors and f are all monic 176 + bi's of cofactors are product of aj's for i < j 177 + f == a1*...*ar (mod q) 178 + ladder is a tuple (sis, tis) and si's of sis and ti's of tis 179 satisfy: ai*si + bi*ti == 1 (mod p), 180 with positive integers p dividing q. 182 If p==q, q can be omit from the argument. 185 self.
a = list(factors)
187 self.
b = list(cofactors)
188 self.
s = list(ladder[0])
189 self.
t = list(ladder[1])
199 Create and return an instance of HenselLiftMulti. 201 HenselLiftMulti.from_factors(f, factors, p) 203 The parameters satisfy that: 204 f == a1*...*ar (mod p) 205 with a prime number p and ai's in factors. 209 for ai0, bi0
in zip(factors, bi0s):
213 return cls(target, factors, bi0s, (sis, tis), p, p)
217 Find a lifted integer coefficient polynomials such that: 218 f = A1*A2*...*Ar (mod q*p), 219 Ai = ai (mod q) (i=1,2,...,r), 220 then, update q with q*p. 222 PRECONDITIONS (automatically satisfied): 223 f = a1*a2*...*ar (mod q), 224 ai*si + bi*ti = 1 (mod p) (i=1,2,...,r) and 228 for i
in range(self.
r - 1):
229 assert not (self.
a[i]*self.
s[i] + self.
b[i]*self.
t[i] - the_one).coefficients_map(
lambda c: c % self.
p), \
230 "a = %s\ns = %s\nb = %s\nt = %s" %(self.
a[i], self.
s[i], self.
b[i], self.
t[i])
232 self.
a[i], self.
b[i],
233 self.
s[i], self.
t[i],
235 mini_pair.lift_factors()
236 self.
a[i] = mini_pair.a1
237 mini_target = self.
b[i] = mini_pair.a2
238 self.
a[self.
r - 1] = mini_target
243 Update ladder si's and ti's with Si's and Ti's: 244 ai*Si + bi*Ti == 1 (mod p**2) (i = 1, 2, ..., r), 245 Si == si (mod p) (i = 1, 2, ..., r) and 246 Ti == ti (mod p) (i = 1, 2, ..., r) 247 then, update p with p**2. 249 PRECONDITIONS (automatically satisfied): 250 ai*si + bi*ti == 1 (mod p) 252 for i
in range(self.
r - 1):
254 self.
a[i], self.
b[i],
255 self.
s[i], self.
t[i],
257 mini_pair.lift_ladder()
258 self.
s[i], self.
t[i] = mini_pair.u1, mini_pair.u2
270 getter for factors of target mod q 274 factors = property(_get_factors)
280 ai's, pi's and gi's are monic 281 f == g1*g2*...*gr (mod p) 282 f == d0 + d1*p + d2*p**2 +...+ dk*p**k 284 1 == gi*si + hi*ti (mod p) (i = 1, 2,..., r) 285 deg(si) < deg(hi), deg(ti) < deg(gi) 287 f == l1*l2*...*lr (mod q/p) 288 f == a1*a2*...*ar (mod q) 289 ui == ai*yi + bi*zi (mod p) (i = 1, 2,..., r) 292 G.E.Collins & M.J.Encarnaci'on. 293 Improved Techniques for factoring Univariate Polynomials, 294 J.Symb.Comp. 21, 313--327 (1996). 297 def __init__(self, target, factors, cofactors, bases, p):
304 self.
his = tuple(cofactors)
306 self.
sis = tuple(bases[0])
307 self.
tis = tuple(bases[1])
315 self.ais, self.
bis = {}, {}
323 Return p-adic expansion of target polynomial. 325 This method is private for __init__. 327 self.
dis.append(self.
f.coefficients_map(self.
modp))
328 rest = self.
f - self.
dis[-1]
330 while any(abs(x) >= q
for x
in self.
f.itercoefficients()):
331 self.
dis.append(self.
f.coefficients_map(
lambda c: (c // q) % self.
p))
332 rest -= self.
dis[-1].scalar_mul(q)
335 self.
dis.append(rest.scalar_exact_division(q))
340 Create and return an instance of HenselLiftSimultaneously, 341 whose factors are lifted by HenselLiftMulti upto ubound (if it 342 is smaller than sys.maxint, or upto sys.maxint). 344 HenselLiftSimultaneously.from_factors(f, factors, p) 346 The parameters satisfy that: 347 f == a1*...*ar (mod p) 348 with a prime number p and ai's in factors. 350 lifter = HenselLiftMulti.from_factors(target, factors, p)
351 interbound = min(ubound, sys.maxint)
352 while lifter.p < interbound:
353 lifter.lift_factors()
355 new = cls(target, lifter.a, lifter.b, (lifter.s, lifter.t), lifter.p)
362 f == l1*l2*...*lr (mod p**2) 363 Initialize di's, ui's, yi's and zi's. 365 Then, update q with p**2. 367 assert self.
p == self.
q 369 for i
in range(self.
r - 1):
371 assert self.
gis[-1] == self.
his[self.
r - 2]
374 if len(self.
dis) > 1:
375 mini_target = self.
dis[0] + self.
dis[1].scalar_mul(self.
p)
378 mini_target = self.
dis[0]
380 self.
uis, self.
yis, self.
zis = [], {}, {}
381 for i
in xrange(self.
r - 1):
382 dividend = mini_target - self.
gis[i] * self.
his[i]
383 self.
uis.append(dividend.scalar_exact_division(self.
p))
385 aj.append(self.
gis[i] + self.
zis[i].scalar_mul(self.
q))
386 bj.append(self.
his[i] + self.
yis[i].scalar_mul(self.
q))
393 for i
in range(self.
r - 1):
394 self.
_assertEqualModulo(self.
f, arith1.product(aj[:i+1])*bj[i], self.
q,
"f != l%d * m%d" % (i, i))
398 self.ais[-1], self.ais[0] = self.
gis, tuple(aj)
399 self.
bis[-1], self.
bis[0] = self.
his, tuple(bj)
404 f == a1*a2*...*ar (mod p*q) 405 Update ai's, bi's, ui's, yi's and zi's. 406 Then, update q with p*q. 408 j = arith1.vp(self.
q, self.
p)[0]
409 if len(self.
dis) > j:
410 mini_target = self.
dis[j]
412 mini_target = the_zero
414 self.ais[-2], self.ais[-1] = self.ais[-1], self.ais[0]
417 for i
in xrange(self.
r - 1):
418 yi, zi = self.
yis[i], self.
zis[i]
419 dividend = self.ais[-2][i]*yi + self.
bis[-2][i]*zi - self.
uis[i]
420 v_j = dividend.scalar_exact_division(self.
p)
422 self.
uis[i] = mini_target - v_j - yi*zi
424 self.
uis[i] = mini_target - v_j - (yi*zi).scalar_mul(self.
p**(j - 2))
426 aj.append(self.ais[-1][i] + self.
zis[i].scalar_mul(self.
q))
427 bj.append(self.
bis[-1][i] + self.
yis[i].scalar_mul(self.
q))
429 mini_target = self.
yis[i]
433 for i
in range(self.
r - 1):
434 self.
_assertEqualModulo(self.
f, arith1.product(aj[:i+1])*bj[i], self.
q,
"f != a%d * b%d" % (i, i))
438 self.ais[0] = tuple(aj)
439 self.
bis[0] = tuple(bj)
450 gi Y + hi Z = ui (mod p) 451 satisfying conditions: 452 (1) deg(Y) < deg(hi) and 453 (2) deg(Z) < deg(gi), 454 and return a tuple(Y, Z). 455 The method needs the following conditions: 456 (I) deg(ui) <= deg(gi)deg(hi), 457 (II) gi si + hi ti = 1 (mod p). 459 umodp = self.
uis[i].coefficients_map(self.
modp)
460 quot, rem = (self.
tis[i] * umodp).pseudo_divmod(self.
gis[i])
461 y = (self.
sis[i] * umodp + self.
his[i] * quot).coefficients_map(self.
modp)
462 z = rem.coefficients_map(self.
modp)
464 assert y.degree() < self.
his[i].degree()
472 getter for factors of target mod q 475 return list(self.ais[0])
477 return list(self.
gis)
479 factors = property(_get_factors)
483 assert expected == actual (mod modulus) 486 for d, c
in actual - expected:
487 assert 0 == c % modulus,
"degree %d\nactual = %s" % (d, str(actual))
489 for d, c
in actual - expected:
490 assert 0 == c % modulus,
"degree %d\nactual = %s\n%s" % (d, str(actual), message)
495 Hensel lift factors mod p of target upto bound and return factors 499 target == product(factors) mod p 501 there exist k s.t. q == p**k >= bound and 502 target == product(result) mod q 504 if len(factors) == 2:
505 lifter = HenselLiftPair.from_factors(target, factors[0], factors[1], p)
506 elif bound < sys.maxint:
507 lifter = HenselLiftMulti.from_factors(target, factors, p)
509 lifter = HenselLiftSimultaneously.from_factors(target, factors, p)
510 while lifter.q < bound:
512 return lifter.factors, lifter.q