def zzx_hensel_lift(p, f, f_list, l): """Multifactor Hensel lifting. Given a prime p, polynomial f over Z[x] such that lc(f) is a unit modulo p, monic pair-wise coprime polynomials f_i over Z[x] satisfying: f = lc(f) f_1 ... f_r (mod p) and a positive integer l, returns a list of monic polynomials F_1, F_2, ..., F_r satisfying: f = lc(f) F_1 ... F_r (mod p**l) F_i = f_i (mod p), i = 1..r For more details on the implemented algorithm refer to: [1] J. von zur Gathen, J. Gerhard, Modern Computer Algebra, First Edition, Cambridge University Press, 1999, pp. 424 """ r = len(f_list) lc = zzx_LC(f) if r == 1: F = zzx_mul_term(f, igcdex(lc, p**l)[0], 0) return [zzx_trunc(F, p**l)] m = p k = int(r // 2) d = int(ceil(log(l, 2))) g = gf_from_int_poly([lc], p) for f_i in f_list[:k]: g = gf_mul(g, gf_from_int_poly(f_i, p), p) h = gf_from_int_poly(f_list[k], p) for f_i in f_list[k + 1:]: h = gf_mul(h, gf_from_int_poly(f_i, p), p) s, t, _ = gf_gcdex(g, h, p) g = gf_to_int_poly(g, p) h = gf_to_int_poly(h, p) s = gf_to_int_poly(s, p) t = gf_to_int_poly(t, p) for _ in range(1, d + 1): (g, h, s, t), m = zzx_hensel_step(m, f, g, h, s, t), m**2 return zzx_hensel_lift(p, g, f_list[:k], l) \ + zzx_hensel_lift(p, h, f_list[k:], l)
def zzx_hensel_lift(p, f, f_list, l): """Multifactor Hensel lifting. Given a prime p, polynomial f over Z[x] such that lc(f) is a unit modulo p, monic pair-wise coprime polynomials f_i over Z[x] satisfying: f = lc(f) f_1 ... f_r (mod p) and a positive integer l, returns a list of monic polynomials F_1, F_2, ..., F_r satisfying: f = lc(f) F_1 ... F_r (mod p**l) F_i = f_i (mod p), i = 1..r For more details on the implemented algorithm refer to: [1] J. von zur Gathen, J. Gerhard, Modern Computer Algebra, First Edition, Cambridge University Press, 1999, pp. 424 """ r = len(f_list) lc = zzx_LC(f) if r == 1: F = zzx_mul_term(f, igcdex(lc, p**l)[0], 0) return [ zzx_trunc(F, p**l) ] m = p k = int(r // 2) d = int(ceil(log(l, 2))) g = gf_from_int_poly([lc], p) for f_i in f_list[:k]: g = gf_mul(g, gf_from_int_poly(f_i, p), p) h = gf_from_int_poly(f_list[k], p) for f_i in f_list[k+1:]: h = gf_mul(h, gf_from_int_poly(f_i, p), p) s, t, _ = gf_gcdex(g, h, p) g = gf_to_int_poly(g, p) h = gf_to_int_poly(h, p) s = gf_to_int_poly(s, p) t = gf_to_int_poly(t, p) for _ in range(1, d+1): (g, h, s, t), m = zzx_hensel_step(m, f, g, h, s, t), m**2 return zzx_hensel_lift(p, g, f_list[:k], l) \ + zzx_hensel_lift(p, h, f_list[k:], l)
def test_gf_from_to_int_poly(): assert gf_from_int_poly([1,0,7,2,20], 5) == [1,0,2,2,0] assert gf_to_int_poly([1,0,4,2,3], 5) == [1,0,-1,2,-2]
def zzx_zassenhaus(f): """Factor square-free polynomials over Z[x]. """ n = zzx_degree(f) if n == 1: return [f] A = zzx_max_norm(f) b = zzx_LC(f) B = abs(int(sqrt(n + 1) * 2**n * A * b)) C = (n + 1)**(2 * n) * A**(2 * n - 1) gamma = int(ceil(2 * log(C, 2))) prime_max = int(2 * gamma * log(gamma)) for p in xrange(3, prime_max + 1): if not isprime(p) or b % p == 0: continue F = gf_from_int_poly(f, p) if gf_sqf_p(F, p): break l = int(ceil(log(2 * B + 1, p))) modular = [] for ff in gf_factor_sqf(F, p)[1]: modular.append(gf_to_int_poly(ff, p)) g = zzx_hensel_lift(p, f, modular, l) T = set(range(len(g))) factors, s = [], 1 while 2 * s <= len(T): for S in subsets(T, s): G, H = [b], [b] S = set(S) for i in S: G = zzx_mul(G, g[i]) for i in T - S: H = zzx_mul(H, g[i]) G = zzx_trunc(G, p**l) H = zzx_trunc(H, p**l) G_norm = zzx_l1_norm(G) H_norm = zzx_l1_norm(H) if G_norm * H_norm <= B: T = T - S G = zzx_primitive(G)[1] f = zzx_primitive(H)[1] factors.append(G) b = zzx_LC(f) break else: s += 1 return factors + [f]
def zzx_zassenhaus(f): """Factor square-free polynomials over Z[x]. """ n = zzx_degree(f) if n == 1: return [f] A = zzx_max_norm(f) b = zzx_LC(f) B = abs(int(sqrt(n+1)*2**n*A*b)) C = (n+1)**(2*n)*A**(2*n-1) gamma = int(ceil(2*log(C, 2))) prime_max = int(2*gamma*log(gamma)) for p in xrange(3, prime_max+1): if not isprime(p) or b % p == 0: continue F = gf_from_int_poly(f, p) if gf_sqf_p(F, p): break l = int(ceil(log(2*B + 1, p))) modular = [] for ff in gf_factor_sqf(F, p)[1]: modular.append(gf_to_int_poly(ff, p)) g = zzx_hensel_lift(p, f, modular, l) T = set(range(len(g))) factors, s = [], 1 while 2*s <= len(T): for S in subsets(T, s): G, H = [b], [b] S = set(S) for i in S: G = zzx_mul(G, g[i]) for i in T-S: H = zzx_mul(H, g[i]) G = zzx_trunc(G, p**l) H = zzx_trunc(H, p**l) G_norm = zzx_l1_norm(G) H_norm = zzx_l1_norm(H) if G_norm*H_norm <= B: T = T - S G = zzx_primitive(G)[1] f = zzx_primitive(H)[1] factors.append(G) b = zzx_LC(f) break else: s += 1 return factors + [f]