def multi_hensel_lift(p, f, f_list, l): """Multifactor Hensel lifting. Input: an integer p, an univariate integer polynomial f such that f's leading coefficient lc(f) is a unit mod p. Monic polynomials f_i that are pair-wise coprime mod p satisfying f = lc(f)*f_1*...*f_r mod p and an integer l. Output: monic polynomials ff_1, ..., ff_r satisfying f = lc(f)*ff_1*...*ff_r mod p**l ff_i = f_i mod p """ r = len(f_list) lc = f[f.degree] if r == 1: lc_g, lc_s, lc_t = modint.xgcd(lc, p**l) return [f.scale(lc_s).mod_int(p**l, symmetric=True)] k = int(r/2) d = int(math.ceil(math.log(l, 2))) # Divide and conquer the factors. IntModpPoly = gfpoly.GFPolyFactory(p) g = IntModpPoly.from_int_dict({0:lc}) for f_i in f_list[0:k]: g *= IntModpPoly.from_int_dict(f_i.coeffs) h = IntModpPoly.from_int_dict(f_list[k].coeffs) for f_i in f_list[k+1:]: h *= IntModpPoly.from_int_dict(f_i.coeffs) x, s, t = gfpoly.xgcd(g, h) g = IntPoly(g.to_sym_int_dict()) h = IntPoly(h.to_sym_int_dict()) s = IntPoly(s.to_sym_int_dict()) t = IntPoly(t.to_sym_int_dict()) # Lift the two coprime parts. m = p for j in range(1, d+1): g, h, s, t = hensel_step(m, f, g, h, s, t) m *= m # Call recursively. return multi_hensel_lift(p, g, f_list[0:k], l) \ + multi_hensel_lift(p, h, f_list[k:], l)
def test_GFPoly(): from sympy.polynomials.fast import gfpoly p = 5 IntMod5Poly = gfpoly.GFPolyFactory(p) IntMod5 = IntMod5Poly.coeff_type f = IntMod5Poly.from_int_dict({2: 1, 0: 1}) g = IntMod5Poly.from_int_dict({3: 2, 1: 1, 0: 2}) assert f[0] == IntMod5(1) assert f[1] == IntMod5(0) assert +f == f assert -f == IntMod5Poly.from_int_dict({2: -1, 0: -1}) assert f.scale(IntMod5(2), 3) == IntMod5Poly.from_int_dict({5: 2, 3: 2}) assert f + g == IntMod5Poly.from_int_dict({3: 2, 2: 1, 1: 1, 0: 3}) assert f - g == IntMod5Poly.from_int_dict({3: -2, 2: 1, 1: -1, 0: -1}) assert f * g == IntMod5Poly.from_int_dict({5: 2, 3: 3, 2: 2, 1: 1, 0: 2}) assert f**5 == IntMod5Poly.from_int_dict({10: 1, 0: 1}) assert (f * g).diff() == IntMod5Poly.from_int_dict({2: 4, 1: 4, 0: 1}) assert g.monic() \ == (IntMod5(2), IntMod5Poly.from_int_dict({3:1, 1:-2, 0:1})) assert g.monic()[1].to_sym_int_dict() == {3: 1, 1: -2, 0: 1} q, r = gfpoly.div(f, g) assert (not q) and (r == f) q, r = gfpoly.div(g, f) assert q == IntMod5Poly.from_int_dict({1: 2}) assert r == IntMod5Poly.from_int_dict({1: 4, 0: 2}) assert gfpoly.gcd(f, g) == IntMod5Poly.from_int_dict({1: 1, 0: 3}) assert gfpoly.gcd(f**3, f**4) == f**3 h, s, t = gfpoly.xgcd(f, g) assert h == IntMod5Poly.from_int_dict({1: 1, 0: 3}) assert s == IntMod5Poly.from_int_dict({1: 2}) assert t == IntMod5Poly.from_int_dict({0: 4}) assert h == (s * f + t * g) assert gfpoly.truncate(f, 5) == f assert gfpoly.truncate(f, 1) == IntMod5Poly.from_int_dict({0: 1}) assert gfpoly.pow_mod(f, 3, IntMod5Poly.from_int_dict({2:1})) \ == gfpoly.truncate(f**3, 2) p = 3 IntMod3Poly = gfpoly.GFPolyFactory(p) f = IntMod3Poly.from_int_dict({1:1}) \ * IntMod3Poly.from_int_dict({1:1, 0:1}) \ * IntMod3Poly.from_int_dict({2:1, 0:1}) \ * IntMod3Poly.from_int_dict({2:1, 1:1, 0:2}) a = gfpoly.distinct_degree_factor(f) assert [g.to_sym_int_dict() for g in a] \ == [{1: 1, 2: 1}, {0: -1, 1: 1, 3: 1, 4: 1}] b = gfpoly.equal_degree_factor(a[0], 1) assert sorted([g.to_sym_int_dict() for g in b]) \ == sorted([{1:1}, {1:1, 0:1}]) c = gfpoly.equal_degree_factor(a[1], 2) assert sorted([g.to_sym_int_dict() for g in c]) \ == sorted([{2:1, 0:1}, {2:1, 1:1, 0:-1}]) r = gfpoly.factor_sqf(f) assert r[0] == IntMod3Poly.coeff_type(1) r = r[1:] assert len(r) == 4 assert IntMod3Poly.from_int_dict({1: 1}) in r assert IntMod3Poly.from_int_dict({1: 1, 0: 1}) in r assert IntMod3Poly.from_int_dict({2: 1, 0: 1}) in r assert IntMod3Poly.from_int_dict({2: 1, 1: 1, 0: 2}) in r f = IntMod3Poly.from_int_dict({1:1}) \ * IntMod3Poly.from_int_dict({1:1, 0:1}) \ * IntMod3Poly.from_int_dict({1:1, 0:1}) \ * IntMod3Poly.from_int_dict({2:1, 0:1}) \ * IntMod3Poly.from_int_dict({2:1, 1:1, 0:2}) \ * IntMod3Poly.from_int_dict({2:1, 1:1, 0:2}) \ * IntMod3Poly.from_int_dict({1:1, 0:1}) \ * IntMod3Poly.from_int_dict({1:1, 0:1}) \ * IntMod3Poly.from_int_dict({2:1, 0:1}) \ * IntMod3Poly.from_int_dict({2:1, 1:1, 0:2}) \ * IntMod3Poly.from_int_dict({2:1, 1:1, 0:2}) \ * IntMod3Poly.from_int_dict({1:1, 0:1}) \ * IntMod3Poly.from_int_dict({1:1, 0:1}) \ * IntMod3Poly.from_int_dict({2:1, 0:1}) \ * IntMod3Poly.from_int_dict({2:1, 1:1, 0:2}) \ * IntMod3Poly.from_int_dict({2:1, 1:1, 0:2}) \ * IntMod3Poly.from_int_dict({2:1, 1:1, 0:2}) r = gfpoly.factor(f) assert r[0] == IntMod3Poly.coeff_type(1) test_dict = {} for item in r[1:]: test_dict[item[1]] = item[0].to_sym_int_dict() assert test_dict == { 1: { 1: 1 }, 3: { 2: 1, 0: 1 }, 6: { 1: 1, 0: 1 }, 7: { 2: 1, 1: 1, 0: -1 } }